Menu
Amazon Cognito
Developer Guide (Version Last Updated: 04/19/2016)

Developer Authenticated Identities

Amazon Cognito supports developer authenticated identities, in addition to web identity federation through Facebook, Google, and Amazon. With developer authenticated identities, you can register and authenticate users via your own existing authentication process, while still using Amazon Cognito to synchronize user data and access AWS resources. Using developer authenticated identities involves interaction between the end user device, your backend for authentication, and Amazon Cognito. For more details, please read our blog.

Understanding the Authentication Flow

For information on the developer authenticated identities authflow and how it differs from the external provider authflow, see Authentication Flow.

Associate Developer Provider

To use developer authenticated identities, you'll need an identity pool associated with your developer provider. To do so, follow these steps:

  1. Log in to the Amazon Cognito Console.
  2. Create a new identity pool and, as part of the process, provide a developer provider name.
  3. Alternatively edit an existing identity pool and add a developer provider.

Note: Once the provider name has been set, it cannot be changed.

For additional instructions on working with the Amazon Cognito Console, see Using the Amazon Cognito Console.

Implement an Identity Provider

Android

To use developer authenticated identities, implement your own identity provider class which extends AWSAbstractCognitoIdentityProvider.

Below is a simple example of an identity provider which is used in our sample app:

public class DeveloperAuthenticationProvider extends AWSAbstractCognitoDeveloperIdentityProvider {

  private static final String developerProvider = "<Developer_provider_name>";

  public DeveloperAuthenticationProvider(String accountId, String identityPoolId, Regions region) {
    super(accountId, identityPoolId, region);
    // Initialize any other objects needed here.
  }

  // Return the developer provider name which you choose while setting up the
  // identity pool in the &COG; Console

  @Override
  public String getProviderName() {
    return developerProvider;
  }

  // Use the refresh method to communicate with your backend to get an
  // identityId and token.

  @Override
  public String refresh() {

    // Override the existing token
    setToken(null);

    // Get the identityId and token by making a call to your backend
    // (Call to your backend)

    // Call the update method with updated identityId and token to make sure
    // these are ready to be used from Credentials Provider.

    update(identityId, token);
    return token;

  }

  // If the app has a valid identityId return it, otherwise get a valid
  // identityId from your backend.

  @Override
  public String getIdentityId() {

    // Load the identityId from the cache
    identityId = cachedIdentityId;

    if (identityId == null) {
       // Call to your backend
    } else {
       return identityId;
    }

  }
}

To use this identity provider, you have to pass it into CognitoCachingCredentialsProvider. Here's an example:

DeveloperAuthenticationProvider developerProvider = new DeveloperAuthenticationProvider( null, "IDENTITYPOOLID", context, Regions.USEAST1);
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider( context, developerProvider, Regions.USEAST1);

iOS - Objective-C

To use developer authenticated identities, implement your own identity provider class which extends AWSAbstractCognitoIdentityProvider. Below is a simple example of an identity provider which is used in our sample app:

@implementation DeveloperAuthenticatedIdentityProvider
private static final String developerProvider = "<Developer_provider_name>";

- (instancetype)initWithRegionType:(AWSRegionType)regionType
                        identityId:(NSString *)identityId
                    identityPoolId:(NSString *)identityPoolId
                            logins:(NSDictionary *)logins
                      providerName:(NSString *)providerName
{
    if (self = [super initWithRegionType:regionType identityId:identityId accountId:nil identityPoolId:identityPoolId logins:logins]) {
        self.providerName = providerName;
        // Initialize any other objects needed here
    }
    return self;
}

/*
 * Use the refresh method to communicate with your backend to get an
 * identityId and token.
 */

- (AWSTask *)refresh {
    /*
     * Get the identityId and token by making a call to your backend
     */
    // Call to your backend

    // Set the identity id and token
    self.identityId = response.identityId;
    self.token = response.token;
    return [AWSTask taskWithResult:self.identityId];
}

/*
 * If the app has a valid identityId return it, otherwise get a valid
 * identityId from your backend.
 */
 - (AWSTask *)getIdentityId {
    // already cached the identity id, return it
    if (self.identityId) {
       return [AWSTask taskWithResult:self.identityId];
    }
    // Add code to call your backend to get identityId
    // Set self.identityId

    return [AWSTask taskWithResult:self.identityId];
    }
}

To use this identity provider, pass it into AWSCognitoCredentialsProvider as shown in the following example:

id<AWSCognitoIdentityProvider> identityProvider =
    [[DeveloperAuthenticatedIdentityProvider alloc]
            initWithRegionType:<Region>
                    identityId:nil
                identityPoolId:<CognitoPoolID>
                        logins:<logins>
                  providerName:<ProviderName>
                    authClient:self.devAuthClient];

id<AWSCognitoCredentialsProvider> credentialsProvider =
    [[AWSCognitoCredentialsProvider alloc]
            initWithRegionType:<Region>
              identityProvider:identityProvider
                 unauthRoleArn:nil
                   authRoleArn:nil];

iOS - Swift

To use developer authenticated identities, implement your own identity provider class which extends AWSAbstractCognitoIdentityProvider. Below is a simple example of an identity provider which is used in our sample app:

class DeveloperAuthenticatedIdentityProvider: AWSAbstractCognitoIdentityProvider {
    let developerProvider = "<Developer_provider_name>"

    init(regionType: AWSRegionType, identityId: String!, accountId: String!, identityPoolId: String!, logins: [NSObject : AnyObject]!, providerName: String!) {
        super.init(regionType: regionType, identityId: identityId, accountId: accountId, identityPoolId: identityPoolId, logins: logins)
        self.providerName = providerName
        // Initialize any other objects needed here
    }

    /*
    * Use the refresh method to communicate with your backend to get an
    * identityId and token.
    */
    override func refresh() -> AWSTask! {
        /*
        * Get the identityId and token by making a call to your backend
        */
        // Call to your backend

        // Set the identity id and token
        self.identityId = response.identityId
        self.token = response.token
        return AWSTask(result: self.identityId)

    }

    /*
    * If the app has a valid identityId return it, otherwise get a valid
    * identityId from your backend.
    */
    override func getIdentityId() -> AWSTask! {
        // already cached the identity id, return it
        if self.identityId != nil {
            return AWSTask(result: self.identityId)
        }
        // Add code to call your backend to get identityId
        // Set self.identityId

        return AWSTask(result: self.identityId)
    }

}

To use this identity provider, pass it into AWSCognitoCredentialsProvider as shown in the following example:

let identityProvider = DeveloperAuthenticatedIdentityProvider(
    regionType: <Region>,
    identityId: nil,
    identityPoolId: <CognitoPoolID>,
    logins: <logins>,
    providerName: <ProviderName>,
    authClient: self.devAuthClient)
let credentialsProvider = AWSCognitoCredentialsProvider(
    regionType: <Region>,
    identityProvider: identityProvider,
    unauthRoleArn: nil,
    authRoleArn: nil)

JavaScript

Once you obtain an identity ID and session token from your backend, you will to pass them into the AWS.CognitoIdentityCredentials provider. Here's an example:

AWS.config.credentials = new AWS.CognitoIdentityCredentials({
   IdentityPoolId: 'IDENTITY_POOL_ID',
   IdentityId: 'IDENTITY_ID_RETURNED_FROM_YOUR_PROVIDER',
   Logins: {
      'cognito-identity.amazonaws.com': 'TOKEN_RETURNED_FROM_YOUR_PROVIDER'
   }
});

Unity

To use developer-authenticated identities you have to extend CognitoAWSCredentials and override the RefreshIdentity method to retrieve the user identity id and token from your backend and return them. Below is a simple example of an identity provider that would contact a hypothetical backend at 'example.com':

using UnityEngine;
using System.Collections;
using Amazon.CognitoIdentity;
using System.Collections.Generic;
using ThirdParty.Json.LitJson;
using System;
using System.Threading;

public class DeveloperAuthenticatedCredentials : CognitoAWSCredentials
{
    const string PROVIDER_NAME = "example.com";
    const string IDENTITY_POOL = "IDENTITY_POOL_ID";
    static readonly RegionEndpoint REGION = RegionEndpoint.USEast1;

    private string login = null;

    public DeveloperAuthenticatedCredentials(string loginAlias)
        : base(IDENTITY_POOL, REGION)
    {
        login = loginAlias;
    }

    protected override IdentityState RefreshIdentity()
    {
        IdentityState state = null;
        ManualResetEvent waitLock = new ManualResetEvent(false);
        MainThreadDispatcher.ExecuteCoroutineOnMainThread(ContactProvider((s) =>
        {
            state = s;
            waitLock.Set();
        }));
        waitLock.WaitOne();
        return state;
    }

    IEnumerator ContactProvider(Action<IdentityState> callback)
    {
        WWW www = new WWW("http://example.com/?username="+login);
        yield return www;
        string response = www.text;

        JsonData json = JsonMapper.ToObject(response);

        //The backend has to send us back an Identity and a OpenID token
        string identityId = json["IdentityId"].ToString();
        string token = json["Token"].ToString();

        IdentityState state = new IdentityState(identityId, PROVIDER_NAME, token, false);
        callback(state);
    }
}

The code above uses a thread dispatcher object to call a coroutine. If you don't have a way to do this in your project, you can use the following script in your scenes:

using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MainThreadDispatcher : MonoBehaviour
{
    static Queue<IEnumerator> _coroutineQueue = new Queue<IEnumerator>();
    static object _lock = new object();

    public void Update()
    {
        while (_coroutineQueue.Count > 0)
        {
            StartCoroutine(_coroutineQueue.Dequeue());
        }
    }

    public static void ExecuteCoroutineOnMainThread(IEnumerator coroutine)
    {
        lock (_lock) {
            _coroutineQueue.Enqueue(coroutine);
        }
    }
}

Xamarin

To use developer-authenticated identities you have to extend CognitoAWSCredentials and override the RefreshIdentity method to retrieve the user identity id and token from your backend and return them. Below is a simple example of an identity provider that would contact a hypothetical backend at 'example.com':

public class DeveloperAuthenticatedCredentials : CognitoAWSCredentials
{
    const string PROVIDER_NAME = "example.com";
    const string IDENTITY_POOL = "IDENTITY_POOL_ID";
    static readonly RegionEndpoint REGION = RegionEndpoint.USEast1;
    private string login = null;

    public DeveloperAuthenticatedCredentials(string loginAlias)
        : base(IDENTITY_POOL, REGION)
    {
        login = loginAlias;
    }

    protected override async Task<IdentityState> RefreshIdentityAsync()
    {
        IdentityState state = null;
        //get your identity and set the state
        return state;
    }
}

Updating the Logins Map (Android and iOS only)

Android

Once the user is authenticated, update the logins map with the developer provider name and a developer user identifier, which is an alphanumeric string that uniquely identifies a user in your authentication system. Be sure to call the refresh method after updating the logins map as the identityId might have changed:

HashMap<String, String> loginsMap = new HashMap<String, String>();
loginsMap.put(developerAuthenticationProvider.getProviderName(), developerUserIdentifier);

credentialsProvider.setLogins(loginsMap);
credentialsProvider.refresh();

iOS - Objective-C

Once the user is authenticated, update the logins map with the developer provider name and a developer user identifier, which is an alphanumeric string that uniquely identifies a user in your authentication system. Be sure to call the refresh method after updating the logins map as the identityId might have changed:

credentialsProvider.logins = @{DeveloperProviderName: userIdentifier}
[credentialsProvider refresh];

iOS - Swift

Once the user is authenticated, update the logins map with the developer provider name and a developer user identifier, which is an alphanumeric string that uniquely identifies a user in your authentication system. Be sure to call the refresh method after updating the logins map as the identityId might have changed:

credentialsProvider.logins = [DeveloperProviderName: userIdentifier]
credentialsProvider.refresh()

Getting a Token (Server Side)

All platforms

Tokens are generated by a Cognito API called GetOpenIdTokenForDeveloperIdentity. You have to call this API from your backend with your AWS developer credentials. The API receives the unique ID of the Cognito identity pool you're connecting to and one or more identifiers for the user. An identifier can be the username of your user, an email address or a numerical value. The API responds to your call with a unique Cognito ID for your user and an OpenID Connect token for the end user.

A few things to keep in mind about the token returned by GetOpenIdTokenForDeveloperIdentity:

  • You can specify a custom expiration time for the token, so that you can cache it. If you don't provide any custom expiration time, the token is valid for 15 minutes, after which you can exchange the token for STS credentials, which are valid for a maximum of one hour.
  • The maximum token duration you can set is 24 hours.
  • You should take care in setting the expiration time for a token, as there are significant security implications: an attacker could use a leaked token to access your AWS resources for the token's duration.

The following example shows how to initialize a Amazon Cognito client and retrieve a token for a custom identity:

import com.amazonaws.services.cognitoidentity.*;
import com.amazonaws.services.cognitoidentity.model.*;

// Initialize an &COG; client with your AWS developer credentials
AmazonCognitoIdentity identityClient = new AmazonCognitoIdentityClient(
  new BasicAWSCredentials("access_key_id", "secret_access_key")
);

// Create a new request to retrieve the token for your end user
GetOpenIdTokenForDeveloperIdentityRequest idRequest =
  new GetOpenIdTokenForDeveloperIdentityRequest();
idRequest.setIdentityPoolId("YOUR_COGNITO_IDENTITY_POOL_ID");
idRequest.setIdentityId("14232");

// You need to specify the token expiration time in seconds.
// We are setting this to 5 minutes. This means that, from
// the moment we return the token to this API call, you have
// 5 minutes to use it to initialize the &COG; client on the device.
// Note the l (long) at the end of the setTokenDuration param.
idRequest.setTokenDuration(60 * 5l);
GetOpenIdTokenForDeveloperIdentityResult idResp =
  identityClient.GetOpenIdTokenForDeveloperIdentity(idRequest);

// We can now read the token and &COG; ID for your user from the
// response, and you can return the token to the client device.
String cognitoId = idResp.getIdentityId();
String oidToken = idResp.getToken();

To get an OpenId connect token you should call GetOpenIdTokenForDeveloperIdentity API from your backend. This API is signed by your AWS credentials, so that Amazon Cognito can trust that the user identifier supplied in the API call is valid. This API returns an identity ID and OpenId connect token as a result:

// Build the request object
GetOpenIdTokenForDeveloperIdentityRequest request = new GetOpenIdTokenForDeveloperIdentityRequest();
request.setIdentityPoolId(IDENTITY_POOL_ID);
request.setTokenDuration(TOKEN_DURATION);
// Use the logins token passed from the device
request.setLogins(LOGINS_MAP);
GetOpenIdTokenForDeveloperIdentityResult result = cognitoClient.getOpenIdTokenForDeveloperIdentity(request);

Following the steps above, you should be able to integrate developer authenticated identities in your app. If you have any issues or questions please feel free to post in our forums.

Connect to an Existing Social Identity (Android, iOS, Unity, and Xamarin)

To connect a custom identity to a user's social identity (Facebook, Google, or Amazon), add the identity provider token to the Logins map in the credentials provider, as shown below:

Android

// Initialize a Logins map for the authentication tokens.
        Map logins = new HashMap();

        // Add the custom identity for this user
        logins.put("custom", identifier);

        // If your user is also logged in with Facebook, Amazon, or Google, we can now add
        // the session token.
        logins.put("graph.facebook.com", Session.getActiveSession().getAccessToken());

        // Add the new map we created to the credentials provider.
        credentialsProvider.setLogins(logins);

iOS - Objective-C

NSString *token = FBSession.activeSession.accessTokenData.accessToken;
credentialsProvider.logins = @{
    @"custom": identifier,
    @"graph.facebook.com": token
};

iOS - Swift

let token = FBSDKAccessToken.currentAccessToken().tokenString
credentialsProvider.logins = [AWSCognitoLoginProviderKey.Facebook.rawValue: token]

Unity

credentials.AddLogin("provider.com", token);

Xamarin

credentials.AddLogin("provider.com", token);

If you add public identity provider tokens to the login map on the identity or credentials provider, it's your responsibility to transmit these tokens to your backend and use them in conjunction with your own custom identifiers when calling Amazon Cognito Identity from your backend. When you link identities in this way, Amazon Cognito automatically associates the tokens in the background, so you can keep working with the same unique Amazon Cognito ID. You can use the credentials from the provider to initialize any other AWS client SDK enabled in your identity pool access policies, and you can synchronize user state and data.

Note

Login provider tokens may expire during the lifetime of your application. When they do, you will need to obtain a new token from the provider and add it to the logins map to ensure that your Amazon Cognito session can refresh properly and retrieve AWS credentials.

Supporting Transition Between Providers

Android

Your application might require supporting unauthenticated identities or authenticated identities using public providers (Login with Amazon, Facebook or Google) along with developer authenticated identities. The essential difference between developer authenticated identities and other identities (unauthenticated identities and authenticated identities using public provider) is the way the identityId and token are obtained. For other identities the mobile application will interact directly with Amazon Cognito instead of contacting your authentication system. So the mobile application should be able to support two distinct flows depending on the choice made by the app user. For this you will have to make some changes to the custom identity provider.

The refresh method should check the logins map, if the map is not empty and has a key with developer provider name, then you should call your backend; otherwise just call the getIdentityId method and return null.

public String refresh() {

   setToken(null);

   // If the logins map is not empty make a call to your backend
   // to get the token and identityId
   if (getProviderName() != null &&
      !this.loginsMap.isEmpty() &&
      this.loginsMap.containsKey(getProviderName())) {

      /**
       * This is where you would call your backend
       **/

      // now set the returned identity id and token in the provider
      update(identityId, token);
      return token;

   } else {
      // Call getIdentityId method and return null
      this.getIdentityId();
      return null;
   }
}

Similarly the getIdentityId method will have two flows depending on the contents of the logins map:

public String getIdentityId() {

   // Load the identityId from the cache
   identityId = cachedIdentityId;

   if (identityId == null) {

      // If the logins map is not empty make a call to your backend
      // to get the token and identityId

      if (getProviderName() != null && !this.loginsMap.isEmpty()
         && this.loginsMap.containsKey(getProviderName())) {

         /**
           * This is where you would call your backend
          **/

         // now set the returned identity id and token in the provider
         update(identityId, token);
         return token;

      } else {
         // Otherwise call &COG; using getIdentityId of super class
         return super.getIdentityId();
      }

   } else {
      return identityId;
   }

}

iOS - Objective-C

Your application might require supporting unauthenticated identities or authenticated identities using public providers (Login with Amazon, Facebook or Google) along with developer authenticated identities. The essential difference between developer authenticated identities and other identities (unauthenticated identities and authenticated identities using public provider) is the way the identityId and token are obtained. For other identities the mobile application will interact directly with Amazon Cognito instead of contacting your authentication system. So the mobile application should be able to support two distinct flows depending on the choice made by the app user. For this you will have to make some changes to the custom identity provider.

The refresh method should check the logins dictionary, if the dictionary is not empty and has a key with developer provider name, then you should call your backend; otherwise just call the getIdentityId method:

- (BOOL)authenticatedWithProvider {
    return [self.logins objectForKey:self.providerName] != nil;
}

- (AWSTask *)refresh {

    // If the logins dictionary does not have a key with
    // developer provider name simply call getIdentityId of the super class
   if (![self authenticatedWithProvider]) {
       return [super getIdentityId];
    }

    // If the logins dictionary has a key with developer provider name
    // make a call to your backend to get the token and identityId
    else {
      /**
       * This is where you would call your backend
       **/

      // now set the returned identity id and token in the provider
      self.identityId = response.identityId;
      self.token = response.token;

      return [AWSTask taskWithResult:self.identityId];
    }
}

