All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.auth.CognitoCredentialsProvider Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2011-2015 Amazon Technologies, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *    http://aws.amazon.com/apache2.0
 *
 * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and
 * limitations under the License.
 */

package com.amazonaws.auth;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.AmazonWebServiceRequest;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.SDKGlobalConfiguration;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.cognitoidentity.AmazonCognitoIdentity;
import com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient;
import com.amazonaws.services.cognitoidentity.model.GetCredentialsForIdentityRequest;
import com.amazonaws.services.cognitoidentity.model.GetCredentialsForIdentityResult;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient;
import com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityRequest;
import com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityResult;
import com.amazonaws.services.securitytoken.model.Credentials;
import com.amazonaws.services.cognitoidentity.model.ResourceNotFoundException;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * AWSCredentialsProvider implementation that uses the Amazon Cognito Identity
 * service and AWS Security Token Service to create temporary, short-lived
 * sessions to use for authentication
 */
public class CognitoCredentialsProvider implements AWSCredentialsProvider {

    /** Used in the enhanced get credentials flow */
    private AmazonCognitoIdentity cib;

    /** Handle the identity-specific interactions */
    private final AWSCognitoIdentityProvider identityProvider;

    /** Default duration for started sessions */
    public static final int DEFAULT_DURATION_SECONDS = 3600;

    /** Default threshold for refreshing session credentials */
    public static final int DEFAULT_THRESHOLD_SECONDS = 500;

    /** The current session credentials */
    protected AWSSessionCredentials sessionCredentials;

    /** The expiration time for the current session credentials */
    protected Date sessionCredentialsExpiration;

    /** The current Token */
    protected String token;

    /**
     * The client for starting STS sessions, used in the basic (non-enhanced
     * flow)
     */
    protected AWSSecurityTokenService securityTokenService;

    protected int sessionDuration;
    protected int refreshThreshold;
    protected String unauthRoleArn;
    protected String authRoleArn;

    protected boolean useEnhancedFlow;

    /**
     * Constructs a new {@link CognitoCredentialsProvider}, which will use the
     * specified Amazon Cognito identity pool to make a request, using the basic
     * authentication flow, to the AWS Security Token Service (STS) to request
     * short-lived session credentials, which will then be returned by this
     * class's {@link #getCredentials()} method.
     * 
     * @param accountId The AWS accountId for the account with Amazon Cognito
     * @param identityPoolId The Amazon Cogntio identity pool to use
     * @param unauthRoleArn The ARN of the IAM Role that will be assumed when
     *            unauthenticated
     * @param authRoleArn The ARN of the IAM Role that will be assumed when
     *            authenticated
     * @param region The region to use when contacting Cognito Identity
     */
    public CognitoCredentialsProvider(String accountId, String identityPoolId,
            String unauthRoleArn, String authRoleArn, Regions region) {
        this(accountId, identityPoolId, unauthRoleArn, authRoleArn, region,
                new ClientConfiguration());
    }

