Front-End Web & Mobile
Use Amazon Cognito in your website for simple AWS authentication
Warning (Nov 2017): The content below is outdated. Updated content is forthcoming.
Amazon Cognito helps you create unique identifiers for your end users that are kept consistent across devices and platforms. Cognito also delivers temporary, limited-privilege credentials to your application to access AWS resources.
Over the past few weeks, the team has been working hard to provide access to Amazon Cognito through other AWS SDKs on top of the AWS Mobile SDK for iOS and Android. This is important for us because it allows Amazon Cognito to bridge the gap between the web and mobile worlds. Cognito APIs are now available in the AWS SDK for Java, .NET, PHP and JavaScript, both Node.js and Browser.
By using Amazon Cognito in your web applications as well as mobile apps, you can utilize a consistent, cross-platform identifier for your end users authenticated through Facebook, Google, or Amazon; together with the Cognito Sync service, this allows you to keep user-related data consistent across all your applications and platforms. Further, Cognito helps you to retrieve temporary, limited-privilege credentials for both your authenticated and unauthenticated users without managing any backend infrastructure. In our previous post, we covered how to connect to the Amazon Cognito Identity service from your mobile applications.
Getting started
The first step is to create a new identity pool through the Amazon Cognito console. During the creation of the identity pool, you will be asked to create a new IAM role or pick an existing one for your end users. The role you select has an impact on which AWS services your end users will be able to access with the temporary credentials. A new policy created by the Amazon Cognito console by default allows access to the Amazon Cognito sync service and Mobile Analytics.
The authentication flow
AWS authentication with Amazon Cognito involves three steps:
- Call the
GetId
API providing your AWS account and identity pool details to retrieve a unique identifier for your end user. - Call the
GetOpenIdToken
API to receive a valid OpenId Connect token for the unique identifier you retrieve with theGetId
call. - Using the STS client make an
AssumeRoleWithWebIdentity
call to retrieve a set of temporary, limited-privilege credentials you can use to access your AWS resources.
See the Amazon Cognito Identity API reference to learn more about these and other API endpoints exposed by the Cognito service.
The JavaScript SDK abstracts this complexity by exposing a CognitoIdentityCredentials
object that takes care of making all of the required API calls, caching the unique identifier for the end user received from the GetId
call and renewing the credentials from the AssumeRoleWithWebIdentity
call when they expire.
To utilize the Java, .NET, or PHP SDK, you have to implement the authentication flow yourself by making the three API calls. This post guides you through that process.
Using JavaScript
The JavaScript SDK for the Browser includes a CognitoIdentityCredentials
object, which abstracts the entire authentication flow. This means you don’t have to call all of the APIs individually but simply utilize the CognitoIdentityCredentials
object, pass your identity pool and account parameters, and the credentials object takes care of retrieving AWS credentials and the unique identifier for your end users.
JavaScript
// The parameters required to intialize the Cognito Credentials object. // If you are authenticating your users through one of the supported // identity providers you should set the Logins object with the provider // tokens. For example: // Logins: { // graph.facebook.com : facebookResponse.authResponse.accessToken // } var params = { IdentityPoolId: "YOUR_COGNITO_IDENTITY_POOL_ID" }; // set the Amazon Cognito region AWS.config.region = 'us-east-1'; // initialize the Credentials object with our parameters AWS.config.credentials = new AWS.CognitoIdentityCredentials(params); // We can set the get method of the Credentials object to retrieve // the unique identifier for the end user (identityId) once the provider // has refreshed itself AWS.config.credentials.get(function(err) { if (err) { console.log("Error: "+err); return; } console.log("Cognito Identity Id: " + AWS.config.credentials.identityId); // Other service clients will automatically use the Cognito Credentials provider // configured in the JavaScript SDK. var cognitoSyncClient = new AWS.CognitoSync(); cognitoSyncClient.listDatasets({ IdentityId: AWS.config.credentials.identityId, IdentityPoolId: "YOUR_COGNITO_IDENTITY_POOL_ID" }, function(err, data) { if ( !err ) { console.log(JSON.stringify(data)); } }); });
GetID
The first call you should make is to retrieve a unique identifier for your user. You only need to make this API call the first time a user connects to your app. You should then cache the identity id returned. If a user with a known identity id connects again, you can skip this call. This call can receive an optional parameter containing the tokens returned by the supported identity providers.
Java
// import the required packages from the AWS SDK for Java import com.amazonaws.services.cognitoidentity.*; import com.amazonaws.services.cognitoidentity.model.*; import com.amazonaws.services.securitytoken.*; import com.amazonaws.services.securitytoken.model.*; import com.amazonaws.auth.*; // initialize the Cognito identity client with a set // of anonymous AWS credentials AmazonCognitoIdentity identityClient = new AmazonCognitoIdentityClient(new AnonymousAWSCredentials()); // send a get id request. This only needs to be executed the first time // and the result should be cached. GetIdRequest idRequest = new GetIdRequest(); idRequest.setAccountId("YOUR_AWS_ACCOUNT_ID"); idRequest.setIdentityPoolId("YOUR_COGNITO_IDENTITY_POOL_ID"); // If you are authenticating your users through an identity provider // then you can set the Map of tokens in the request // Map providerTokens = new HashMap(); // providerTokens.put("graph.facebook.com", "facebook session key"); // idRequest.setLogins(providerTokens); GetIdResult idResp = identityClient.getId(idRequest); String identityId = idResp.getIdentityId(); // TODO: At this point you should save this identifier so you won't // have to make this call the next time a user connects
.NET
// import the libraries we need from the AWS SDK for .NET using Amazon; using Amazon.Runtime; using Amazon.CognitoIdentity; using Amazon.CognitoIdentity.Model; using Amazon.SecurityToken; using Amazon.SecurityToken.Model; // initialize a set of anonymous AWS credentials for our API calls AnonymousAWSCredentials cred = new AnonymousAWSCredentials (); // initialize the Cognito identity client and prepare a request object // to get the identity id AmazonCognitoIdentityClient cognitoClient = new AmazonCognitoIdentityClient( cred, // the anonymous credentials RegionEndpoint.USEast1 // the Amazon Cognito region ); GetIdRequest idRequest = new GetIdRequest (); idRequest.AccountId = "YOUR_AWS_ACCOUNT_ID"; idRequest.IdentityPoolId = "YOUR_COGNITO_IDENTITY_POOL_ID"; // set the Dictionary of logins if you are authenticating users // through an identity provider //idRequest.Logins = new Dictionary { // { "graph.facebook.com", "FacebookSessionToken" } //}; // The identity id is in the IdentityId parameter of the response object GetIdResponse idResp = cognitoClient.GetId (idRequest); // TODO: At this point you should save this identifier so you won't // have to make this call the next time a user connects
PHP
// include the AWS client library in your code require "aws.phar"; use AwsCognitoIdentityCognitoIdentityClient; use AwsStsStsClient; // initialize a Cognito identity client using the factory $identityClient = CognitoIdentityClient::factory(array( 'region' => 'us-east-1' )); // call the GetId API with the required parameters $idResp = $identityClient->getId(array( 'AccountId' => 'YOUR_AWS_ACCOUNT_ID', 'IdentityPoolId' => 'YOUR_COGNITO_IDENTITY_POOL_ID', // If you are authenticating your users through an identity // provider then you can set the associative array of tokens // in this call // 'Logins' => array( // 'graph.facebook.com' => 'your facebook session token', //) )); // retrieve the identity id from the response data structure $identityId = $idResp["IdentityId"]; // TODO: At this point you should save this identifier so you won't // have to make this call the next time a user connects
GetOpenIdToken
Once you have retrieved the unique identifier for your end user, or loaded the previous identifier from your cache, you can make the GetOpenIdToken
call to retrieve a valid OpenID Connect token for them. The token returned by this call can be traded to the Security Token Service (STS) in exchange for temporary, limited-privilege AWS credentials.
If your identity id has already been authenticated with any supported provider – such as Facebook, Google, or Amazon – you will need to include at least one token for a login linked to this identity in the Logins
map.
Java
// Create the request object GetOpenIdTokenRequest tokenRequest = new GetOpenIdTokenRequest(); tokenRequest.setIdentityId(identityId); // If you are authenticating your users through an identity provider // then you can set the Map of tokens in the request // Map providerTokens = new HashMap(); // providerTokens.put("graph.facebook.com", "facebook session key"); // tokenRequest.setLogins(providerTokens); GetOpenIdTokenResult tokenResp = identityClient.getOpenIdToken(tokenRequest); // get the OpenID token from the response String openIdToken = tokenResp.getToken();
.NET
// create a new request object GetOpenIdTokenRequest openIdReq = new GetOpenIdTokenRequest (); openIdReq.IdentityId = idResp.IdentityId; // set the Dictionary of logins if you are authenticating users // through an identity provider //openIdReq.Logins = new Dictionary { // { "graph.facebook.com", "FacebookSessionToken" } //}; GetOpenIdTokenResponse openIdResp = cognitoClient.GetOpenIdToken(openIdReq); // the authentication token is available in the Token property // of the response object
PHP
// execute the getOpenIdToken call with the identityId // retrieved from the previous call or cached $tokenResp = $identityClient->getOpenIdToken(array( 'IdentityId' => $identityId, // If you are authenticating your users through an identity // provider then you can set the associative array of tokens // in this call // 'Logins' => array( // 'graph.facebook.com' => 'your facebook session token', //) )); // read the OpenID token from the response $token = $tokenResp["Token"];
AssumeRoleWithWebIdentity
This call to the Security Token Service retrieves a set of temporary, limited-privilege credentials for your application to access AWS Resources. The permissions associated to these credentials are set by the IAM access policy set on the role. You can additionally specify a “scope-down” policy on this API call to further restrict access. If your application is allowing access to unauthenticated users, i.e., users that did not log in with your application through an identity provider, we recommend you restrict access as much as possible.
Java
// you can now create a set of temporary, limited-privilege credentials to access // your AWS resources through the Security Token Service utilizing the OpenID // token returned by the previous API call. The IAM Role ARN passed to this call // will be applied to the temporary credentials returned AWSSecurityTokenService stsClient = new AWSSecurityTokenServiceClient(new AnonymousAWSCredentials()); AssumeRoleWithWebIdentityRequest stsReq = new AssumeRoleWithWebIdentityRequest(); stsReq.setRoleArn("arn:aws:iam::6157xxxxxxxx:role/a_valid_aws_role_arn"); stsReq.setWebIdentityToken(awsAccessToken); stsReq.setRoleSessionName("AppTestSession"); AssumeRoleWithWebIdentityResult stsResp = stsClient.assumeRoleWithWebIdentity(stsReq); Credentials stsCredentials = stsResp.getCredentials(); // Create the session credentials object AWSSessionCredentials sessionCredentials = new BasicSessionCredentials( stsCredentials.getAccessKeyId(), stsCredentials.getSecretAccessKey(), stsCredentials.getSessionToken() ); // save the timeout for these credentials Date sessionCredentialsExpiration = stsCredentials.getExpiration(); // these credentials can then be used to initialize other AWS clients, // for example the Amazon Cognito Sync client AmazonCognitoSync syncClient = new AmazonCognitoSyncClient(sessionCredentials); ListDatasetsRequest syncRequest = new ListDatasetsRequest(); syncRequest.setIdentityId(idResp.getIdentityId()); syncRequest.setIdentityPoolId("YOUR_COGNITO_IDENTITY_POOL_ID"); ListDatasetsResult syncResp = syncClient.listDatasets(syncRequest);
.NET
// create a new security token service client with the // anonymous credentials we initialized in the first step AmazonSecurityTokenServiceClient stsClient = new AmazonSecurityTokenServiceClient( cred, RegionEndpoint.USEast1 ); // create a new AssumeRoleWithWebIdentityRequest object and set // the parameters we need AssumeRoleWithWebIdentityRequest stsReq = new AssumeRoleWithWebIdentityRequest(); stsReq.RoleArn = "arn:aws:iam::6157xxxxxxxx:role/a_valid_aws_role_arn"; stsReq.RoleSessionName = "AppTestSession"; // you need to give the session a name stsReq.WebIdentityToken = openIdResp.Token; // the Token from the previous API call // execute the request and retrieve the credentials AssumeRoleWithWebIdentityResponse stsResp = stsClient.AssumeRoleWithWebIdentity(stsReq); // you can now initialize other AWS clients with these credentials, // for example the Amazon Cognito Sync client AmazonCognitoSyncClient cognitoSyncClient = new AmazonCognitoSyncClient (stsResp.Credentials); ListDatasetsRequest syncRequest = new ListDatasetsRequest (); syncRequest.IdentityId = idResp.IdentityId; syncRequest.IdentityPoolId = "us-east-1:6df80629-0e7e-432f-a730-23898c0e3901"; ListDatasetsResult syncResp = cognitoSyncClient.ListDatasets(syncRequest);
PHP
// create a new STS client $stsClient = StsClient::factory(array( 'region' => 'us-east-1' )); // run the AssumeRoleWithWebIdentity request with the IAM role // for your user and the OpenID token retrieved from the previous // API call $stsResp = $stsClient->assumeRoleWithWebIdentity(array( 'RoleArn' => 'arn:aws:iam::6157xxxxxxxx:role/a_valid_aws_role_arn', 'RoleSessionName' => 'AppTestSession', // you need to give the session a name 'WebIdentityToken' => $token )); // you can use the credentials from the STS call response to // initialize other AWS clients. For example the Amazon Cognito Sync client $cognitSync = CognitoSyncClient::factory(array( 'key' => $stsResp['Credentials']['AccessKeyId'], 'secret' => $stsResp['Credentials']['SecretAccessKey'], 'token' => $stsResp['Credentials']['SessionToken'], 'region' => 'us-east-1' )); $syncResp = $cognitoSync->listDatasets(array( 'IdentityId' => $identityId, 'IdentityPoolId' => 'YOUR_COGNITO_IDENTITY_POOL_ID' ));
Conclusion
Amazon Cognito makes it simple to securely access your AWS resources from your web applications as well as mobile apps by delivering temporary, limited-privilege credentials. Further, you can keep a consistent end-user identifier in both your web and mobile applications without setting up any infrastructure and managing a backend service. You can also utilize the Amazon Cognito Sync service to save profile data for a user and make it easily accessible from all the platforms supported by your application. The next post will explain how to make the most of the fine-grained access permissions in IAM policies utilizing the Cognito identifier.