Similarly the getIdentityId method will have two flows depending on the contents of the logins map.

- (AWSTask *)getIdentityId {
    // already cached the identity id, return it
   if (self.identityId) {
       return [AWSTask taskWithResult:nil];
   }
  // not authenticated with our developer provider
  else if (![self authenticatedWithProvider]) {
      return [super getIdentityId];
  }

  // authenticated with our developer provider, use refresh
  // logic to get id/token pair
  else {
      return [[AWSTask taskWithResult:nil] continueWithBlock:^id(AWSTask *task) {
        if (!self.identityId) {
           return [self refresh];
        }
        return [AWSTask taskWithResult:self.identityId];
       }];
     }
 }

iOS - Swift

Your application might require supporting unauthenticated identities or authenticated identities using public providers (Login with Amazon, Facebook or Google) along with developer authenticated identities. The essential difference between developer authenticated identities and other identities (unauthenticated identities and authenticated identities using public provider) is the way the identityId and token are obtained. For other identities the mobile application will interact directly with Amazon Cognito instead of contacting your authentication system. So the mobile application should be able to support two distinct flows depending on the choice made by the app user. For this you will have to make some changes to the custom identity provider.

The refresh method should check the logins dictionary, if the dictionary is not empty and has a key with developer provider name, then you should call your backend; otherwise just call the getIdentityId method:

func authenticatedWithProvider() -> Bool {
  if let pName = self.providerName, lgins = self.logins {
      if lgins[pName] != nil {
          return true
      }
  }
  return false
}

override func refresh() -> AWSTask! {

  // If the logins dictionary does not have a key with
  // developer provider name simply call getIdentityId() of the super class

  if !self.authenticatedWithProvider() {
      return super.getIdentityId()
  }

  // If the logins dictionary has a key with developer provider name
  // make a call to your backend to get the token and identityId
  else {
    /**
    * This is where you would call your backend
    **/


    // now set the returned identity id and token in the provider
    self.identityId = response.identityId
    self.token = response.token
    return AWSTask(result: self.identityId)
  }

}

Similarly the getIdentityId method will have two flows depending on the contents of the logins map.

override func getIdentityId() -> AWSTask! {
    //already cached the identity id, return it
    if self.identityId != nil {
      return AWSTask(result:self.identityId)
    }
    //not authenticated with our developer provider
    else if !self.authenticatedWithProvider() {
      return super.getIdentityId()
    }

    //authenticated with our developer provider, use refresh
    // logic to get id/token pair
    else {
      return AWSTask(result:nil).continueWithBlock { (task) -> AnyObject! in
          if self.identityId != nil {
              return self.refresh()
          }
          return AWSTask(result: self.identityId)
      }
    }
}

Unity

Your application might require supporting unauthenticated identities or authenticated identities using public providers (Login with Amazon, Facebook or Google) along with developer authenticated identities. The essential difference between developer authenticated identities and other identities (unauthenticated identities and authenticated identities using public provider) is the way the identityId and token are obtained. For other identities the mobile application will interact directly with Amazon Cognito instead of contacting your authentication system. So the mobile application should be able to support two distinct flows depending on the choice made by the app user. For this you will have to make some changes to the custom identity provider.

The recommended way to do it in Unity is to extend your identity provider from AmazonCognitoEnhancedIdentityProvide instead of AbstractCognitoIdentityProvider, and call the parent RefreshAsync method instead of your own in case the user is not authenticated with your own backend. If the user is authenticated, you can use the same flow explained before.

Xamarin

Your application might require supporting unauthenticated identities or authenticated identities using public providers (Login with Amazon, Facebook or Google) along with developer authenticated identities. The essential difference between developer authenticated identities and other identities (unauthenticated identities and authenticated identities using public provider) is the way the identityId and token are obtained. For other identities the mobile application will interact directly with Amazon Cognito instead of contacting your authentication system. So the mobile application should be able to support two distinct flows depending on the choice made by the app user. For this you will have to make some changes to the custom identity provider.