    /**
     * Constructs a new {@link CognitoCredentialsProvider}, which will use the
     * specified Amazon Cognito identity pool to make a request, using the basic
     * authentication flow, to the AWS Security Token Service (STS) to request
     * short-lived session credentials, which will then be returned by this
     * class's {@link #getCredentials()} method.
     * 

* This version of the constructor allows you to specify a client * configuration for the Amazon Cognito and STS clients. *

* * @param accountId The AWS accountId for the account with Amazon Cognito * @param identityPoolId The Amazon Cognito identity pool to use * @param unauthRoleArn The ARN of the IAM Role that will be assumed when * unauthenticated * @param authRoleArn The ARN of the IAM Role that will be assumed when * authenticated * @param region The region to use when contacting Cognito Identity * @param clientConfiguration Configuration to apply to service clients * created */ public CognitoCredentialsProvider(String accountId, String identityPoolId, String unauthRoleArn, String authRoleArn, Regions region, ClientConfiguration clientConfiguration) { this.cib = new AmazonCognitoIdentityClient( new AnonymousAWSCredentials(), clientConfiguration); this.cib.setRegion(Region.getRegion(region)); this.sessionDuration = DEFAULT_DURATION_SECONDS; this.refreshThreshold = DEFAULT_THRESHOLD_SECONDS; this.unauthRoleArn = unauthRoleArn; this.authRoleArn = authRoleArn; this.useEnhancedFlow = (unauthRoleArn == null && authRoleArn == null); if (this.useEnhancedFlow) { this.securityTokenService = null; this.identityProvider = new AWSEnhancedCognitoIdentityProvider(accountId, identityPoolId, cib); } else { this.securityTokenService = new AWSSecurityTokenServiceClient( new AnonymousAWSCredentials(), clientConfiguration); this.identityProvider = new AWSBasicCognitoIdentityProvider(accountId, identityPoolId, this.cib); } } /** * Constructs a new {@link CognitoCredentialsProvider}, which will use the * specified Amazon Cognito identity pool to make a request to Cognito, * using the enhanced flow, to get short lived session credentials, which * will then be returned by this class's {@link #getCredentials()} method. * * @param identityPoolId The Amazon Cognito identity pool to use * @param region The region to use when contacting Cognito Identity */ public CognitoCredentialsProvider(String identityPoolId, Regions region) { this(null, identityPoolId, null, null, region, new ClientConfiguration()); } /** * Constructs a new {@link CognitoCredentialsProvider}, which will use the * specified Amazon Cognito identity pool to make a request to Cognito, * using the enhanced flow, to get short lived session credentials, which * will then be returned by this class's {@link #getCredentials()} method. *

* This version of the constructor allows you to specify a client * configuration for the Amazon Cognito client. *

* * @param identityPoolId The Amazon Cognito identity pool to use * @param region The region to use when contacting Cognito Identity * @param clientConfiguration Configuration to apply to service clients * created */ public CognitoCredentialsProvider(String identityPoolId, Regions region, ClientConfiguration clientConfiguration) { this(null, identityPoolId, null, null, region, clientConfiguration); } /** * Constructs a new {@link CognitoCredentialsProvider}, which will use the * specified Amazon Cognito identity pool to make a request to the AWS * Security Token Service (STS) to get short-lived session credentials, * which will then be returned by this class's {@link #getCredentials()} * method. *

* This version of the constructor allows you to specify the Amazon Cognito * and STS client to use. *

*

* Set the roles and stsClient to null to use the enhanced authentication * flow, not contacting STS. Otherwise the basic flow will be used. *

* * @param accountId The AWS accountId for the account with Amazon Cognito * @param identityPoolId The Amazon Cogntio identity pool to use * @param unauthRoleArn The ARN of the IAM Role that will be assumed when * unauthenticated * @param authRoleArn The ARN of the IAM Role that will be assumed when * authenticated * @param cibClient Preconfigured CognitoIdentity client to make requests * with * @param stsClient Preconfigured STS client to make requests with */ public CognitoCredentialsProvider(String accountId, String identityPoolId, String unauthRoleArn, String authRoleArn, AmazonCognitoIdentityClient cibClient, AWSSecurityTokenService stsClient) { // No need to specify parameters for Region and ClientConfig because we // don't create the clients this.cib = cibClient; this.securityTokenService = stsClient; this.unauthRoleArn = unauthRoleArn; this.authRoleArn = authRoleArn; this.sessionDuration = DEFAULT_DURATION_SECONDS; this.refreshThreshold = DEFAULT_THRESHOLD_SECONDS; this.useEnhancedFlow = (unauthRoleArn == null && authRoleArn == null); if (this.useEnhancedFlow) { this.identityProvider = new AWSEnhancedCognitoIdentityProvider(accountId, identityPoolId, cibClient); } else { this.identityProvider = new AWSBasicCognitoIdentityProvider(accountId, identityPoolId, cibClient); } } /** * Constructs a new CognitoCredentialsProvider, which will set up a link to * the provider passed in using the basic authentication flow to get get * short-lived credentials from STS, which can be retrieved from * {@link #getCredentials()} *

* This version of the constructor allows you to specify your own Identity * Provider class. *

* * @param provider a reference to the provider in question, including what's * needed to interact with it to later connect with STS * @param unauthArn the unauthArn, for use with the STS call * @param authArn the authArn, for use with the STS call */ public CognitoCredentialsProvider(AWSCognitoIdentityProvider provider, String unauthArn, String authArn) { this(provider, unauthArn, authArn, new AWSSecurityTokenServiceClient( new AnonymousAWSCredentials(), new ClientConfiguration())); } /** * Constructs a new CognitoCredentialsProvider, which will set up a link to * the provider passed in to use the basic authentication flow to get * short-lived credentials from STS, which can be retrieved from * {@link #getCredentials()} *

* This version of the constructor allows you to specify your own Identity * Provider class, and the STS client to use. *

* * @param provider a reference to the provider in question, including what's * needed to interact with it to later connect with STS * @param unauthArn the unauthArn, for use with the STS call * @param authArn the authArn, for use with the STS call * @param stsClient the sts endpoint to get session credentials from */ public CognitoCredentialsProvider(AWSCognitoIdentityProvider provider, String unauthArn, String authArn, AWSSecurityTokenService stsClient) { this.identityProvider = provider; this.unauthRoleArn = unauthArn; this.authRoleArn = authArn; this.securityTokenService = stsClient; this.sessionDuration = DEFAULT_DURATION_SECONDS; this.refreshThreshold = DEFAULT_THRESHOLD_SECONDS; this.useEnhancedFlow = false; } /** * Constructs a new CognitoCredentialsProvider, which will set up a link to * the provider passed in using the enhanced authentication flow to get * short-lived credentials from Amazon Cognito, which can be retrieved from * {@link #getCredentials()} *

* This version of the constructor allows you to specify your own Identity * Provider class. *

* * @param provider a reference to the provider in question, including what's * needed to interact with it to later connect with Amazon * Cognito * @param region The region to use when contacting Cognito */ public CognitoCredentialsProvider(AWSCognitoIdentityProvider provider, Regions region) { this(provider, region, new ClientConfiguration()); } /** * Constructs a new CognitoCredentialsProvider, which will set up a link to * the provider passed in using the enhanced authentication flow to get * short-lived credentials from Amazon Cognito, which can be retrieved from * {@link #getCredentials()} *

* This version of the constructor allows you to specify your own Identity * Provider class and the configuration for the Amazon Cognito client. *

* * @param provider a reference to the provider in question, including what's * needed to interact with it to later connect with Amazon * Cognito * @param clientConfiguration Configuration to apply to service clients * created * @param region The region to use when contacting Cognito Identity */ public CognitoCredentialsProvider(AWSCognitoIdentityProvider provider, Regions region, ClientConfiguration clientConfiguration) { this.cib = new AmazonCognitoIdentityClient( new AnonymousAWSCredentials(), new ClientConfiguration()); this.cib.setRegion(Region.getRegion(region)); this.identityProvider = provider; this.unauthRoleArn = null; this.authRoleArn = null; this.securityTokenService = null; this.sessionDuration = DEFAULT_DURATION_SECONDS; this.refreshThreshold = DEFAULT_THRESHOLD_SECONDS; this.useEnhancedFlow = true; } public String getIdentityId() { return identityProvider.getIdentityId(); } public String getToken() { return identityProvider.getToken(); } public AWSIdentityProvider getIdentityProvider() { return identityProvider; } public void setSessionCredentialsExpiration(Date expiration) { sessionCredentialsExpiration = expiration; } public Date getSessionCredentitalsExpiration() { return sessionCredentialsExpiration; } public String getIdentityPoolId() { return identityProvider.getIdentityPoolId(); } /** * If the current session has expired/credentials are invalid, a new session * is started, establishing the credentials. In either case, those * credentials are returned */ @Override public AWSSessionCredentials getCredentials() { if (needsNewSession()) { startSession(); } return sessionCredentials; } /** * Set the duration of the session credentials created by this client in * seconds. Values must be supported by AssumeRoleWithWebIdentityRequest. * * @see com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityRequest * @param sessionDuration The new duration for session credentials created * by this provider */ public void setSessionDuration(int sessionDuration) { this.sessionDuration = sessionDuration; } /** * Set the duration of the session credentials created by this client in * seconds. Values must be supported by AssumeRoleWithWebIdentityRequest. * Returns reference to object so methods can be chained together. * * @see com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityRequest * @param sessionDuration The new duration for session credentials created * by this provider * @return A reference to this updated object so that method calls can be * chained together. */ public CognitoCredentialsProvider withSessionDuration(int sessionDuration) { this.setSessionDuration(sessionDuration); return this; } /** * Get the duration of the session credentials created by this client in * seconds. Values must be supported by AssumeRoleWithWebIdentityRequest. * * @see com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityRequest * @return The duration for session credentials created by this provider */ public int getSessionDuration() { return this.sessionDuration; } /** * Set the refresh threshold for the session credentials created by this * client in seconds. This value will be used internally to determine if new * credentials should be fetched from STS. * * @see com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityRequest * @param refreshThreshold The new refresh threshold for session credentials * created by this provider */ public void setRefreshThreshold(int refreshThreshold) { this.refreshThreshold = refreshThreshold; } /** * Set the refresh threshold for the session credentials created by this * client in seconds. This value will be used internally to determine if new * credentials should be fetched from STS. Returns a reference to the object * so methods can be chained. * * @see com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityRequest * @param refreshThreshold The new refresh threshold for session credentials * created by this provider * @return A reference to this updated object so that method calls can be * chained together. */ public CognitoCredentialsProvider withRefreshThreshold(int refreshThreshold) { this.setRefreshThreshold(refreshThreshold); return this; } /** * Get the refresh threshold for the session credentials created by this * client in seconds. This value will be used internally to determine if new * credentials should be fetched from STS. * * @see com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityRequest * @return The refresh threshold for session credentials created by this * provider */ public int getRefreshThreshold() { return this.refreshThreshold; } protected void setIdentityId(String identityId) { identityProvider.identityChanged(identityId); } /** * Set the logins map used to authenticated with Amazon Cognito. Note: You * should manually call refresh on on the credentials provider after adding * logins to the provider as your Identity Id may have changed. * * @param logins The new logins map (providerName, providerToken) to use to * communicate with Amazon Cognito */ public void setLogins(Map logins) { identityProvider.setLogins(logins); this.sessionCredentials = null; } /** * Set the logins map used to authenticated with Amazon Cognito. Returns a * reference to the object so methods can be chained. Note: You should * manually call refresh on on the credentials provider after adding logins * to the provider as your Identity Id may have changed. * * @see com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityRequest * @param logins The new logins map (providerName, providerToken) to use to * communicate with Amazon Cognito * @return A reference to this updated object so that method calls can be * chained together. */ public AWSCredentialsProvider withLogins(Map logins) { this.setLogins(logins); return this; } /** * Get the logins map used to authenticated with Amazon Cognito * * @return The logins map (providerName, providerToken) to use to * communicate with Amazon Cognito */ public Map getLogins() { return identityProvider.getLogins(); } @Override public void refresh() { startSession(); } /** * Clear all in-memory and saved state for the credentials provider. Will * destroy any saved Amazon Cognito Identity Id and associated AWS * credentials. */ public void clear() { sessionCredentials = null; sessionCredentialsExpiration = null; setIdentityId(null); identityProvider.setLogins(new HashMap()); } /** * Clear credentials. This will destroy all the saved AWS credentials but * not the identity Id. */ public void clearCredentials() { sessionCredentials = null; sessionCredentialsExpiration = null; } /** * Starts a new session by getting short lived session credentials. */ protected void startSession() { // make sure we have an identityId. In the case of cognito identity, // the try catch will handle a deleted or corrupted id. // Developer authenticated won't throw amazon exceptions, // and for 2hop, it will be handled below, as the getId call // won't fail since it is set. try { token = identityProvider.refresh(); } catch (ResourceNotFoundException rnfe) { // If the identity id or identity pool is non-existant, this is thrown token = retryRefresh(); } catch (AmazonServiceException ase) { // If it's a corrupt id, then a validation exception is thrown if (ase.getErrorCode().equals("ValidationException")) { token = retryRefresh(); } else { throw ase; } } if (useEnhancedFlow) { populateCredentialsWithCognito(token); } else { populateCredentialsWithSts(token); } } /** * To be used to call the provider back end to get identifiers. Specifically, * this is the helper that handles the case for when a refresh call ran into the corrupt * identity id case, either a deleted id or a malformed id. If that happens, this is * called, a new id and token are fetched, and the process is resumed. * * @return the new token gotten by the service */ private String retryRefresh() { // Ensure we get a new id and token setIdentityId(null); token = identityProvider.refresh(); return token; } /** * To be used to help the calling of the 2hop flow in event of the identity id * being either missing or deleted. Once that is caught as having happened, this * call is made, which will clear the old id, get a new one/token, and get the * flow going back to where it was with a new request * * @return the result of the new request */ private GetCredentialsForIdentityResult retryGetCredentialsForIdentity(){ token = retryRefresh(); Map logins; if (token != null && !token.isEmpty()) { logins = new HashMap(); logins.put("cognito-identity.amazonaws.com", token); } else { logins = getLogins(); } GetCredentialsForIdentityRequest request = new GetCredentialsForIdentityRequest() .withIdentityId(getIdentityId()) .withLogins(logins); return cib.getCredentialsForIdentity(request); } /** * Gets the session credentials from Amazon Cognito. */ private void populateCredentialsWithCognito(String token) { // For Cognito-authenticated identities token will always be null, but // for developer-authenticated identities, refresh() may return a token // that the the developer backend has received from Cognito and we have // to send back in our request. Map logins; if (token != null && !token.isEmpty()) { logins = new HashMap(); logins.put("cognito-identity.amazonaws.com", token); } else { logins = getLogins(); } GetCredentialsForIdentityRequest request = new GetCredentialsForIdentityRequest() .withIdentityId(getIdentityId()) .withLogins(logins); GetCredentialsForIdentityResult result = null; try { result = cib.getCredentialsForIdentity(request); } catch (ResourceNotFoundException rnfe) { // If the identity id or identity pool is non-existant, this is thrown result = retryGetCredentialsForIdentity(); } catch (AmazonServiceException ase) { // If it's a corrupt id, then a validation exception is thrown if (ase.getErrorCode().equals("ValidationException")) { result = retryGetCredentialsForIdentity(); } else { throw ase; } } com.amazonaws.services.cognitoidentity.model.Credentials credentials = result .getCredentials(); sessionCredentials = new BasicSessionCredentials(credentials.getAccessKeyId(), credentials.getSecretKey(), credentials.getSessionToken()); sessionCredentialsExpiration = credentials.getExpiration(); if (!result.getIdentityId().equals(getIdentityId())) { setIdentityId(result.getIdentityId()); } } /** * Gets the session credentials by requesting an OpenId Connect token from * Amazon Cognito and then trading it with AWS Secure Token Service for the * short lived session credentials. */ private void populateCredentialsWithSts(String token) { boolean isAuthenticated = identityProvider.isAuthenticated(); String roleArn = (isAuthenticated) ? authRoleArn : unauthRoleArn; AssumeRoleWithWebIdentityRequest sessionTokenRequest = new AssumeRoleWithWebIdentityRequest() .withWebIdentityToken(token) .withRoleArn(roleArn) .withRoleSessionName("ProviderSession") .withDurationSeconds(sessionDuration); appendUserAgent(sessionTokenRequest, getUserAgent()); AssumeRoleWithWebIdentityResult sessionTokenResult = securityTokenService .assumeRoleWithWebIdentity(sessionTokenRequest); Credentials stsCredentials = sessionTokenResult.getCredentials(); sessionCredentials = new BasicSessionCredentials( stsCredentials.getAccessKeyId(), stsCredentials.getSecretAccessKey(), stsCredentials.getSessionToken()); sessionCredentialsExpiration = stsCredentials.getExpiration(); } /** * Returns true if a new STS session needs to be started. A new STS session * is needed when no session has been started yet, or if the last session is * within the configured refresh threshold. * * @return True if a new STS session needs to be started. */ protected boolean needsNewSession() { if (sessionCredentials == null) { return true; } long currentTime = System.currentTimeMillis() - SDKGlobalConfiguration.getGlobalTimeOffset() * 1000; long timeRemaining = sessionCredentialsExpiration.getTime() - currentTime; return timeRemaining < (refreshThreshold * 1000); } /** * Append user agent string to the request. The final string is what is set * in the ClientCofniguration concatenated with the given userAgent string. * * @param request the request object to be appended * @param userAgent additional user agent string to append */ private void appendUserAgent(AmazonWebServiceRequest request, String userAgent) { request.getRequestClientOptions().appendUserAgent(userAgent); } /** * Gets the user agent string to append to all requests made by this * provider. Default is an empty string. */ protected String getUserAgent() { return ""; } public void registerIdentityChangedListener(IdentityChangedListener listener) { identityProvider.registerIdentityChangedListener(listener); } public void unregisterIdentityChangedListener( IdentityChangedListener listener) { identityProvider.unregisterIdentityChangedListener(listener); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy