ここらへんを参考にしてワンタイムパスワードを実装しようとしたができなかったのでメモっておく。
ソースは末尾に。
出力結果
まずは admin-create-user
-> admin-set-user-password
-> admin-initiate-auth
の順で実行。
$ aws cognito-idp admin-initiate-auth --user-pool-id ap-northeast-1_xxxxxxxxxx --auth-flow CUSTOM_AUTH --client-id yyyyyyyyyy --auth-parameters USERNAME=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee An error occurred (InvalidLambdaResponseException) when calling the AdminInitiateAuth operation: Invalid lambda function output : Invalid JSON
adminだからダメなのかなと思って、 admin-create-user
-> admin-set-user-password
-> initiate-auth
の順で実行。
$ aws cognito-idp initiate-auth --auth-flow CUSTOM_AUTH --client-id yyyyyyyyyy --auth-parameters USERNAME=test@example.com An error occurred (InvalidLambdaResponseException) when calling the InitiateAuth operation: Invalid lambda function output : Invalid JSON
エラーの内容が同じだったので根本的に何かが違う気がするがわからん。
VerifyAuthChallengeResponse でこけてたけど、データをログに出しても大丈夫な感じはするんだけど、うーーーん。
{ version: '1', region: 'ap-northeast-1', userPoolId: 'ap-northeast-1_xxxxxxxx', userName: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', callerContext: { awsSdkVersion: 'aws-sdk-unknown-unknown', clientId: 'yyyyyyyyyy' }, triggerSource: 'DefineAuthChallenge_Authentication', request: { userAttributes: { sub: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', 'cognito:user_status': 'CONFIRMED', email: 'test@example.com' }, session: [] }, response: { challengeName: 'CUSTOM_CHALLENGE', issueTokens: false, failAuthentication: false } }
ソース
serverless frameworkです。ほぼコピペ。
service: test frameworkVersion: '2' provider: name: aws runtime: nodejs12.x lambdaHashingVersion: 20201221 stage: dev region: ap-northeast-1 functions: define: handler: handler.define create: handler: handler.create verify: handler: handler.verify resources: Resources: UserPool: Type: AWS::Cognito::UserPool Properties: UserPoolName: test UsernameAttributes: [email] LambdaConfig: CreateAuthChallenge: !GetAtt CreateLambdaFunction.Arn DefineAuthChallenge: !GetAtt DefineLambdaFunction.Arn VerifyAuthChallengeResponse: !GetAtt VerifyLambdaFunction.Arn UserPoolClient: Type: AWS::Cognito::UserPoolClient Properties: UserPoolId: !Ref UserPool ClientName: test GenerateSecret: false CreatePermission: Type: 'AWS::Lambda::Permission' Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt CreateLambdaFunction.Arn Principal: "cognito-idp.amazonaws.com" SourceArn: !Join [ "", [ "arn:aws:cognito-idp", ":", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":", "userpool/", !Ref UserPool ] ] DefinePermission: Type: 'AWS::Lambda::Permission' Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt DefineLambdaFunction.Arn Principal: "cognito-idp.amazonaws.com" SourceArn: !Join [ "", [ "arn:aws:cognito-idp", ":", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":", "userpool/", !Ref UserPool ] ] VerifyPermission: Type: 'AWS::Lambda::Permission' Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt VerifyLambdaFunction.Arn Principal: "cognito-idp.amazonaws.com" SourceArn: !Join [ "", [ "arn:aws:cognito-idp", ":", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":", "userpool/", !Ref UserPool ] ]
const { randomDigits } = require('crypto-secure-random-digit'); module.exports.verify = (event) => { const expectedAnswer = event.request.privateChallengeParameters.secretLoginCode; if (event.request.challengeAnswer === expectedAnswer) { event.response.answerCorrect = true; } else { event.response.answerCorrect = false; } return event; }; module.exports.create = (event, context, callback) => { let secretLoginCode = null; if (!event.request.session || !event.request.session.length) { secretLoginCode = randomDigits(6).join(''); console.log(secretLoginCode); } else { const previousChallenge = event.request.session.slice(-1)[0]; secretLoginCode = previousChallenge.challengeMetadata.match(/CODE-(\d*)/)[1]; } event.response.publicChallengeParameters = { email: event.request.userAttributes.email }; event.response.privateChallengeParameters = { secretLoginCode }; event.response.challengeMetadata = `CODE-${secretLoginCode}`; return event; }; module.exports.define = (event, context, callback) => { if (event.request.session && event.request.session.length >= 3 && event.request.session.slice(-1)[0].challengeResult === false) { // ユーザの入力コードが3回間違っていた場合(認証失敗) event.response.issueTokens = false; event.response.failAuthentication = true; } else if (event.request.session && event.request.session.length && event.request.session.slice(-1)[0].challengeResult === true) { // ユーザの入力コードが正しい場合(認証成功) event.response.issueTokens = true; event.response.failAuthentication = false; } else { // それ以外: ユーザの入力コードが正しくなく、3回間違えてない場合 (認証チャレンジ継続) event.response.issueTokens = false; event.response.failAuthentication = false; event.response.challengeName = 'CUSTOM_CHALLENGE'; } console.log(event); return event; };