サインアップやサインイン、パスワード再発行など、ユーザ管理がないアプリケーションを見かけない日はありません
とはいえ自前でバックエンドのシステムを作るのも大変...
でもAmplifyはちょっとリッチすぎる気がする...
そんな私のための備忘録です
下記のことができます
- メールアドレス、パスワードによるサインアップ、ログアウト
- メールアドレス認証
- パスワード再発行
メールアドレスやパスワードはガッツリ個人情報です
その辺りの管理をAWSにお願いできるのはかなり気楽ですよねー
前準備
SES (Simple Email Service)に配信用のメールアドレスを登録する
AWSのSESに、認証コードを送信する用のメールアドレスを登録、認証しておきます
登録したメールアドレスに確認用のURLが届くのでクリックすると認証できます
Verification Statusが"verified"になっていれば使用可能です
"Send a Test Email"ボタンで自分自身にテストメールを送り、無事受信できればOKです
Cognitoにユーザプールを作成する
AWS Cognitoのダッシュボードにアクセスし、UserPoolを作成します
送信元のEメールアドレスに、先ほど認証したメールアドレスを登録し、Amazon SESによるEメール配信を”はい”に指定します
アプリクライアント側の設定はこんな感じです
Androidクライアントを実装する
依存関係
依存関係に、下記を追加します
implementation 'com.amazonaws:aws-android-sdk-cognitoidentityprovider:2.19.0'
いよいよ実装
初期化
val userPoolId = "ap-northeast-1_xxxxxxxx"
val clientId = "yyyyyyyyyyyyy"
val clientSecret = "zzzzzzzzzzzzzz"
val userPool = CognitoUserPool(context, userPoolId, clientId, clientSecret, Regions.AP_NORTHEAST_1)
先ほど作成したCognitoUserPoolのUserPoolId, ClientId, ClientSecretを使ってインスタンスを作成します
以後、このuserPoolインスタンスを使って各種アクセスをおこないます
サインアップ
サインアップをするには2つのメソッドが必要です
- signUpInBackground : userPoolにユーザを作成する
- confirmSignUpInBackground : cognitoユーザを認証する
まずはsignUpInBackgroundです
この時点でuserPoolにユーザが追加され、本人確認用コードが送信されます
ただしUNCONFIRMEDの状態で何もできません
fun signUp(username: String,
password: String,
successCallback: () -> Unit,
failureCallback: (exception: Exception?) -> Unit
) {
val userAttributes = CognitoUserAttributes()
userPool.signUpInBackground(username, password, userAttributes, null,
object : SignUpHandler {
override fun onSuccess(user: CognitoUser?, signUpResult: SignUpResult?) {
successCallback()
}
override fun onFailure(exception: java.lang.Exception?) {
failureCallback(exception)
}
}
)
}
このままだとサインインも何もできないので、続いて本人確認をする必要があります
送信された認証用コードを使ってconfirmSignUpInBackgroundを呼ぶとCONFIRMED状態になります
fun confirmSignUp(username: String,
verifyCode: String,
successCallback: () -> Unit,
failureCallback: (exception: Exception?) -> Unit
) {
userPool.getUser(username).confirmSignUpInBackground(verifyCode, false,
object : GenericHandler {
override fun onSuccess() {
successCallback()
}
override fun onFailure(exception: Exception?) {
failureCallback(exception)
}
})
}
サインイン
サインインにはgetSessionInBackgroundを使用します
一度サインインに成功すると端末内にセッションとして状態が保持されるようです
今回は2段階認証を有効化していないので、getMFACodeなどはコールされない(はず)
fun signIn(username: String,
password: String,
successCallback: () -> Unit,
failureCallback: (exception: Exception?) -> Unit
) {
userPool.getUser(username).getSessionInBackground(
object : AuthenticationHandler {
override fun onSuccess(userSession: CognitoUserSession?, newDevice: CognitoDevice?) {
successCallback()
}
override fun getAuthenticationDetails(
authenticationContinuation: AuthenticationContinuation?,
userId: String?
) {
val authenticationDetails = AuthenticationDetails(userId, password, null)
authenticationContinuation!!.setAuthenticationDetails(authenticationDetails)
authenticationContinuation.continueTask()
}
override fun getMFACode(continuation: MultiFactorAuthenticationContinuation?) {
/* should not be called */
}
override fun authenticationChallenge(continuation: ChallengeContinuation?) {
/* should not be called */
}
override fun onFailure(exception: Exception?) {
failureCallback(exception)
}
})
}
パスワード再発行
パスワードの再発行にはforgotPasswordInBackGroundを使います
実行するとパスワード再発行用の認証コードがメールアドレスに送られ、getResetCodeコールバックが返ってきます
その時に一緒に得られるcontinuationを使って、新しい認証コードとパスワードを設定すると完了です
認証周りはサインアップと似たようなものかと思いきや、少し違いますね
fun forgotPassword(username: String,
successCallback: () -> Unit,
verifyCallback: () -> Unit,
failureCallback: (exception: Exception?) -> Unit
) {
userPool.getUser(username).forgotPasswordInBackground(
object: ForgotPasswordHandler {
override fun onSuccess() {
successCallback()
}
override fun getResetCode(forgotPasswordContinuation: ForgotPasswordContinuation?) {
continuation = forgotPasswordContinuation
verifyCallback()
}
override fun onFailure(exception: Exception?) {
failureCallback(exception)
}
})
}
fun verifyPasswordReset(password: String, verifyCode: String) {
continuation?.apply {
setPassword(password)
setVerificationCode(verifyCode)
continueTask()
}
}
まとめ
cognito側の仕様にはあまり触れていませんが、これでさっくり使えます!