Amplify SDKを使ってMFA対応ログイン処理を書くときの覚書
AWS Amplify Advent Calendar 2020、6日目の記事です。 AWS AmplifyのSDKを使うとCognitoのMFAも割と簡単に実装できます。が、AWSが用意しているUIではなく自前のUIで […]
目次
AWS Amplify Advent Calendar 2020、6日目の記事です。
AWS AmplifyのSDKを使うとCognitoのMFAも割と簡単に実装できます。が、AWSが用意しているUIではなく自前のUIでやろうと思うと多少頑張らないといけないところがあるので、その辺について軽く触れます。
Auth.signIn
の挙動
MFA設定の有無に関わらず、自前でログイン処理を走らせる場合はAuth.signIn
を利用します。
const result = await Auth.signIn(username, password)
この処理の戻り値を見ることで、MFAなどを利用した多段階認証であるかを確認できます。
const result = await Auth.signIn(username, password)
const hasChallenge = Object.prototype.hasOwnProperty.call(result, 'challengeName');
if (!hasChallenge) {
// Login Complete!
} else {
// MFAなどの処理
}
MFAのコードを使った認証
window.confirm
なり自前のJSXなりでSMSやアプリに表示されたコードを入力するUIを実装します。
そしてそこから送られたコードをAmplify Auth(もといCognito User Pools)に渡す時には、先ほどの戻り値を一部利用します。
// なにかしらの方法でAuth.signInの戻り値を取得する
const restoredResult = getSignInResult()
// なにがしらの実装でMFAのコードを取得する
const challengeCode = getChallengeCodeFromAnyUI()
if (!restoredResult
) throw new Error('No Cognito User data')
await Auth.confirmSignIn(restoredResult
, challengeCode, restoredResult
.challengeName)
Auth.signIn
の戻り値にはCognitoUser
クラスが入っています。その中にあるchallengeName
を使うことで、ユーザーがSMS / TOTPどちらで認証しようとしているかなどを判別することができます。
ReactであればuseContext、ReduxであればStoreなどに預けてコードを入力させ、続きの処理ではselectなどを使って取り出して続行するという形が一般的かなと思います。
Appendix: MFAを有効化・無効化する
Amplify SDKの場合、どちらの操作も同じ処理で実装できます。
export type MFAType = 'SMS' | 'TOTP' | 'NOMFA';
export const updateMFA = async (type: MFAType) => {
const user = await Auth.currentAuthenticatedUser();
return Auth.setPreferredMFA(user, type);
};
NOMFA
を渡すと無効化されますので、updateMFA
をラップしたdisableMFA
などを用意してやるとより使いやすいでしょう。
export const disableMFA = () => updateMFA('NOMFA')
ちなみに、Challenge(ログイン時)は'SOFTWARE_TOKEN_MFA', 'SMS_MFA'
、設定時は'SMS', 'TOTP'
なので、Auth
からの戻り値を使ってうまく動かないと思った時は、この表記揺れに引っかかっていないかチェックしておきましょう。
const fetchMFAStatus = async () => {
const user = await Auth.currentAuthenticatedUser();
const result = await Auth.getPreferredMFA(user);
if (/^SMS/.test(result)) return 'SMS';
if (result === 'SOFTWARE_TOKEN_MFA') return 'TOTP';
return result;
};
自分の場合は、設定系では上のように雑に丸めてます。