bpt-pack-two
Version:
Study Passwordless authentication on aws project
105 lines (104 loc) • 4.19 kB
JavaScript
/**
* 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 { logger } from "./common.js";
export const handler = async (event) => {
logger.debug(JSON.stringify(event, null, 2));
if (!event.request.session.length) {
// The auth flow just started, send a custom challenge
logger.info("No session yet, starting one ...");
return customChallenge(event);
}
// We only accept custom challenges
if (event.request.session.find((attempt) => attempt.challengeName !== "CUSTOM_CHALLENGE")) {
return deny(event, "Expected CUSTOM_CHALLENGE");
}
const { signInMethod } = event.request.clientMetadata ?? {};
logger.info(`Requested signInMethod: ${signInMethod} (attempt: ${countAttempts(event)})`);
if (signInMethod === "MAGIC_LINK") {
return handleMagicLinkResponse(event);
}
else if (signInMethod === "SMS_OTP_STEPUP") {
return handleSmsOtpStepUpResponse(event);
}
else if (signInMethod === "FIDO2") {
return handleFido2Response(event);
}
return deny(event, `Unrecognized signInMethod: ${signInMethod}`);
};
function handleMagicLinkResponse(event) {
logger.info("Checking Magic Link Auth ...");
const { alreadyHaveMagicLink } = event.request.clientMetadata ?? {};
const lastResponse = event.request.session.slice(-1)[0];
if (lastResponse.challengeResult === true) {
return allow(event);
}
else if (alreadyHaveMagicLink !== "yes" && countAttempts(event) === 0) {
logger.info("No magic link yet, creating one");
return customChallenge(event);
}
return deny(event, "Failed to authenticate with Magic Link");
}
function handleSmsOtpStepUpResponse(event) {
logger.info("Checking SMS OTP Step Auth ...");
const lastResponse = event.request.session.slice(-1)[0];
const attemps = countAttempts(event);
if (lastResponse.challengeResult === true) {
return allow(event);
}
else if (attemps < 3) {
logger.info(`Not successfull yet. Attempt number ${attemps + 1} of max 3`);
return customChallenge(event);
}
return deny(event, "Failed to authenticate with SMS OTP Step-Up");
}
function handleFido2Response(event) {
logger.info("Checking Fido2 Auth ...");
const lastResponse = event.request.session.slice(-1)[0];
if (lastResponse.challengeResult === true) {
return allow(event);
}
else if (countAttempts(event, false) === 0) {
logger.info("No challenge yet, creating one ...");
return customChallenge(event);
}
return deny(event, "Failed to authenticate with FIDO2");
}
function deny(event, reason) {
logger.info("Failing authentication because:", reason);
event.response.issueTokens = false;
event.response.failAuthentication = true;
logger.debug(JSON.stringify(event, null, 2));
return event;
}
function allow(event) {
logger.info("Authentication successfull");
event.response.issueTokens = true;
event.response.failAuthentication = false;
logger.debug(JSON.stringify(event, null, 2));
return event;
}
function customChallenge(event) {
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = "CUSTOM_CHALLENGE";
logger.info("Next step: CUSTOM_CHALLENGE");
logger.debug(JSON.stringify(event, null, 2));
return event;
}
function countAttempts(event, excludeProvideAuthParameters = true) {
if (!excludeProvideAuthParameters)
return event.request.session.length;
return event.request.session.filter((entry) => entry.challengeMetadata !== "PROVIDE_AUTH_PARAMETERS").length;
}