UNPKG

bpt-pack-two

Version:

Study Passwordless authentication on aws project

124 lines (123 loc) 5.38 kB
/** * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). You * may not use this file except in compliance with the License. A copy of * the License is located at * * http://aws.amazon.com/apache2.0/ * * or in the "license" file accompanying this file. This file is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF * ANY KIND, either express or implied. See the License for the specific * language governing permissions and limitations under the License. */ import { configure } from "./config.js"; import { busyState } from "./model.js"; import { assertIsChallengeResponse, assertIsAuthenticatedResponse, isChallengeResponse, initiateAuth, respondToAuthChallenge, } from "./cognito-api.js"; import { defaultTokensCb } from "./common.js"; import { parseJwtPayload } from "./util.js"; import { retrieveTokens } from "./storage.js"; export function stepUpAuthenticationWithSmsOtp({ username, smsMfaCode, tokensCb, statusCb, currentStatus, clientMetadata, }) { if (currentStatus && busyState.includes(currentStatus)) { throw new Error(`Can't sign in while in status ${currentStatus}`); } const abort = new AbortController(); const signedIn = (async () => { const { debug } = configure(); statusCb?.("SIGNING_IN_WITH_OTP"); try { const { accessToken } = (await retrieveTokens()) ?? {}; if (!accessToken) { throw new Error("Missing access token. You must be signed-in already for step-up auth"); } let session; debug?.(`Invoking initiateAuth ...`); const initAuthResponse = await initiateAuth({ authflow: "CUSTOM_AUTH", authParameters: { USERNAME: username, }, abort: abort.signal, }); debug?.(`Response from initiateAuth:`, initAuthResponse); assertIsChallengeResponse(initAuthResponse); session = initAuthResponse.Session; let phoneNumberWithOtp; let authResult; if (initAuthResponse.ChallengeParameters.challenge === "PROVIDE_AUTH_PARAMETERS") { debug?.(`Invoking respondToAuthChallenge ...`); authResult = await respondToAuthChallenge({ challengeName: "CUSTOM_CHALLENGE", challengeResponses: { ANSWER: "__dummy__", USERNAME: username, }, clientMetadata: { ...clientMetadata, signInMethod: "SMS_OTP_STEPUP", }, session: session, abort: abort.signal, }); assertIsChallengeResponse(authResult); debug?.(`Response from respondToAuthChallenge:`, authResult); session = authResult.Session; phoneNumberWithOtp = authResult.ChallengeParameters.phoneNumber; } else { phoneNumberWithOtp = initAuthResponse.ChallengeParameters.phoneNumber; } let attempt = 1; for (;;) { const secretCode = await smsMfaCode(phoneNumberWithOtp, attempt); debug?.(`Invoking respondToAuthChallenge ...`); authResult = await respondToAuthChallenge({ challengeName: "CUSTOM_CHALLENGE", challengeResponses: { ANSWER: JSON.stringify({ jwt: accessToken, secretCode, }), USERNAME: username, }, clientMetadata: { ...clientMetadata, signInMethod: "SMS_OTP_STEPUP", }, session: session, abort: abort.signal, }); debug?.(`Response from respondToAuthChallenge:`, authResult); if (!isChallengeResponse(authResult)) { break; } session = authResult.Session; attempt++; } assertIsAuthenticatedResponse(authResult); debug?.(`Response from respondToAuthChallenge:`, authResult); const tokens = { accessToken: authResult.AuthenticationResult.AccessToken, idToken: authResult.AuthenticationResult.IdToken, refreshToken: authResult.AuthenticationResult.RefreshToken, expireAt: new Date(Date.now() + authResult.AuthenticationResult.ExpiresIn * 1000), username: parseJwtPayload(authResult.AuthenticationResult.IdToken)["cognito:username"], }; tokensCb ? await tokensCb(tokens) : await defaultTokensCb({ tokens, abort: abort.signal }); statusCb?.("SIGNED_IN_WITH_OTP"); return tokens; } catch (err) { statusCb?.("SIGNIN_WITH_OTP_FAILED"); throw err; } })(); return { signedIn, abort: () => abort.abort(), }; }