diff --git a/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/CognitoUser.java b/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/CognitoUser.java index 61e78f93fa..b0ee706730 100644 --- a/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/CognitoUser.java +++ b/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/CognitoUser.java @@ -418,6 +418,20 @@ private void confirmSignUpInternal(final String confirmationCode, * @param callback REQUIRED: {@link VerificationHandler} callback handler. */ public void resendConfirmationCodeInBackground(final VerificationHandler callback) { + resendConfirmationCodeInBackground(Collections.emptyMap(), callback); + } + + /** + * Request to resend registration confirmation code for a user, in + * background. + * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. + * @param callback REQUIRED: {@link VerificationHandler} callback handler. + */ + public void resendConfirmationCodeInBackground( + final Map clientMetadata, + final VerificationHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); } @@ -427,7 +441,8 @@ public void run() { final Handler handler = new Handler(context.getMainLooper()); Runnable returnCallback; try { - final ResendConfirmationCodeResult resendConfirmationCodeResult = resendConfirmationCodeInternal(); + final ResendConfirmationCodeResult resendConfirmationCodeResult = + resendConfirmationCodeInternal(clientMetadata); returnCallback = new Runnable() { @Override public void run() { @@ -460,11 +475,31 @@ public void run() { * @param callback REQUIRED: {@link VerificationHandler} callback handler. */ public void resendConfirmationCode(final VerificationHandler callback) { + resendConfirmationCode(Collections.emptyMap(), callback); + } + + /** + * Request to resend registration confirmation code for a user, in current + * thread. + *

+ * Note: This method will perform network operations. Calling this + * method in applications' main thread will cause Android to throw + * NetworkOnMainThreadException. + *

+ * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. + * @param callback REQUIRED: {@link VerificationHandler} callback handler. + */ + public void resendConfirmationCode( + final Map clientMetadata, + final VerificationHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); } try { - final ResendConfirmationCodeResult resendConfirmationCodeResult = resendConfirmationCodeInternal(); + final ResendConfirmationCodeResult resendConfirmationCodeResult = + resendConfirmationCodeInternal(clientMetadata); callback.onSuccess(new CognitoUserCodeDeliveryDetails( resendConfirmationCodeResult.getCodeDeliveryDetails())); } catch (final Exception e) { @@ -474,13 +509,16 @@ public void resendConfirmationCode(final VerificationHandler callback) { /** * Internal method to request registration code resend. + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. */ - private ResendConfirmationCodeResult resendConfirmationCodeInternal() { + private ResendConfirmationCodeResult resendConfirmationCodeInternal(final Map clientMetadata) { final ResendConfirmationCodeRequest resendConfirmationCodeRequest = new ResendConfirmationCodeRequest() .withUsername(userId) .withClientId(clientId) - .withSecretHash(secretHash); + .withSecretHash(secretHash) + .withClientMetadata(clientMetadata); final String pinpointEndpointId = pool.getPinpointEndpointId(); resendConfirmationCodeRequest.setUserContextData(getUserContextData()); if (pinpointEndpointId != null) { @@ -868,9 +906,12 @@ public void run() { returnCallback = new Runnable() { @Override public void run() { - final AuthenticationContinuation authenticationContinuation = new AuthenticationContinuation( - cognitoUser, context, - AuthenticationContinuation.RUN_IN_BACKGROUND, callback); + final AuthenticationContinuation authenticationContinuation = + new AuthenticationContinuation( + cognitoUser, + context, + AuthenticationContinuation.RUN_IN_BACKGROUND, callback + ); callback.getAuthenticationDetails(authenticationContinuation, cognitoUser.getUserId()); } @@ -894,7 +935,7 @@ public void run() { *

* This method is synchronous and performs network operations * on the same thread in which the method is called. Calling this - * method in the MainThread will result in {@link android.os.NetworkOnMainThreadException} + * method in the MainThread will result in NetworkOnMainThreadException *

* *

@@ -924,7 +965,52 @@ public void run() { * * @param callback REQUIRED: {@link AuthenticationHandler} callback */ - public void getSession(final AuthenticationHandler callback) { + public void getSession( + final AuthenticationHandler callback) { + getSession(Collections.emptyMap(), callback); + } + + /** + * getSession orchestrates the SignIn flow with Amazon Cognito UserPools. + * + *

+ * This method is synchronous and performs network operations + * on the same thread in which the method is called. Calling this + * method in the MainThread will result in NetworkOnMainThreadException + *

+ * + *

+ * 1) Read the tokens (Id, Access and Refresh) that are cached on the device. + * 1.1) If the Id and Access tokens are present and they are valid, the + * {@link AuthenticationHandler#onSuccess(CognitoUserSession, CognitoDevice)}. + * will be called with a {@link CognitoUserSession} that has references to the valid tokens. + * This means that the user is signed-in. + * 1.2) If the Id and Access tokens are expired, and if there is a valid refresh token, + * a network call is made to get new Id and Access tokens. + * If valid Id and Access tokens are retrieved, they are cached on the device + * and {@link AuthenticationHandler#onSuccess(CognitoUserSession, CognitoDevice)} + * will be called with a {@link CognitoUserSession} that has references to the valid + * tokens. This means that the user is signed-in. + * + * 2) If there are no valid tokens cached on the device, the callback method + * {@link AuthenticationHandler#getAuthenticationDetails(AuthenticationContinuation, String)} + * will be called where the {@link AuthenticationDetails} will need to be supplied + * to continue the SignIn operation. See + * {@link com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.CognitoIdentityProviderContinuation} + * for details on continuation objects. + * + * 3) In all other error scenarios, {@link AuthenticationHandler#onFailure(Exception)} will + * be called with the type and message of the exception and it is the responsibility of + * the caller to handle the exceptions appropriately. + *

+ * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. + * @param callback REQUIRED: {@link AuthenticationHandler} callback + */ + public void getSession( + final Map clientMetadata, + final AuthenticationHandler callback) { if (callback == null) { throw new InvalidParameterException("callback is null"); } @@ -935,8 +1021,14 @@ public void getSession(final AuthenticationHandler callback) { } catch (final InvalidParameterException e) { callback.onFailure(e); } catch (final CognitoNotAuthorizedException e) { - final AuthenticationContinuation authenticationContinuation = new AuthenticationContinuation( - this, context, AuthenticationContinuation.RUN_IN_CURRENT, callback); + final AuthenticationContinuation authenticationContinuation = + new AuthenticationContinuation( + this, + context, + AuthenticationContinuation.RUN_IN_CURRENT, + callback + ); + authenticationContinuation.setClientMetaData(clientMetadata); callback.getAuthenticationDetails(authenticationContinuation, getUserId()); } catch (final Exception e) { callback.onFailure(e); @@ -944,7 +1036,7 @@ public void getSession(final AuthenticationHandler callback) { } /** - * Note: Please use {@link #getSession(AuthenticationHandler)} or + * Note: Please use {@link #getSession(Map, AuthenticationHandler)} or * {@link #getSessionInBackground(AuthenticationHandler)} instead. * * Initiates user authentication through the generic auth flow (also called @@ -959,9 +1051,40 @@ public void getSession(final AuthenticationHandler callback) { * @param callback REQUIRED: {@link AuthenticationHandler} callback. * @return {@link Runnable} for the next step in user authentication. */ - public Runnable initiateUserAuthentication(final AuthenticationDetails authenticationDetails, - final AuthenticationHandler callback, - final boolean runInBackground) { + public Runnable initiateUserAuthentication( + final AuthenticationDetails authenticationDetails, + final AuthenticationHandler callback, + final boolean runInBackground) { + return initiateUserAuthentication( + Collections.emptyMap(), + authenticationDetails, + callback, + runInBackground); + } + + /** + * Note: Please use {@link #getSession(Map, AuthenticationHandler)} or + * {@link #getSessionInBackground(AuthenticationHandler)} instead. + * + * Initiates user authentication through the generic auth flow (also called + * as Enhanced or Custom authentication). This is the first step in user + * authentication. The response to this step from the service will contain + * information about the next step in the authentication process. + * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. + * @param authenticationDetails REQUIRED: Contains details about the user + * authentication. + * @param runInBackground flag indicating if the operation has to run in + * background + * @param callback REQUIRED: {@link AuthenticationHandler} callback. + * @return {@link Runnable} for the next step in user authentication. + */ + public Runnable initiateUserAuthentication( + final Map clientMetadata, + final AuthenticationDetails authenticationDetails, + final AuthenticationHandler callback, + final boolean runInBackground) { final AuthenticationHandler internalCallback = new AuthenticationHandler() { @Override public void onSuccess(final CognitoUserSession userSession, final CognitoDevice newDevice) { @@ -1033,7 +1156,8 @@ public void run() { } } }; - final Runnable task = _initiateUserAuthentication(authenticationDetails, internalCallback, runInBackground); + final Runnable task = _initiateUserAuthentication( + clientMetadata, authenticationDetails, internalCallback, runInBackground); if (runInBackground) { return new Runnable() { @Override @@ -1051,17 +1175,20 @@ public void run() { } } - Runnable _initiateUserAuthentication(final AuthenticationDetails authenticationDetails, - final AuthenticationHandler callback, final boolean runInBackground) { + Runnable _initiateUserAuthentication( + final Map clientMetadata, + final AuthenticationDetails authenticationDetails, + final AuthenticationHandler callback, + final boolean runInBackground) { if (CognitoServiceConstants.CHLG_TYPE_USER_PASSWORD_VERIFIER.equals( authenticationDetails.getAuthenticationType())) { - return startWithUserSrpAuth(authenticationDetails, callback, runInBackground); + return startWithUserSrpAuth(clientMetadata, authenticationDetails, callback, runInBackground); } else if (CognitoServiceConstants.CHLG_TYPE_CUSTOM_CHALLENGE.equals( authenticationDetails.getAuthenticationType())) { - return startWithCustomAuth(authenticationDetails, callback, runInBackground); + return startWithCustomAuth(clientMetadata, authenticationDetails, callback, runInBackground); } else if (CognitoServiceConstants.CHLG_TYPE_USER_PASSWORD.equals( authenticationDetails.getAuthenticationType())) { - return startWithUserPasswordAuth(authenticationDetails, callback, runInBackground); + return startWithUserPasswordAuth(clientMetadata, authenticationDetails, callback, runInBackground); } else { return new Runnable() { @Override @@ -1087,8 +1214,39 @@ public void run() { * @param callback REQUIRED: {@link AuthenticationHandler} callback. * @return {@link Runnable} for the next step in user authentication. */ - public Runnable respondToMfaChallenge(final String mfaCode, - final RespondToAuthChallengeResult challenge, final AuthenticationHandler callback, + public Runnable respondToMfaChallenge( + final String mfaCode, + final RespondToAuthChallengeResult challenge, + final AuthenticationHandler callback, + final boolean runInBackground) { + return respondToMfaChallenge( + Collections.emptyMap(), + mfaCode, + challenge, + callback, + runInBackground); + } + + /** + * Responds to an MFA challenge. This method creates a response to the + * challenge and calls the internal method to respond to the authentication + * challenge. + * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. + * @param mfaCode REQUIRED: The MFA code received by the user. + * @param challenge REQUIRED: Current challenge + * {@link RespondToAuthChallengeResult}. + * @param runInBackground flag indicating if the operation has to run in + * background. + * @param callback REQUIRED: {@link AuthenticationHandler} callback. + * @return {@link Runnable} for the next step in user authentication. + */ + public Runnable respondToMfaChallenge( + final Map clientMetadata, + final String mfaCode, + final RespondToAuthChallengeResult challenge, + final AuthenticationHandler callback, final boolean runInBackground) { final RespondToAuthChallengeRequest challengeResponse = new RespondToAuthChallengeRequest(); final Map mfaParameters = new HashMap(); @@ -1105,7 +1263,8 @@ public Runnable respondToMfaChallenge(final String mfaCode, challengeResponse.setChallengeName(challenge.getChallengeName()); challengeResponse.setChallengeResponses(mfaParameters); challengeResponse.setUserContextData(getUserContextData()); - return respondToChallenge(challengeResponse, callback, runInBackground); + challengeResponse.setClientMetadata(clientMetadata); + return respondToChallenge(clientMetadata, challengeResponse, callback, runInBackground); } /** @@ -1155,8 +1314,8 @@ protected CognitoUserSession getCachedSession() { /** * Request to change password for this user, in background. *

- * This operation requires a valid accessToken - * {@link CognitoUserSession#accessToken}. Un-authenticated users will have + * This operation requires a valid accessToken. + * Un-authenticated users will have * to be authenticated before calling this method. *

* @@ -1203,8 +1362,8 @@ public void run() { /** * Request to change password for this user, in current thread. *

- * This operation requires a valid accessToken - * {@link CognitoUserSession#accessToken}. Unauthenticated users will need + * This operation requires a valid accessToken. + * Unauthenticated users will need * to be authenticated before calling this method. Note: This method * will perform network operations. Calling this method in applications' * main thread will cause Android to throw NetworkOnMainThreadException. @@ -1351,7 +1510,34 @@ private CognitoUserDetails getUserDetailsInternal(CognitoUserSession session) { * verification. * @param callback REQUIRED: callback. */ - public void getAttributeVerificationCodeInBackground(final String attributeName, + public void getAttributeVerificationCodeInBackground( + final String attributeName, + final VerificationHandler callback) { + getAttributeVerificationCodeInBackground(Collections.emptyMap(), attributeName, callback); + } + + /** + * Requests code to verify a user attribute, in background. + *

+ * The user attributes that can be verified are those attributes that can be + * used to communicate with the user, e.g. phone_number and email. The + * verification code is sent to the medium that is represented by the + * attribute. Attribute verification is required to enable the attribute to + * be used an attribute as alias for the user. Aliases attributes can be + * used in lieu of the userId to authenticate the user. If an attribute was + * used in the confirm the user after sign-up, then that alias is already + * verified and does not require re-verification. + *

+ * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * lambda functions triggered by forgot password. + * @param attributeName REQUIRED: Name of the attribute that requires + * verification. + * @param callback REQUIRED: callback. + */ + public void getAttributeVerificationCodeInBackground( + final Map clientMetadata, + final String attributeName, final VerificationHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); @@ -1366,7 +1552,7 @@ public void run() { try { final CognitoUserSession session = user.getCachedSession(); final GetUserAttributeVerificationCodeResult getUserAttributeVerificationCodeResult = getAttributeVerificationCodeInternal( - attributeName, session); + clientMetadata, attributeName, session); returnCallback = new Runnable() { @Override public void run() { @@ -1407,14 +1593,44 @@ public void run() { * verification. * @param callback REQUIRED: callback. */ - public void getAttributeVerificationCode(String attributeName, VerificationHandler callback) { + public void getAttributeVerificationCode( + String attributeName, + VerificationHandler callback) { + getAttributeVerificationCode(Collections.emptyMap(), attributeName, callback); + } + + /** + * Requests code to verify a user attribute, in current thread. + *

+ * The user attributes that can be verified are those attributes that can be + * used to communicate with the user, e.g. phone_number and email. The + * verification code is sent to the medium that is represented by the + * attribute. Attribute verification is required to enable the attribute to + * be used an attribute as alias for the user. Aliases attributes can be + * used in lieu of the userId to authenticate the user. If an attribute was + * used in the confirm the user after sign-up, then that alias is already + * verified and does not require re-verification. Note: This method + * will perform network operations. Calling this method in applications' + * main thread will cause Android to throw NetworkOnMainThreadException. + *

+ * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * lambda functions triggered by forgot password. + * @param attributeName REQUIRED: Name of the attribute that requires + * verification. + * @param callback REQUIRED: callback. + */ + public void getAttributeVerificationCode( + final Map clientMetadata, + String attributeName, + VerificationHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); } try { final GetUserAttributeVerificationCodeResult getUserAttributeVerificationCodeResult = getAttributeVerificationCodeInternal( - attributeName, this.getCachedSession()); + clientMetadata, attributeName, this.getCachedSession()); callback.onSuccess(new CognitoUserCodeDeliveryDetails( getUserAttributeVerificationCodeResult.getCodeDeliveryDetails())); } catch (final Exception e) { @@ -1425,11 +1641,14 @@ public void getAttributeVerificationCode(String attributeName, VerificationHandl /** * Internal method to request for attribute verification code. * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * lambda functions triggered by forgot password. * @param attributeName REQUIRED: Name of the attribute that requires * verification. * @param session REQUIRED: A valid {@link CognitoUserSession}. */ private GetUserAttributeVerificationCodeResult getAttributeVerificationCodeInternal( + final Map clientMetadata, final String attributeName, final CognitoUserSession session) { if (session != null && session.isValid()) { @@ -1437,6 +1656,7 @@ private GetUserAttributeVerificationCodeResult getAttributeVerificationCodeInter getUserAttributeVerificationCodeRequest .setAccessToken(session.getAccessToken().getJWTToken()); getUserAttributeVerificationCodeRequest.setAttributeName(attributeName); + getUserAttributeVerificationCodeRequest.setClientMetadata(clientMetadata); return cognitoIdentityProviderClient .getUserAttributeVerificationCode(getUserAttributeVerificationCodeRequest); @@ -1582,14 +1802,32 @@ public void run() { returnCallback = new Runnable() { @Override public void run() { - callback.onVerify(new VerifyMfaContinuation(context, clientId, user, callback, parameters, true, nextSessionToken, VerifyMfaContinuation.RUN_IN_BACKGROUND)); + callback.onVerify(new VerifyMfaContinuation( + context, + clientId, + user, + callback, + parameters, + true, + nextSessionToken, + VerifyMfaContinuation.RUN_IN_BACKGROUND) + ); } }; } else { returnCallback = new Runnable() { @Override public void run() { - callback.onVerify(new VerifyMfaContinuation(context, clientId, user, callback, parameters, false, nextSessionToken, VerifyMfaContinuation.RUN_IN_BACKGROUND)); + callback.onVerify(new VerifyMfaContinuation( + context, + clientId, + user, + callback, + parameters, + false, + nextSessionToken, + VerifyMfaContinuation.RUN_IN_BACKGROUND) + ); } }; } @@ -1631,7 +1869,16 @@ public void associateSoftwareToken(final String sessionToken, final RegisterMfaH final Map parameters = new HashMap(); parameters.put("type", CognitoServiceConstants.CHLG_TYPE_SOFTWARE_TOKEN_MFA); parameters.put("secretKey", result.getSecretCode()); - callback.onVerify(new VerifyMfaContinuation(context, clientId, user, callback, parameters, useSessionToken, nextSessionToken, VerifyMfaContinuation.RUN_IN_CURRENT)); + callback.onVerify(new VerifyMfaContinuation( + context, + clientId, + user, + callback, + parameters, + useSessionToken, + nextSessionToken, + VerifyMfaContinuation.RUN_IN_CURRENT) + ); } catch (Exception e) { callback.onFailure(e); } @@ -1837,7 +2084,27 @@ private VerifySoftwareTokenResult verifyTotpAssociationInternal(VerifySoftwareTo * updated for this user. * @param callback REQUIRED: {@link UpdateAttributesHandler} callback. */ - public void updateAttributesInBackground(final CognitoUserAttributes attributes, + public void updateAttributesInBackground( + final CognitoUserAttributes attributes, + final UpdateAttributesHandler callback) { + updateAttributesInBackground(Collections.emptyMap(), attributes, callback); + } + + /** + * Updates attributes for a user. Runs in background. + *

+ * Requires valid accessToken. + *

+ * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. + * @param attributes REQUIRED: All attributes and values that need to be + * updated for this user. + * @param callback REQUIRED: {@link UpdateAttributesHandler} callback. + */ + public void updateAttributesInBackground( + final Map clientMetadata, + final CognitoUserAttributes attributes, final UpdateAttributesHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); @@ -1852,7 +2119,7 @@ public void run() { try { final CognitoUserSession session = user.getCachedSession(); final UpdateUserAttributesResult updateUserAttributesResult = updateAttributesInternal( - attributes, session); + clientMetadata, attributes, session); final List attributesVerificationList = new ArrayList(); if (updateUserAttributesResult.getCodeDeliveryDetailsList() != null) { @@ -1894,7 +2161,28 @@ public void run() { * updated for this user. * @param callback REQUIRED: {@link UpdateAttributesHandler} callback. */ - public void updateAttributes(final CognitoUserAttributes attributes, + public void updateAttributes( + final CognitoUserAttributes attributes, + final UpdateAttributesHandler callback) { + updateAttributes(attributes, Collections.emptyMap(), callback); + } + + /** + * Updates attributes for a user. Runs in background. + *

+ * Requires valid accessToken. Note: This method will perform network + * operations. Calling this method in applications' main thread will cause + * Android to throw NetworkOnMainThreadException. + *

+ * @param attributes REQUIRED: All attributes and values that need to be + * updated for this user. + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. + * @param callback REQUIRED: {@link UpdateAttributesHandler} callback. + */ + public void updateAttributes( + final CognitoUserAttributes attributes, + final Map clientMetadata, final UpdateAttributesHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); @@ -1903,7 +2191,7 @@ public void updateAttributes(final CognitoUserAttributes attributes, try { final CognitoUserSession session = getCachedSession(); final UpdateUserAttributesResult updateUserAttributesResult = updateAttributesInternal( - attributes, session); + clientMetadata, attributes, session); final List attributesVerificationList = new ArrayList(); if (updateUserAttributesResult.getCodeDeliveryDetailsList() != null) { @@ -1921,16 +2209,20 @@ public void updateAttributes(final CognitoUserAttributes attributes, /** * Helper method to update user attributes. * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. * @param attributes REQUIRED: Attributes. * @param session REQUIRED: A valid {@link CognitoUserSession}. */ private UpdateUserAttributesResult updateAttributesInternal( + final Map clientMetadata, final CognitoUserAttributes attributes, final CognitoUserSession session) { if (session != null && session.isValid()) { final UpdateUserAttributesRequest updateUserAttributesRequest = new UpdateUserAttributesRequest(); updateUserAttributesRequest.setAccessToken(session.getAccessToken().getJWTToken()); updateUserAttributesRequest.setUserAttributes(attributes.getAttributesList()); + updateUserAttributesRequest.setClientMetadata(clientMetadata); return cognitoIdentityProviderClient.updateUserAttributes(updateUserAttributesRequest); } else { @@ -2534,8 +2826,7 @@ private CognitoUserSession getCognitoUserSession(AuthenticationResultType authRe */ private CognitoUserSession refreshSession(CognitoUserSession currSession) { CognitoUserSession cognitoUserSession = null; - final InitiateAuthRequest initiateAuthRequest = initiateRefreshTokenAuthRequest( - currSession); + final InitiateAuthRequest initiateAuthRequest = initiateRefreshTokenAuthRequest(currSession); final InitiateAuthResult refreshSessionResult = cognitoIdentityProviderClient .initiateAuth(initiateAuthRequest); if (refreshSessionResult.getAuthenticationResult() == null) { @@ -2559,8 +2850,33 @@ private CognitoUserSession refreshSession(CognitoUserSession currSession) { * threading. * @return {@link Runnable} for the next step in user authentication. */ - public Runnable respondToChallenge(final RespondToAuthChallengeRequest challengeResponse, - final AuthenticationHandler callback, final boolean runInBackground) { + public Runnable respondToChallenge( + final RespondToAuthChallengeRequest challengeResponse, + final AuthenticationHandler callback, + final boolean runInBackground) { + return respondToChallenge(Collections.emptyMap(), challengeResponse, callback, runInBackground); + } + + /** + * This method sends the challenge response to the Cognito IDP service. The + * call to the Cognito IDP service returns a new challenge and a different + * method is called to process the challenge. Restarts authentication if the + * service cannot find a device-key. + * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. + * @param challengeResponse REQUIRED: {@link RespondToAuthChallengeRequest} + * contains response for the current challenge. + * @param callback REQUIRED: {@link AuthenticationHandler} callback. + * @param runInBackground REQUIRED: Boolean to indicate the current + * threading. + * @return {@link Runnable} for the next step in user authentication. + */ + public Runnable respondToChallenge( + final Map clientMetadata, + final RespondToAuthChallengeRequest challengeResponse, + final AuthenticationHandler callback, + final boolean runInBackground) { try { if (challengeResponse != null && challengeResponse.getChallengeResponses() != null) { challengeResponse.getChallengeResponses() @@ -2568,7 +2884,7 @@ public Runnable respondToChallenge(final RespondToAuthChallengeRequest challenge } final RespondToAuthChallengeResult challenge = cognitoIdentityProviderClient .respondToAuthChallenge(challengeResponse); - return handleChallenge(challenge, null, callback, runInBackground); + return handleChallenge(clientMetadata, challenge, null, callback, runInBackground); } catch (final ResourceNotFoundException rna) { final CognitoUser cognitoUser = this; if (rna.getMessage().contains("Device")) { @@ -2579,6 +2895,7 @@ public Runnable respondToChallenge(final RespondToAuthChallengeRequest challenge public void run() { final AuthenticationContinuation authenticationContinuation = new AuthenticationContinuation( cognitoUser, context, runInBackground, callback); + authenticationContinuation.setClientMetaData(clientMetadata); callback.getAuthenticationDetails(authenticationContinuation, cognitoUser.getUserId()); } @@ -2606,6 +2923,8 @@ public void run() { * verification. Restarts authentication if the service cannot find a * device-key. * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. * @param authenticationDetails REQUIRED: {@link AuthenticationDetails} * contains user details for authentication. * @param callback REQUIRED: {@link AuthenticationHandler} callback. @@ -2613,15 +2932,18 @@ public void run() { * threading. * @return {@link Runnable} for the next step in user authentication. */ - private Runnable startWithUserSrpAuth(final AuthenticationDetails authenticationDetails, - final AuthenticationHandler callback, final boolean runInBackground) { + private Runnable startWithUserSrpAuth( + final Map clientMetadata, + final AuthenticationDetails authenticationDetails, + final AuthenticationHandler callback, + final boolean runInBackground) { return new Runnable() { @Override public void run() { final AuthenticationHelper authenticationHelper = new AuthenticationHelper( pool.getUserPoolId()); final InitiateAuthRequest initiateAuthRequest = initiateUserSrpAuthRequest( - authenticationDetails, authenticationHelper); + clientMetadata, authenticationDetails, authenticationHelper); try { final InitiateAuthResult initiateAuthResult = cognitoIdentityProviderClient .initiateAuth(initiateAuthRequest); @@ -2633,15 +2955,16 @@ public void run() { "authentication details to response to PASSWORD_VERIFIER challenge"); } final RespondToAuthChallengeRequest challengeRequest = userSrpAuthRequest( + clientMetadata, initiateAuthResult.getChallengeParameters(), authenticationDetails.getPassword(), initiateAuthResult.getChallengeName(), initiateAuthResult.getSession(), authenticationHelper ); - respondToChallenge(challengeRequest, callback, runInBackground).run(); + respondToChallenge(clientMetadata, challengeRequest, callback, runInBackground).run(); } else { - handleChallenge(initiateAuthResult, authenticationDetails, callback, runInBackground).run(); + handleChallenge(clientMetadata, initiateAuthResult, authenticationDetails, callback, runInBackground).run(); } } catch (final ResourceNotFoundException rna) { final CognitoUser cognitoUser = CognitoUser.this; @@ -2650,6 +2973,7 @@ public void run() { context); final AuthenticationContinuation authenticationContinuation = new AuthenticationContinuation( cognitoUser, context, runInBackground, callback); + authenticationContinuation.setClientMetaData(clientMetadata); callback.getAuthenticationDetails(authenticationContinuation, cognitoUser.getUserId()); } else { @@ -2666,6 +2990,8 @@ public void run() { * This method starts the user authentication with a custom (developer * defined) flow. * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. * @param authenticationDetails REQUIRED: {@link AuthenticationDetails} * contains details about the custom authentication flow. * @param callback REQUIRED: {@link AuthenticationHandler} callback. @@ -2673,14 +2999,18 @@ public void run() { * threading. * @return {@link Runnable} for the next step in user authentication. */ - private Runnable startWithCustomAuth(final AuthenticationDetails authenticationDetails, - final AuthenticationHandler callback, final boolean runInBackground) { + private Runnable startWithCustomAuth( + final Map clientMetadata, + final AuthenticationDetails authenticationDetails, + final AuthenticationHandler callback, + final boolean runInBackground) { return new Runnable() { @Override public void run() { try { final AuthenticationHelper authenticationHelper = new AuthenticationHelper(CognitoUser.this.getUserPoolId()); final InitiateAuthRequest initiateAuthRequest = initiateCustomAuthRequest( + clientMetadata, authenticationDetails, authenticationHelper); final InitiateAuthResult initiateAuthResult = cognitoIdentityProviderClient @@ -2693,15 +3023,16 @@ public void run() { "authentication details to response to PASSWORD_VERIFIER challenge"); } final RespondToAuthChallengeRequest challengeRequest = userSrpAuthRequest( + clientMetadata, initiateAuthResult.getChallengeParameters(), authenticationDetails.getPassword(), initiateAuthResult.getChallengeName(), initiateAuthResult.getSession(), authenticationHelper ); - respondToChallenge(challengeRequest, callback, runInBackground).run(); + respondToChallenge(clientMetadata, challengeRequest, callback, runInBackground).run(); } else { - handleChallenge(initiateAuthResult, authenticationDetails, callback, runInBackground).run(); + handleChallenge(clientMetadata, initiateAuthResult, authenticationDetails, callback, runInBackground).run(); } } catch (final Exception e) { callback.onFailure(e); @@ -2732,10 +3063,12 @@ public void run() { * threading. * @return {@link Runnable} for the next step in user authentication. */ - private Runnable handleChallenge(final RespondToAuthChallengeResult challenge, - final AuthenticationDetails authenticationDetails, - final AuthenticationHandler callback, - final boolean runInBackground) { + private Runnable handleChallenge( + final Map clientMetadata, + final RespondToAuthChallengeResult challenge, + final AuthenticationDetails authenticationDetails, + final AuthenticationHandler callback, + final boolean runInBackground) { Runnable nextTask; final CognitoUser cognitoUser = this; nextTask = new Runnable() { @@ -2801,6 +3134,7 @@ public void run() { || CognitoServiceConstants.CHLG_TYPE_SOFTWARE_TOKEN_MFA.equals(challengeName)) { final MultiFactorAuthenticationContinuation multiFactorAuthenticationContinuation = new MultiFactorAuthenticationContinuation( cognitoUser, context, challenge, runInBackground, callback); + multiFactorAuthenticationContinuation.setClientMetaData(clientMetadata); nextTask = new Runnable() { @Override public void run() { @@ -2826,7 +3160,7 @@ public void run() { } }; } else if (CognitoServiceConstants.CHLG_TYPE_DEVICE_SRP_AUTH.equals(challengeName)) { - nextTask = deviceSrpAuthentication(challenge, callback, runInBackground); + nextTask = deviceSrpAuthentication(clientMetadata, challenge, callback, runInBackground); } else if (CognitoServiceConstants.CHLG_TYPE_NEW_PASSWORD_REQUIRED.equals(challengeName)) { final NewPasswordContinuation newPasswordContinuation = new NewPasswordContinuation( cognitoUser, context, usernameInternal, clientId, secretHash, challenge, @@ -2842,6 +3176,7 @@ public void run() { cognitoUser, context, usernameInternal, clientId, CognitoSecretHash.getSecretHash(usernameInternal, clientId, clientSecret), challenge, runInBackground, callback); + challengeContinuation.setClientMetaData(clientMetadata); nextTask = new Runnable() { @Override public void run() { @@ -2859,6 +3194,8 @@ public void run() { * {@code handleChallenge(RespondToAuthChallengeResult challenge, final AuthenticationHandler callback)} * method. * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. * @param authResult REQUIRED: Result from the {@code initiateAuth(...)} * method. * @param callback REQUIRED: Callback for type {@link AuthenticationHandler} @@ -2866,17 +3203,19 @@ public void run() { * threading. * @return {@link Runnable} for the next step in user authentication. */ - private Runnable handleChallenge(final InitiateAuthResult authResult, - final AuthenticationDetails authenticationDetails, - final AuthenticationHandler callback, - final boolean runInBackground) { + private Runnable handleChallenge( + final Map clientMetadata, + final InitiateAuthResult authResult, + final AuthenticationDetails authenticationDetails, + final AuthenticationHandler callback, + final boolean runInBackground) { try { final RespondToAuthChallengeResult challenge = new RespondToAuthChallengeResult(); challenge.setChallengeName(authResult.getChallengeName()); challenge.setSession(authResult.getSession()); challenge.setAuthenticationResult(authResult.getAuthenticationResult()); challenge.setChallengeParameters(authResult.getChallengeParameters()); - return handleChallenge(challenge, authenticationDetails, callback, runInBackground); + return handleChallenge(clientMetadata, challenge, authenticationDetails, callback, runInBackground); } catch (final Exception e) { return new Runnable() { @Override @@ -2890,6 +3229,8 @@ public void run() { /** * This method performs user authentication with username and password without using SRP. * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. * @param authenticationDetails REQUIRED: {@link AuthenticationDetails} * contains details about the custom authentication flow. * @param callback REQUIRED: {@link AuthenticationHandler} callback. @@ -2897,19 +3238,22 @@ public void run() { * threading. * @return {@link Runnable} for the next step in user authentication. */ - private Runnable startWithUserPasswordAuth(final AuthenticationDetails authenticationDetails, - final AuthenticationHandler callback, final boolean runInBackground) { + private Runnable startWithUserPasswordAuth( + final Map clientMetadata, + final AuthenticationDetails authenticationDetails, + final AuthenticationHandler callback, + final boolean runInBackground) { return new Runnable() { @Override public void run() { try { final InitiateAuthRequest initiateAuthRequest = initiateUserPasswordAuthRequest( - authenticationDetails); + clientMetadata, authenticationDetails); final InitiateAuthResult initiateAuthResult = cognitoIdentityProviderClient .initiateAuth(initiateAuthRequest); CognitoUser.this.usernameInternal = initiateAuthResult.getChallengeParameters() .get(CognitoServiceConstants.CHLG_PARAM_USER_ID_FOR_SRP); - handleChallenge(initiateAuthResult, authenticationDetails, callback, runInBackground).run(); + handleChallenge(clientMetadata, initiateAuthResult, authenticationDetails, callback, runInBackground).run(); } catch (final Exception e) { callback.onFailure(e); } @@ -2920,11 +3264,14 @@ public void run() { /** * Creates a authentication request to start authentication with user-password authentication (without SRP) flow. * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. * @param authenticationDetails REQUIRED: {@link AuthenticationDetails}, * contains details required to start authentication flow. * @return {@link InitiateAuthRequest}, request to start the authentication. */ private InitiateAuthRequest initiateUserPasswordAuthRequest( + final Map clientMetadata, AuthenticationDetails authenticationDetails) { if (StringUtils.isBlank(authenticationDetails.getUserId()) @@ -2935,6 +3282,7 @@ private InitiateAuthRequest initiateUserPasswordAuthRequest( final InitiateAuthRequest authRequest = new InitiateAuthRequest(); authRequest.setAuthFlow(CognitoServiceConstants.AUTH_TYPE_INIT_USER_PASSWORD); authRequest.setClientId(clientId); + authRequest.setClientMetadata(clientMetadata); authRequest.addAuthParametersEntry(CognitoServiceConstants.AUTH_PARAM_USERNAME, authenticationDetails.getUserId()); authRequest.addAuthParametersEntry(CognitoServiceConstants.AUTH_PARAM_PASSWORD, @@ -2960,6 +3308,8 @@ private InitiateAuthRequest initiateUserPasswordAuthRequest( * Performs device SRP authentication to identify remembered devices. * Restarts authentication if the device verification does not succeed. * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. * @param challenge REQUIRED: {@link RespondToAuthChallengeResult}, contains * the current challenge. * @param callback REQUIRED: {@link AuthenticationHandler} callback. @@ -2967,28 +3317,30 @@ private InitiateAuthRequest initiateUserPasswordAuthRequest( * threading. * @return {@link Runnable} for the next step in user authentication. */ - private Runnable deviceSrpAuthentication(final RespondToAuthChallengeResult challenge, - final AuthenticationHandler callback, final boolean runInBackground) { + private Runnable deviceSrpAuthentication( + final Map clientMetadata, + final RespondToAuthChallengeResult challenge, + final AuthenticationHandler callback, + final boolean runInBackground) { final String deviceSecret = CognitoDeviceHelper.getDeviceSecret(usernameInternal, pool.getUserPoolId(), context); final String deviceGroupKey = CognitoDeviceHelper.getDeviceGroupKey(usernameInternal, pool.getUserPoolId(), context); final AuthenticationHelper authenticationHelper = new AuthenticationHelper(deviceGroupKey); final RespondToAuthChallengeRequest devicesAuthRequest = initiateDevicesAuthRequest( - authenticationHelper); + clientMetadata, authenticationHelper); try { final RespondToAuthChallengeResult initiateDeviceAuthResult = cognitoIdentityProviderClient .respondToAuthChallenge(devicesAuthRequest); if (CognitoServiceConstants.CHLG_TYPE_DEVICE_PASSWORD_VERIFIER .equals(initiateDeviceAuthResult.getChallengeName())) { final RespondToAuthChallengeRequest challengeResponse = deviceSrpAuthRequest( - initiateDeviceAuthResult, deviceSecret, deviceGroupKey, - authenticationHelper); + clientMetadata, initiateDeviceAuthResult, deviceSecret, deviceGroupKey, authenticationHelper); final RespondToAuthChallengeResult deviceSRPAuthResult = cognitoIdentityProviderClient .respondToAuthChallenge(challengeResponse); - return handleChallenge(deviceSRPAuthResult, null, callback, runInBackground); + return handleChallenge(clientMetadata, deviceSRPAuthResult, null, callback, runInBackground); } else { - return handleChallenge(initiateDeviceAuthResult, null, callback, runInBackground); + return handleChallenge(clientMetadata, initiateDeviceAuthResult, null, callback, runInBackground); } } catch (final NotAuthorizedException na) { final CognitoUser cognitoUser = this; @@ -2998,6 +3350,7 @@ private Runnable deviceSrpAuthentication(final RespondToAuthChallengeResult chal public void run() { final AuthenticationContinuation authenticationContinuation = new AuthenticationContinuation( cognitoUser, context, runInBackground, callback); + authenticationContinuation.setClientMetaData(clientMetadata); callback.getAuthenticationDetails(authenticationContinuation, cognitoUser.getUserId()); } @@ -3016,6 +3369,8 @@ public void run() { * Creates a authentication request to start authentication with user SRP * verification. * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. * @param authenticationDetails REQUIRED: {@link AuthenticationDetails}, * contains details for user SRP authentication. * @param authenticationHelper REQUIRED: Internal helper class for SRP @@ -3024,12 +3379,14 @@ public void run() { * authentication. */ private InitiateAuthRequest initiateUserSrpAuthRequest( + final Map clientMetadata, AuthenticationDetails authenticationDetails, AuthenticationHelper authenticationHelper) { userId = authenticationDetails.getUserId(); final InitiateAuthRequest initiateAuthRequest = new InitiateAuthRequest(); initiateAuthRequest.setAuthFlow(CognitoServiceConstants.AUTH_TYPE_INIT_USER_SRP); initiateAuthRequest.setClientId(clientId); + initiateAuthRequest.setClientMetadata(clientMetadata); initiateAuthRequest.addAuthParametersEntry(CognitoServiceConstants.AUTH_PARAM_SECRET_HASH, CognitoSecretHash.getSecretHash(userId, clientId, clientSecret)); initiateAuthRequest.addAuthParametersEntry(CognitoServiceConstants.AUTH_PARAM_USERNAME, @@ -3066,17 +3423,22 @@ private InitiateAuthRequest initiateUserSrpAuthRequest( * Creates a authentication request to start authentication with custom * authentication. * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. * @param authenticationDetails REQUIRED: {@link AuthenticationDetails}, * contains details required to start a custom authentication * flow. * @return {@link InitiateAuthRequest}, request to start with the user SRP * authentication. */ - private InitiateAuthRequest initiateCustomAuthRequest(final AuthenticationDetails authenticationDetails, - final AuthenticationHelper authenticationHelper) { + private InitiateAuthRequest initiateCustomAuthRequest( + final Map clientMetadata, + final AuthenticationDetails authenticationDetails, + final AuthenticationHelper authenticationHelper) { final InitiateAuthRequest authRequest = new InitiateAuthRequest(); authRequest.setAuthFlow(CognitoServiceConstants.AUTH_TYPE_INIT_CUSTOM_AUTH); authRequest.setClientId(clientId); + authRequest.setClientMetadata(clientMetadata); /** * Compute secret hash based on the client secret and pass into the AuthParameters if @@ -3108,6 +3470,8 @@ private InitiateAuthRequest initiateCustomAuthRequest(final AuthenticationDetail /** * Creates a request to initiate device authentication. * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * lambda functions triggered by forgot password. * @param authenticationHelper REQUIRED: {@link AuthenticationDetails}, * contains details required to start a custom authentication * flow. @@ -3115,11 +3479,13 @@ private InitiateAuthRequest initiateCustomAuthRequest(final AuthenticationDetail * authentication. */ private RespondToAuthChallengeRequest initiateDevicesAuthRequest( + final Map clientMetadata, AuthenticationHelper authenticationHelper) { final RespondToAuthChallengeRequest initiateDevicesAuthRequest = new RespondToAuthChallengeRequest(); initiateDevicesAuthRequest.setClientId(clientId); initiateDevicesAuthRequest .setChallengeName(CognitoServiceConstants.CHLG_TYPE_DEVICE_SRP_AUTH); + initiateDevicesAuthRequest.setClientMetadata(clientMetadata); initiateDevicesAuthRequest.addChallengeResponsesEntry( CognitoServiceConstants.CHLG_RESP_USERNAME, usernameInternal); initiateDevicesAuthRequest.addChallengeResponsesEntry( @@ -3178,7 +3544,8 @@ private InitiateAuthRequest initiateRefreshTokenAuthRequest(CognitoUserSession c * @param authenticationHelper * @return */ - private RespondToAuthChallengeRequest userSrpAuthRequest(final Map challengeParameters, + private RespondToAuthChallengeRequest userSrpAuthRequest(final Map clientMetadata, + final Map challengeParameters, final String password, final String challengeName, final String session, @@ -3242,6 +3609,7 @@ private RespondToAuthChallengeRequest userSrpAuthRequest(final Map clientMetadata, RespondToAuthChallengeResult challenge, String deviceSecret, String deviceGroupKey, @@ -3323,6 +3694,7 @@ public RespondToAuthChallengeRequest deviceSrpAuthRequest( authChallengeRequest.setSession(challenge.getSession()); authChallengeRequest.setChallengeResponses(srpAuthResponses); authChallengeRequest.setUserContextData(getUserContextData()); + authChallengeRequest.setClientMetadata(clientMetadata); return authChallengeRequest; } diff --git a/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/AuthenticationContinuation.java b/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/AuthenticationContinuation.java index 06b140da54..de57b8d430 100644 --- a/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/AuthenticationContinuation.java +++ b/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/AuthenticationContinuation.java @@ -23,6 +23,9 @@ import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser; import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.AuthenticationHandler; +import java.util.Collections; +import java.util.Map; + /** * Defines Continuation for authentication. This Continuation is used when user log-in details * are required to continue to authenticate the user and get tokens. @@ -46,6 +49,7 @@ public class AuthenticationContinuation implements CognitoIdentityProviderContin private final boolean runInBackground; private AuthenticationDetails authenticationDetails = null; + private Map clientMetadata; /** * Constructs a new continuation in the authentication process. @@ -70,6 +74,27 @@ public AuthenticationContinuation(CognitoUser user, this.context = context; this.runInBackground = runInBackground; this.callback = callback; + this.clientMetadata = Collections.emptyMap(); + } + + /** + *

+ * clientMetadata is a map of custom key-value pairs that you can provide as input for any + * custom work flows. Accessor method for clientMetadata. + *

+ * @return ClientMetadata + */ + public Map getClientMetaData() { + return clientMetadata; + } + + /** + * Mutator for clientMetadata. + * @param clientMetadata Metadata to be passed as input to the lambda triggers. + */ + public void setClientMetaData(Map clientMetadata) { + this.clientMetadata.clear(); + this.clientMetadata.putAll(clientMetadata); } /** @@ -97,7 +122,7 @@ public void run() { final Handler handler = new Handler(context.getMainLooper()); Runnable nextStep; try { - nextStep = user.initiateUserAuthentication(authenticationDetails, callback, RUN_IN_BACKGROUND); + nextStep = user.initiateUserAuthentication(clientMetadata, authenticationDetails, callback, RUN_IN_BACKGROUND); } catch (final Exception e) { nextStep = new Runnable() { @Override @@ -112,7 +137,7 @@ public void run() { } else { Runnable nextStep; try { - nextStep = user.initiateUserAuthentication(authenticationDetails, callback, RUN_IN_CURRENT); + nextStep = user.initiateUserAuthentication(clientMetadata, authenticationDetails, callback, RUN_IN_CURRENT); } catch (final Exception e) { nextStep = new Runnable() { @Override diff --git a/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/ChallengeContinuation.java b/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/ChallengeContinuation.java index 80a934bc24..bf5afdf738 100644 --- a/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/ChallengeContinuation.java +++ b/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/ChallengeContinuation.java @@ -86,7 +86,8 @@ public ChallengeContinuation(CognitoUser user, this.username = username; this.callback = callback; this.runInBackground = runInBackground; - challengeResponses = new HashMap(); + challengeResponses = new HashMap<>(); + clientMetaData = new HashMap<>(); } /** @@ -171,7 +172,7 @@ public void run() { final Handler handler = new Handler(context.getMainLooper()); Runnable nextStep; try { - nextStep = user.respondToChallenge(respondToAuthChallengeRequest, callback, RUN_IN_BACKGROUND); + nextStep = user.respondToChallenge(clientMetaData, respondToAuthChallengeRequest, callback, RUN_IN_BACKGROUND); } catch (final Exception e) { nextStep = new Runnable() { @Override @@ -186,7 +187,7 @@ public void run() { } else { Runnable nextStep; try { - nextStep = user.respondToChallenge(respondToAuthChallengeRequest, callback, RUN_IN_CURRENT); + nextStep = user.respondToChallenge(clientMetaData, respondToAuthChallengeRequest, callback, RUN_IN_CURRENT); } catch (final Exception e) { nextStep = new Runnable() { @Override diff --git a/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/MultiFactorAuthenticationContinuation.java b/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/MultiFactorAuthenticationContinuation.java index 08fd77fea2..4e01fdefdc 100644 --- a/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/MultiFactorAuthenticationContinuation.java +++ b/aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/continuations/MultiFactorAuthenticationContinuation.java @@ -26,6 +26,9 @@ import com.amazonaws.mobileconnectors.cognitoidentityprovider.util.CognitoServiceConstants; import com.amazonaws.services.cognitoidentityprovider.model.RespondToAuthChallengeResult; +import java.util.Collections; +import java.util.Map; + /** * This is a Continuation for multi-factor authentication. */ @@ -47,6 +50,7 @@ public class MultiFactorAuthenticationContinuation implements CognitoIdentityPro private final boolean runInBackground; private final AuthenticationHandler callback; private String mfaCode = null; + private Map clientMetadata; /** * Constructs a multi-factor authentication continuation. @@ -67,6 +71,27 @@ public MultiFactorAuthenticationContinuation(CognitoUser user, this.callback = callback; this.runInBackground = runInBackground; this.challenge = challenge; + this.clientMetadata = Collections.emptyMap(); + } + + /** + *

+ * clientMetadata is a map of custom key-value pairs that you can provide as input for any + * custom work flows. Accessor method for clientMetadata. + *

+ * @return ClientMetadata + */ + public Map getClientMetaData() { + return clientMetadata; + } + + /** + * Mutator for clientMetadata. + * @param clientMetadata Metadata to be passed as input to the lambda triggers. + */ + public void setClientMetaData(Map clientMetadata) { + this.clientMetadata.clear(); + this.clientMetadata.putAll(clientMetadata); } /** @@ -104,7 +129,7 @@ public void run() { Runnable nextStep; try { - nextStep = user.respondToMfaChallenge(mfaCode, challenge, callback, + nextStep = user.respondToMfaChallenge(clientMetadata, mfaCode, challenge, callback, RUN_IN_BACKGROUND); } catch (final Exception e) { nextStep = new Runnable() { @@ -120,7 +145,7 @@ public void run() { } else { Runnable nextStep; try { - nextStep = user.respondToMfaChallenge(mfaCode, challenge, callback, RUN_IN_CURRENT); + nextStep = user.respondToMfaChallenge(clientMetadata, mfaCode, challenge, callback, RUN_IN_CURRENT); } catch (final Exception e) { nextStep = new Runnable() { @Override diff --git a/aws-android-sdk-mobile-client/src/main/java/com/amazonaws/mobile/client/AWSMobileClient.java b/aws-android-sdk-mobile-client/src/main/java/com/amazonaws/mobile/client/AWSMobileClient.java index 687adfe862..82f0d7d61a 100644 --- a/aws-android-sdk-mobile-client/src/main/java/com/amazonaws/mobile/client/AWSMobileClient.java +++ b/aws-android-sdk-mobile-client/src/main/java/com/amazonaws/mobile/client/AWSMobileClient.java @@ -1115,23 +1115,41 @@ public void signIn(final String username, final String password, final Map validationData, final Callback callback) { + signIn(username, password, validationData, Collections.emptyMap(), callback); + } + + @AnyThread + public void signIn(final String username, + final String password, + final Map validationData, + final Map clientMetadata, + final Callback callback) { final InternalCallback internalCallback = new InternalCallback(callback); - internalCallback.async(_signIn(username, password, validationData, internalCallback)); + internalCallback.async(_signIn(username, password, validationData, clientMetadata, internalCallback)); } @WorkerThread public SignInResult signIn(final String username, final String password, final Map validationData) throws Exception { + return signIn(username, password, validationData, Collections.emptyMap()); + } + + @WorkerThread + public SignInResult signIn(final String username, + final String password, + final Map validationData, + final Map clientMetadata) throws Exception { final InternalCallback internalCallback = new InternalCallback(); - return internalCallback.await(_signIn(username, password, validationData, internalCallback)); + return internalCallback.await(_signIn(username, password, validationData, clientMetadata, internalCallback)); } private Runnable _signIn(final String username, final String password, final Map validationData, + final Map clientMetadata, final Callback callback) { this.signInCallback = callback; @@ -1142,85 +1160,90 @@ private Runnable _signIn(final String username, @Override public void run() { try { - userpool.getUser(username).getSession(new AuthenticationHandler() { + userpool.getUser(username).getSession( + clientMetadata, + new AuthenticationHandler() { + @Override + public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice) { + try { + mCognitoUserSession = userSession; + signInState = SignInState.DONE; + } catch (Exception e) { + signInCallback.onError(e); + signInCallback = null; + } - @Override - public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice) { - try { - mCognitoUserSession = userSession; - signInState = SignInState.DONE; - } catch (Exception e) { - signInCallback.onError(e); - signInCallback = null; - } + try { + if (isFederationEnabled()) { + federatedSignInWithoutAssigningState(userpoolsLoginKey, mCognitoUserSession.getIdToken().getJWTToken()); + } - try { - if (isFederationEnabled()) { - federatedSignInWithoutAssigningState(userpoolsLoginKey, mCognitoUserSession.getIdToken().getJWTToken()); + releaseSignInWait(); + } catch (Exception e) { + Log.w(TAG, "Failed to federate tokens during sign-in", e); + } finally { + setUserState(new UserStateDetails(UserState.SIGNED_IN, getSignInDetailsMap())); } - releaseSignInWait(); - } catch (Exception e) { - Log.w(TAG, "Failed to federate tokens during sign-in", e); - } finally { - setUserState(new UserStateDetails(UserState.SIGNED_IN, getSignInDetailsMap())); + signInCallback.onResult(SignInResult.DONE); } - signInCallback.onResult(SignInResult.DONE); - } - - @Override - public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) { - Log.d(TAG, "Sending password."); - try { - if (awsConfiguration.optJsonObject("Auth") != null && awsConfiguration.optJsonObject("Auth").has("authenticationFlowType") && awsConfiguration.optJsonObject("Auth").getString("authenticationFlowType").equals("CUSTOM_AUTH")) { - final HashMap authParameters = new HashMap(); - authenticationContinuation.setAuthenticationDetails(new AuthenticationDetails(username, password, authParameters, validationData)); - } else { - authenticationContinuation.setAuthenticationDetails(new AuthenticationDetails(username, password, validationData)); + @Override + public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) { + Log.d(TAG, "Sending password."); + try { + if (awsConfiguration.optJsonObject("Auth") != null && + awsConfiguration.optJsonObject("Auth").has("authenticationFlowType") && + awsConfiguration.optJsonObject("Auth").getString("authenticationFlowType").equals("CUSTOM_AUTH") + ) { + final HashMap authParameters = new HashMap(); + authenticationContinuation.setAuthenticationDetails(new AuthenticationDetails(username, password, authParameters, validationData)); + } else { + authenticationContinuation.setAuthenticationDetails(new AuthenticationDetails(username, password, validationData)); + } + } catch (JSONException e) { + e.printStackTrace(); } - } catch (JSONException e) { - e.printStackTrace(); + authenticationContinuation.continueTask(); } - authenticationContinuation.continueTask(); - } - @Override - public void getMFACode(MultiFactorAuthenticationContinuation continuation) { - signInMfaContinuation = continuation; - CognitoUserCodeDeliveryDetails parameters = continuation.getParameters(); - signInState = SignInState.SMS_MFA; - signInCallback.onResult( - new SignInResult( - SignInState.SMS_MFA, - new UserCodeDeliveryDetails( - parameters.getDestination(), - parameters.getDeliveryMedium(), - parameters.getAttributeName() - ) - ) - ); - } + @Override + public void getMFACode(MultiFactorAuthenticationContinuation continuation) { + signInMfaContinuation = continuation; + CognitoUserCodeDeliveryDetails parameters = continuation.getParameters(); + signInState = SignInState.SMS_MFA; + signInCallback.onResult( + new SignInResult( + SignInState.SMS_MFA, + new UserCodeDeliveryDetails( + parameters.getDestination(), + parameters.getDeliveryMedium(), + parameters.getAttributeName() + ) + ) + ); + } - @Override - public void authenticationChallenge(ChallengeContinuation continuation) { - try { - signInState = SignInState.valueOf(continuation.getChallengeName()); - signInChallengeContinuation = continuation; - - signInCallback.onResult(new SignInResult( - signInState, - continuation.getParameters())); - } catch (IllegalArgumentException e) { - signInCallback.onError(e); + @Override + public void authenticationChallenge(ChallengeContinuation continuation) { + try { + signInState = SignInState.valueOf(continuation.getChallengeName()); + signInChallengeContinuation = continuation; + + signInCallback.onResult(new SignInResult( + signInState, + continuation.getParameters())); + } catch (IllegalArgumentException e) { + signInCallback.onError(e); + } } - } - @Override - public void onFailure(Exception exception) { - signInCallback.onError(exception); + @Override + public void onFailure(Exception exception) { + signInCallback.onError(exception); + } } - }); + ); } catch (Exception e) { callback.onError(e); } @@ -1231,19 +1254,33 @@ public void onFailure(Exception exception) { @AnyThread public void confirmSignIn(final String signInChallengeResponse, final Callback callback) { + confirmSignIn(signInChallengeResponse, Collections.emptyMap(), callback); + } + + @AnyThread + public void confirmSignIn(final String signInChallengeResponse, + final Map clientMetadata, + final Callback callback) { final InternalCallback internalCallback = new InternalCallback(callback); - internalCallback.async(_confirmSignIn(signInChallengeResponse, internalCallback)); + internalCallback.async(_confirmSignIn(signInChallengeResponse, clientMetadata, internalCallback)); } @WorkerThread public SignInResult confirmSignIn(final String signInChallengeResponse) throws Exception { + return confirmSignIn(signInChallengeResponse, Collections.emptyMap()); + } + + @WorkerThread + public SignInResult confirmSignIn(final String signInChallengeResponse, + final Map clientMetadata) throws Exception { final InternalCallback internalCallback = new InternalCallback(); - return internalCallback.await(_confirmSignIn(signInChallengeResponse, internalCallback)); + return internalCallback.await(_confirmSignIn(signInChallengeResponse, clientMetadata, internalCallback)); } private Runnable _confirmSignIn(final String signInChallengeResponse, + final Map clientMetadata, final Callback callback) { return new Runnable() { @@ -1260,12 +1297,14 @@ public void run() { switch (signInState) { case SMS_MFA: signInMfaContinuation.setMfaCode(signInChallengeResponse); + signInMfaContinuation.setClientMetaData(clientMetadata); detectedContinuation = signInMfaContinuation; signInCallback = new InternalCallback(callback); break; case NEW_PASSWORD_REQUIRED: ((NewPasswordContinuation) signInChallengeContinuation) .setPassword(signInChallengeResponse); + signInChallengeContinuation.setClientMetaData(clientMetadata); detectedContinuation = signInChallengeContinuation; signInCallback = new InternalCallback(callback); break; @@ -1760,47 +1799,49 @@ public void run() { } try { - userpool.getCurrentUser().getSession(new AuthenticationHandler() { - - @Override - public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice) { - try { - mCognitoUserSession = userSession; - callback.onResult(new Tokens( - userSession.getAccessToken().getJWTToken(), - userSession.getIdToken().getJWTToken(), - userSession.getRefreshToken().getToken() - )); - } catch (Exception e) { - callback.onError(e); + userpool.getCurrentUser().getSession( + Collections.emptyMap(), + new AuthenticationHandler() { + @Override + public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice) { + try { + mCognitoUserSession = userSession; + callback.onResult(new Tokens( + userSession.getAccessToken().getJWTToken(), + userSession.getIdToken().getJWTToken(), + userSession.getRefreshToken().getToken() + )); + } catch (Exception e) { + callback.onError(e); + } } - } - @Override - public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) { - signalTokensNotAvailable(null); - } + @Override + public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) { + signalTokensNotAvailable(null); + } - @Override - public void getMFACode(MultiFactorAuthenticationContinuation continuation) { - signalTokensNotAvailable(null); - } + @Override + public void getMFACode(MultiFactorAuthenticationContinuation continuation) { + signalTokensNotAvailable(null); + } - @Override - public void authenticationChallenge(ChallengeContinuation continuation) { - signalTokensNotAvailable(null); - } + @Override + public void authenticationChallenge(ChallengeContinuation continuation) { + signalTokensNotAvailable(null); + } - @Override - public void onFailure(Exception exception) { - signalTokensNotAvailable(exception); - } + @Override + public void onFailure(Exception exception) { + signalTokensNotAvailable(exception); + } - private void signalTokensNotAvailable(final Exception e) { - Log.w(TAG, "signalTokensNotAvailable"); - callback.onError(new Exception("No cached session.", e)); + private void signalTokensNotAvailable(final Exception e) { + Log.w(TAG, "signalTokensNotAvailable"); + callback.onError(new Exception("No cached session.", e)); + } } - }); + ); } catch (Exception e) { callback.onError(e); } @@ -2090,10 +2131,29 @@ public void onFailure(Exception exception) { * @param callback */ @AnyThread - public void resendSignUp(final String username, - final Callback callback) { + public void resendSignUp( + final String username, + final Callback callback) { + resendSignUp(username, Collections.emptyMap(), callback); + } + + /** + * Used when a user has attempted sign-up previously and wants to continue the process. + * Note: If the user tries through the normal process with the same username, then it will + * fail and this method is required. + * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. + * @param username + * @param callback + */ + @AnyThread + public void resendSignUp( + final String username, + final Map clientMetadata, + final Callback callback) { final InternalCallback internalCallback = new InternalCallback(callback); - internalCallback.async(_resendSignUp(username, internalCallback)); + internalCallback.async(_resendSignUp(username, clientMetadata, internalCallback)); } /** @@ -2105,35 +2165,54 @@ public void resendSignUp(final String username, */ @WorkerThread public SignUpResult resendSignUp(final String username) throws Exception { + return resendSignUp(username, Collections.emptyMap()); + } + + /** + * Used when a user has attempted sign-up previously and wants to continue the process. + * Note: If the user tries through the normal process with the same username, then it will + * fail and this method is required. + * + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. + * @param username + */ + @WorkerThread + public SignUpResult resendSignUp(final String username, final Map clientMetadata) throws Exception { final InternalCallback internalCallback = new InternalCallback(); - return internalCallback.await(_resendSignUp(username, internalCallback)); + return internalCallback.await(_resendSignUp(username, clientMetadata, internalCallback)); } - private Runnable _resendSignUp(final String username, - final Callback callback) { + private Runnable _resendSignUp( + final String username, + final Map clientMetadata, + final Callback callback) { return new Runnable() { @Override public void run() { - userpool.getUser(username).resendConfirmationCodeInBackground(new VerificationHandler() { - @Override - public void onSuccess(CognitoUserCodeDeliveryDetails verificationCodeDeliveryMedium) { - UserCodeDeliveryDetails userCodeDeliveryDetails = new UserCodeDeliveryDetails( - verificationCodeDeliveryMedium.getDestination(), - verificationCodeDeliveryMedium.getDeliveryMedium(), - verificationCodeDeliveryMedium.getAttributeName() - ); - callback.onResult(new SignUpResult( - false, - userCodeDeliveryDetails, - null - )); - } + userpool.getUser(username).resendConfirmationCodeInBackground( + clientMetadata, + new VerificationHandler() { + @Override + public void onSuccess(CognitoUserCodeDeliveryDetails verificationCodeDeliveryMedium) { + UserCodeDeliveryDetails userCodeDeliveryDetails = new UserCodeDeliveryDetails( + verificationCodeDeliveryMedium.getDestination(), + verificationCodeDeliveryMedium.getDeliveryMedium(), + verificationCodeDeliveryMedium.getAttributeName() + ); + callback.onResult(new SignUpResult( + false, + userCodeDeliveryDetails, + null + )); + } - @Override - public void onFailure(Exception exception) { - callback.onError(exception); - } - }); + @Override + public void onFailure(Exception exception) { + callback.onError(exception); + } + } + ); } }; } @@ -2152,7 +2231,7 @@ public void forgotPassword(final String username, final Callback callback) { final InternalCallback internalCallback = new InternalCallback(callback); - internalCallback.async(_forgotPassword(username, internalCallback, clientMetadata)); + internalCallback.async(_forgotPassword(username, clientMetadata, internalCallback)); } /** @@ -2165,7 +2244,7 @@ public ForgotPasswordResult forgotPassword(final String username, final Map clientMetadata) throws Exception { final InternalCallback internalCallback = new InternalCallback(); - return internalCallback.await(_forgotPassword(username, internalCallback, clientMetadata)); + return internalCallback.await(_forgotPassword(username, clientMetadata, internalCallback)); } /** @@ -2180,8 +2259,7 @@ public void forgotPassword(final String username, final Callback callback) { final InternalCallback internalCallback = new InternalCallback(callback); - internalCallback.async(_forgotPassword(username, internalCallback, - Collections.emptyMap())); + internalCallback.async(_forgotPassword(username, Collections.emptyMap(), internalCallback)); } /** @@ -2193,12 +2271,12 @@ public void forgotPassword(final String username, public ForgotPasswordResult forgotPassword(final String username) throws Exception { final InternalCallback internalCallback = new InternalCallback(); - return internalCallback.await(_forgotPassword(username, internalCallback, Collections.emptyMap())); + return internalCallback.await(_forgotPassword(username, Collections.emptyMap(), internalCallback)); } private Runnable _forgotPassword(final String username, - final Callback callback, - final Map clientMetadata) { + final Map clientMetadata, + final Callback callback) { return new Runnable() { @Override @@ -2414,29 +2492,65 @@ public void onFailure(Exception exception) { * @param callback verification delivery information */ @AnyThread - public void updateUserAttributes(final Map userAttributes, - final Callback> callback) { + public void updateUserAttributes( + final Map userAttributes, + final Callback> callback) { + updateUserAttributes(userAttributes, Collections.emptyMap(), callback); + } + + /** + * Sends a map of user attributes to update. If an attribute needs to + * be verified, then the verification delivery information is returned. + * @param userAttributes the attributes i.e. email + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. + * @param callback verification delivery information + */ + @AnyThread + public void updateUserAttributes( + final Map userAttributes, + final Map clientMetadata, + final Callback> callback) { InternalCallback internalCallback = new InternalCallback>(callback); - internalCallback.async(_updateUserAttributes(userAttributes, internalCallback)); + internalCallback.async(_updateUserAttributes(userAttributes, clientMetadata, internalCallback)); + } + + /** + * Sends a map of user attributes to update. If an attribute needs to + * be verified, then the verification delivery information is returned. + * @param userAttributes the attributes i.e. email + * @return verification delivery information + * @throws Exception + */ + @WorkerThread + public List updateUserAttributes( + final Map userAttributes) throws Exception { + return updateUserAttributes(userAttributes, Collections.emptyMap()); } /** * Sends a map of user attributes to update. If an attribute needs to * be verified, then the verification delivery information is returned. * @param userAttributes the attributes i.e. email + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * custom workflow. * @return verification delivery information * @throws Exception */ @WorkerThread - public List updateUserAttributes(final Map userAttributes) throws Exception { + public List updateUserAttributes( + final Map userAttributes, + final Map clientMetadata) throws Exception { InternalCallback> internalCallback = new InternalCallback>(); - return internalCallback.await(_updateUserAttributes(userAttributes, internalCallback)); + return internalCallback.await(_updateUserAttributes(userAttributes, clientMetadata, internalCallback)); } - private Runnable _updateUserAttributes(final Map userAttributes, - final Callback> callback) { + private Runnable _updateUserAttributes( + final Map userAttributes, + final Map clientMetadata, + final Callback> callback) { return new Runnable() { @Override @@ -2453,25 +2567,29 @@ public void run() { } } - userpool.getCurrentUser().updateAttributes(cognitoUserAttributes, new UpdateAttributesHandler() { - @Override - public void onSuccess(List attributesVerificationList) { - final List list = new LinkedList(); - for (CognitoUserCodeDeliveryDetails details : attributesVerificationList) { - list.add(new UserCodeDeliveryDetails( - details.getDestination(), - details.getDeliveryMedium(), - details.getAttributeName() - )); - } - callback.onResult(list); - } + userpool.getCurrentUser().updateAttributes( + cognitoUserAttributes, + clientMetadata, + new UpdateAttributesHandler() { + @Override + public void onSuccess(List attributesVerificationList) { + final List list = new LinkedList(); + for (CognitoUserCodeDeliveryDetails details : attributesVerificationList) { + list.add(new UserCodeDeliveryDetails( + details.getDestination(), + details.getDeliveryMedium(), + details.getAttributeName() + )); + } + callback.onResult(list); + } - @Override - public void onFailure(Exception exception) { - callback.onError(exception); - } - }); + @Override + public void onFailure(Exception exception) { + callback.onError(exception); + } + } + ); } }; } @@ -2484,9 +2602,23 @@ public void onFailure(Exception exception) { @AnyThread public void verifyUserAttribute(final String attributeName, final Callback callback) { + verifyUserAttribute(attributeName, Collections.emptyMap(), callback); + } + + /** + * Verify an attribute like email. + * @param attributeName i.e. email + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * lambda functions triggered by forgot password. + * @param callback verification delivery information + */ + @AnyThread + public void verifyUserAttribute(final String attributeName, + final Map clientMetadata, + final Callback callback) { InternalCallback internalCallback = new InternalCallback(callback); - internalCallback.async(_verifyUserAttribute(attributeName, internalCallback)); + internalCallback.async(_verifyUserAttribute(attributeName, clientMetadata, internalCallback)); } /** @@ -2497,12 +2629,27 @@ public void verifyUserAttribute(final String attributeName, */ @WorkerThread public UserCodeDeliveryDetails verifyUserAttribute(final String attributeName) throws Exception { + return verifyUserAttribute(attributeName, Collections.emptyMap()); + } + + /** + * Verify an attribute like email. + * @param attributeName i.e. email + * @param clientMetadata A map of custom key-value pairs that is passed to the lambda function for + * lambda functions triggered by forgot password. + * @return verification delivery information + * @throws Exception + */ + @WorkerThread + public UserCodeDeliveryDetails verifyUserAttribute(final String attributeName, + final Map clientMetadata) throws Exception { InternalCallback internalCallback = new InternalCallback(); - return internalCallback.await(_verifyUserAttribute(attributeName, internalCallback)); + return internalCallback.await(_verifyUserAttribute(attributeName, clientMetadata, internalCallback)); } private Runnable _verifyUserAttribute(final String attributeName, + final Map clientMetadata, final Callback callback) { return new Runnable() { @@ -2514,6 +2661,7 @@ public void run() { } userpool.getCurrentUser().getAttributeVerificationCodeInBackground( + clientMetadata, attributeName, new VerificationHandler() { @Override