Skip to content

Instantly share code, notes, and snippets.

@adrianhall
Created April 18, 2018 19:56
Show Gist options
  • Save adrianhall/14f79925627d677e66838ea750badacf to your computer and use it in GitHub Desktop.
Save adrianhall/14f79925627d677e66838ea750badacf to your computer and use it in GitHub Desktop.
AuthenticationHandler implementation
/**
* 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