Created
April 18, 2018 19:56
-
-
Save adrianhall/14f79925627d677e66838ea750badacf to your computer and use it in GitHub Desktop.
AuthenticationHandler implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Part of the AuthenticationHandler interface | |
* | |
* Called when the Amazon Cognito service returns a valid set of tokens, when the tokens | |
* are available (cached) or after successful completion of the authentication process. | |
* | |
* @param userSession Contains the valid user tokens | |
* @param newDevice If this is a new device, then contains the device information | |
*/ | |
override fun onSuccess(userSession: CognitoUserSession?, newDevice: CognitoDevice?) { | |
Log.i(TAG, "onSuccess()") | |
val cognitoUser = (identityManager as AWSIdentityManager).userPool.currentUser | |
val user = User(cognitoUser.userId) | |
// This is the important token storage part | |
userSession?.let { | |
user.username = it.username | |
user.tokens[TokenType.ACCESS_TOKEN] = userSession.accessToken.jwtToken | |
user.tokens[TokenType.ID_TOKEN] = userSession.idToken.jwtToken | |
user.tokens[TokenType.REFRESH_TOKEN] = userSession.refreshToken.token | |
} | |
// Obtain the details about the user from Amazon Cognito | |
cognitoUser.getDetails(object : GetDetailsHandler { | |
/** | |
* This method is called on successfully fetching user attributes. | |
* `attributesList` contains all attributes set for the user. | |
* | |
* @param cognitoUserDetails contains the users' details retrieved from the Cognito Service | |
*/ | |
override fun onSuccess(cognitoUserDetails: CognitoUserDetails?) { | |
Log.i(TAG, "getDetails::onSuccess()") | |
cognitoUserDetails?.let { | |
for ((key, value) in it.attributes.attributes) { | |
user.userAttributes[key] = value | |
} | |
} | |
Log.d(TAG, "getDetails::onSuccess(): Details received") | |
runOnUiThread { | |
identityManager.signin(user) | |
[email protected]() | |
} | |
} | |
/** | |
* This method is called upon encountering errors during this operation. | |
* Probe `exception` for the cause of this exception. | |
* | |
* @param exception REQUIRED: Failure details. | |
*/ | |
override fun onFailure(exception: Exception?) { | |
// Print out the message - this should technically not happen | |
val message: String = exception?.localizedMessage ?: "Unknown error" | |
Log.e(TAG, "getDetails::onFailure $message", exception) | |
runOnUiThread { | |
identityManager.signin(user) | |
[email protected]() | |
} | |
} | |
}) | |
} | |
/** | |
* This method is called when a fatal exception was encountered during | |
* authentication. The current authentication process continue because of the error | |
* , hence a continuation is not available. Probe `exception` for details. | |
* | |
* @param exception is this Exception leading to authentication failure. | |
*/ | |
override fun onFailure(exception: Exception?) { | |
val message: String = exception?.localizedMessage ?: "Error submitting credentials" | |
Log.e(TAG, "onFailure: $message", exception) | |
runOnUiThread { | |
alert("Error submitting credentials") { | |
title = "Login Denied" | |
positiveButton("Close") { } | |
}.show() | |
} | |
} | |
/** | |
* Obtain the credentials for the user. | |
* | |
* @param continuation A continuation object for continuing with the task | |
* @param userId The user-ID (username or alias) used in authentication. | |
*/ | |
override fun getAuthenticationDetails(continuation: AuthenticationContinuation?, userId: String?) { | |
// This should never occur!!! That's because we use initiateUserAuthentication() which gets | |
// supplied with the authenticationDetails. | |
Log.i(TAG, "getAuthenticationDetails()") | |
val username = loginform_username.getContent() | |
val password = loginform_password.getContent() | |
val authDetails = AuthenticationDetails(username, password, HashMap<String, String>()) | |
continuation?.let { | |
it.setAuthenticationDetails(authDetails) | |
it.continueTask() | |
} | |
} | |
/** | |
* Call out to the dev to respond to a challenge. | |
* The authentication process as presented the user with the a challenge, to successfully authenticate. | |
* This a generic challenge, that is not MFA or user password verification. | |
* | |
* @param continuation contains details about the challenge and allows dev to respond to the | |
* challenge. | |
*/ | |
@SuppressLint("InflateParams") | |
override fun authenticationChallenge(continuation: ChallengeContinuation?) { | |
Log.i(TAG, "authenticationChallenge: ${continuation?.challengeName}") | |
when (continuation?.challengeName) { | |
"NEW_PASSWORD_REQUIRED" -> { | |
runOnUiThread { | |
val newPasswordDialog = layoutInflater.inflate(R.layout.dialog_new_password, null) | |
val passwordInput = newPasswordDialog.find(R.id.newpassworddialog_password) as EditText | |
alert { | |
title = "Enter New Password" | |
customView = newPasswordDialog | |
positiveButton("OK") { | |
// Respond to the challenge | |
Log.i(TAG, "authenticationChallenge: new password = ${passwordInput.getContent()}") | |
continuation.setChallengeResponse("NEW_PASSWORD", passwordInput.getContent()) | |
continuation.setChallengeResponse("USERNAME", loginform_username.getContent()) | |
thread(start = true) { continuation.continueTask() } | |
} | |
}.show() | |
} | |
} | |
else -> { | |
Log.e(TAG, "authenticationChallenge: error - unexpected challenge") | |
throw NotImplementedError("authenticationChallenge is not implemented") | |
} | |
} | |
} | |
/** | |
* Call out to the dev to send MFA code. | |
* MFA code would have been sent via the deliveryMethod before this is invoked. | |
* This callback can be invoked in two scenarios - | |
* 1) MFA verification is required and only one possible MFA delivery medium is | |
* available. | |
* 2) MFA verification is required and a MFA delivery medium was successfully set. | |
* 3) An MFA code sent earlier was incorrect and at-least one more attempt to send | |
* MFA code is available. | |
* | |
* @param continuation medium through which the MFA will be delivered | |
*/ | |
@Suppress("InflateParams") | |
override fun getMFACode(continuation: MultiFactorAuthenticationContinuation?) { | |
runOnUiThread { | |
val mfaPromptDialog = layoutInflater.inflate(R.layout.dialog_mfa_prompt, null) | |
val mfaInput = mfaPromptDialog.find(R.id.mfapromptdialog_code) as MFAEditText | |
continuation?.let { | |
val mfaInstructions = mfaPromptDialog.find(R.id.mfapromptdialog_instructions) as TextView | |
mfaInstructions.text = "Enter the code we just sent to ${it.parameters.deliveryMedium}:${it.parameters.destination}" | |
} | |
alert { | |
title = "Multi-factor Code Required" | |
customView = mfaPromptDialog | |
positiveButton("OK") { | |
continuation?.let { | |
Log.i(TAG, "getMFACode: new code = ${mfaInput.getContent()}") | |
it.setMfaCode(mfaInput.getContent()) | |
thread(start = true) { it.continueTask() } | |
} | |
} | |
}.show() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment