Front-End Web & Mobile

Announcing SAML Support for Amazon Cognito

Today, we are excited to announce support in Amazon Cognito for Security Assertion Markup Language (SAML) 2.0 authentication. SAML 2.0 is an XML-based open standard that is used to transfer authentication and authorization data between parties.

Now developers can sign in users through their own SAML identity providers and provide secure access through Amazon Cognito to their AWS resources, such as Amazon S3 and Amazon DynamoDB.

With SAML support in Amazon Cognito, developers can also configure IAM roles to provide different permissions to different users and groups.

This post covers how you can configure SAML for Amazon Cognito to authenticate your users and assign them to specific AWS IAM roles.

1. Configure your identity pool for a SAML provider

To configure your identity pool to support a SAML provider, choose the SAML tab in the Authentication provider section of the Amazon Cognito console. If you want to use an existing SAML provider, select the provider and save the changes for the identity pool.

To create a new SAML provider:

  • Choose the AWS IAM console link to go to the IAM console.
  • In the left pane, choose Identity Providers.
  • Choose Create Provider and then choose SAML in Provider Type.

2. Create roles

Now you create roles that you want to associate with your end user.  Unlike other types of IdPs, when you use a SAML-based IdP with Cognito, you do not have to select authenticated role for your Cognito identity pool. However, you can choose Create new role next to the Authenticated role label in the Cognito console and allow it to create the correct trust relationship as a convenient way to start creating new IAM roles. You can further configure the role policy and trust policy by using the IAM Console. Refer to the Cognito developer guide to learn more about the role policy by using context keys available with Cognito. You can create as many roles as required by your application and configure your IdP to populate the role in SAML assertion response. The role received in the SAML assertion from your IdP will be used for vending the AWS credentials for your users.

The trust policy for your role used by Cognito will look similar to the following policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Federated": "cognito-identity.amazonaws.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "cognito-identity.amazonaws.com:aud": "us-east-1:12345678-identity-pool-id-123abc"
        },
        "ForAnyValue:StringLike": {
          "cognito-identity.amazonaws.com:amr": "authenticated"
        }
      }
    }
  ]
}

3. Configure your SAML IdP

After you create the SAML provider with AWS and setup your IAM roles, you can configure your SAML IdP to add relying party trust between your IdP and AWS.  Many IdPs allow you to specify a URL from which the IdP can read an XML document that contains relying party information and certificates. For AWS, you can use https://signin.aws.amazon.com/static/saml-metadata.xml

The next step is to configure the SAML assertion response from your IdP to populate the claims needed by AWS. For details on the claim configuration, see Configuring SAML Assertions for the Authentication Response. For instructions on how to configure many popular IdPs, see Integrating Third-Party SAML Solution Providers with AWS.

4. Customize Roles

Using SAML with Amazon Cognito Identity allows the IAM role to be customized for the end user.  You configure your organization’s IdP in a way that it maps users or groups in your organization to the IAM roles you want those users to assume. The exact steps for performing the mapping depend on what IdP you’re using. Essentially you will be configuring the claim attribute https://aws.amazon.com/SAML/Attributes/Role to get populated in the SAML assertion response. This attribute specifies one or more pairs of comma delimited roles and provider ARNs. These are the roles for which the user is allowed to get credentials.

If multiple roles are received in the SAML assertion response, the optional customRoleArn parameter should be populated while calling getCredentialsForIdentity.  The role received in the customRoleArn parameter will be used if it matches a role in the SAML assertion claim. Your app will need to provide the logic or prompt the user to choose a role if multiple roles are included in the SAML assertion.

The value of the https://aws.amazon.com/SAML/Attributes/RoleSessionName claim attribute is used for setting the role session name in the credentials vended by Amazon Cognito. Role session name is set to a generic value ‘CognitoIdentityCredentials’ by Amazon Cognito if your identity pool supports multiple SAML providers and multiple assertions are received in the logins map which have different values of RoleSessionName.

5. Authenticate users with SAML IdP and get SAML assertion

After you have set up your Amazon Cognito identity pool and SAML IdP, you are ready to authenticate the user against the SAML IdP and federate with Amazon Cognito. The SAML IdP issues a SAML assertion for the authenticated user. The base-64 encoded assertion response must be passed to Amazon Cognito as a value in the logins map. The key in this map will be the ARN of the SAML provider that you associated with your identity pool in step #1.

To federate with SAML based IdP, you will need to determine the URL which is being used to initiate the login. AWS federation utilizes IdP-initiated login. In ADFS 2.0 URL takes the form of https://<fqdn>/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices

The following describes how to extract the SAML assertion response from ADFS.

  • Populate the IdP URL, username, and password in the following code. Connect to the IdP with NTLM authentication. This should return an HTML input form in the response, which will have a  SAML assertion as a value. The following example shows the Java code for this step.  For different SAML IdPs and platforms, this step might be different. Refer to the documentation from your SAML identity provider to learn how to get the SAML assertion response.
        String idpurl= "https://<fqdn>/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices";

        Authenticator.setDefault(new Authenticator() {
            @Override
            public PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(username, password.toCharArray());
            }
        });

        URL urlRequest = new URL(idpurl);
        HttpURLConnection conn = (HttpURLConnection) urlRequest.openConnection();
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setRequestMethod("GET");

        InputStream stream = conn.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));

        StringBuilder response = new StringBuilder();
        String str;
        while ((str = reader.readLine()) != null) {
            response.append(str);
        }
        String html = response.toString();
  • After you receive the HTML response from the IdP, use the following example code to parse the response and extract the Base64-encoded SAML response from it.
        DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document document = builder.parse(new InputSource(new StringReader(html)));

        Element docElement = document.getDocumentElement();
        NodeList inputNodes = docElement.getElementsByTagName("input");
        if (inputNodes != null && inputNodes.getLength() > 0) {

            for (int i = 0; i <= inputNodes.getLength() - 1; i++)  {
                Node inputNode = inputNodes.item(i);
                NamedNodeMap inputNodeAttributes = inputNode.getAttributes();

                Map inputAttibuteMap = new HashMap();

                for (int j = 0; j <= inputNodeAttributes.getLength() - 1; j++) {
                    inputAttibuteMap.put(inputNodeAttributes.item(j).getNodeName(),
                            inputNodeAttributes.item(j).getNodeValue());
                }                
                if (inputAttibuteMap.get("name").equals("SAMLResponse")) {
                    // Base 64 encoded SAML Authentication response.
                    return inputAttibuteMap.get("value");
                }
            }
        }
     

6. Use the SAML assertion to call Amazon Cognito

The examples below show how to call Cognito with the SAML assertion on different platforms. Please note, only the Enhanced flow in Amazon Congito is supported with SAML based IdP.

Java:

    AmazonCognitoIdentity client = new AmazonCognitoIdentityClient(new AnonymousAWSCredentials());
    // Get id request. This only needs to be executed the first time and the result should be cached.
    GetIdRequest idRequest = new GetIdRequest();
    idRequest.setAccountId("aws account id");
    idRequest.setIdentityPoolId("identity pool id");

    Map logins = new HashMap();
    logins.put("arn:aws:iam::aws account id:saml-provider/name", "base64 encoded assertion response");
    idRequest.setLogins(logins );
    GetIdResult idResp = client.getId(idRequest);
    String identityId = idResp.getIdentityId();

    // GetCredentialsForIdentity call.
    GetCredentialsForIdentityRequest credRequest = new GetCredentialsForIdentityRequest();
    request.setIdentityId(identityId);
    request.setLogins(logins);

    // If SAML assertion contains multiple roles, resolve the role by setting the custom role
    request.setCustomRoleArn("arn:aws:iam::aws account id:role/admin");

    GetCredentialsForIdentityResult credetialsResult = client.getCredentialsForIdentity(credRequest)

 

Android

If you are using the Android SDK you can populate the logins map with the SAML assertion as follows.

    Map logins = new HashMap();
    logins.put("arn:aws:iam::aws account id:saml-provider/name", "base64 encoded assertion response");
    // Now this should be set to CognitoCachingCredentialsProvider object.
    CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(context, identity pool id, region);
    credentialsProvider.setLogins(logins);
    // If SAML assertion contains multiple roles, resolve the role by setting the custom role
    credentialsProvider.setCustomRoleArn("arn:aws:iam::aws account id:role/customRoleName");
    // This should trigger a call to Cognito service to get the credentials.
    credentialsProvider.getCredentials();

iOS

If you are using the iOS SDK you can provide the SAML assertion in your AWSIdentityProviderManager as follows.

    - (AWSTask<NSDictionary<NSString*,NSString*> *> *) logins {
        //this is hardcoded for simplicity, normally you would asynchronously go to your SAML provider 
        //get the assertion and return the logins map using a AWSTaskCompletionSource
        return [AWSTask taskWithResult:@{@"arn:aws:iam::aws account id:saml-provider/name":@"base64 encoded assertion response"}];
    }

    // If SAML assertion contains multiple roles, resolve the role by setting the custom role.
    // Implementing this is optional if there is only one role.
    - (NSString *)customRoleArn {
        return @"arn:aws:iam::accountId:role/customRoleName";
    }

 

As you can see, integrating a SAML-based IdP with Amazon Cognito provides you flexibility to choose different roles to associate with your organizations’ users and groups to access AWS resources. It is easy to integrate your SAML IdP with Amazon Cognito. This integration enables you to leverage the identity management and data synchronization functionality provided by Amazon Cognito.

We welcome your feedback on this feature.  For more information, see the SAML Identity Provider topic in the Amazon Cognito Developer Guide. You can reach us by posting to the Amazon Cognito forums.