Skip to content

Instantly share code, notes, and snippets.

@juljeanpierre
Last active May 19, 2020 05:37
Show Gist options
  • Save juljeanpierre/c64d2e6858dc37e9ca1b80c244918356 to your computer and use it in GitHub Desktop.
Save juljeanpierre/c64d2e6858dc37e9ca1b80c244918356 to your computer and use it in GitHub Desktop.
Enforce MFA for getSigninToken on AWS

Enforce MFA for getSigninToken on AWS

You can write and run code to create a URL that lets users who sign in to your organization's network securely access the AWS Management Console. The URL includes a sign-in token that you get from AWS and that authenticates the user to AWS.

Example Code [Python]

The following example shows how to use Python to programmatically construct a URL that gives federated users direct access to the AWS Management Console.

import urllib, json, sys
import requests # 'pip install requests'
import boto3 # AWS SDK for Python (Boto3) 'pip install boto3'

# Step 1: Authenticate user in your own identity system.

# Step 2: Using the access keys for an IAM user in your AWS account,
# call "AssumeRole" to get temporary access keys for the federated user

# Note: Calls to AWS STS AssumeRole must be signed using the access key ID 
# and secret access key of an IAM user or using existing temporary credentials.
# The credentials can be in EC2 instance metadata, in environment variables, 
# or in a configuration file, and will be discovered automatically by the 
# client('sts') function. For more information, see the Python SDK docs:
# http://boto3.readthedocs.io/en/latest/reference/services/sts.html
# http://boto3.readthedocs.io/en/latest/reference/services/sts.html#STS.Client.assume_role
sts_connection = boto3.client('sts')

assumed_role_object = sts_connection.assume_role(
    RoleArn="arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:role/ROLE-NAME",
    RoleSessionName="AssumeRoleSession",
)

# Step 3: Format resulting temporary credentials into JSON
url_credentials = {}
url_credentials['sessionId'] = assumed_role_object.get('Credentials').get('AccessKeyId')
url_credentials['sessionKey'] = assumed_role_object.get('Credentials').get('SecretAccessKey')
url_credentials['sessionToken'] = assumed_role_object.get('Credentials').get('SessionToken')
json_string_with_temp_credentials = json.dumps(url_credentials)

# Step 4. Make request to AWS federation endpoint to get sign-in token. Construct the parameter string with
# the sign-in action request, a 12-hour session duration, and the JSON document with temporary credentials 
# as parameters.
request_parameters = "?Action=getSigninToken"
request_parameters += "&SessionDuration=43200"
if sys.version_info[0] < 3:
    def quote_plus_function(s):
        return urllib.quote_plus(s)
else:
    def quote_plus_function(s):
        return urllib.parse.quote_plus(s)
request_parameters += "&Session=" + quote_plus_function(json_string_with_temp_credentials)
request_url = "https://signin.aws.amazon.com/federation" + request_parameters
r = requests.get(request_url)
# Returns a JSON document with a single element named SigninToken.
signin_token = json.loads(r.text)

# Step 5: Create URL where users can use the sign-in token to sign in to 
# the console. This URL must be used within 15 minutes after the
# sign-in token was issued.
request_parameters = "?Action=login" 
request_parameters += "&Issuer=Example.org" 
request_parameters += "&Destination=" + quote_plus_function("https://console.aws.amazon.com/")
request_parameters += "&SigninToken=" + signin_token["SigninToken"]
request_url = "https://signin.aws.amazon.com/federation" + request_parameters

# Send final URL to stdout
print (request_url)

To add MFA, you need to provide an argument to the script that is an MFA Device Serial ARN it will prompt you for an MFA token code and use the MFA provided credentials in the AssumeRole API call.

token = input("Please enter your token code for MFA device: ")

assumed_role_object = sts_connection.assume_role(
    RoleArn="arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME",
    RoleSessionName="AssumeRoleSession",
    SerialNumber="arn:aws:iam::ACCOUNT_ID:mfa/MFA_DEVICE",
    TokenCode=token,
)

Finally, you can adjust the trust policy of the role to require MFA in order for it to be assumed using the below "Condition".
Please see reference link "Editing the Trust Relationship for an Existing Role" for detailed steps.

"Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
AWS | FEDERATION | MFA | getSingInToken | PYTHON | BOTO 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment