@joinmeow/cognito-passwordless-auth
Version:
Passwordless authentication with Amazon Cognito: FIDO2 (WebAuthn, support for Passkeys)
87 lines (86 loc) • 3.83 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 { configure } from "./config.js";
import { initiateAuth, handleAuthResponse, isAuthenticatedResponse, } from "./cognito-api.js";
import { processTokens } from "./common.js";
import { retrieveDeviceKey } from "./storage.js";
import { createDeviceSrpAuthHandler } from "./device.js";
export function authenticateWithPlaintextPassword({ username, password, smsMfaCode, otpMfaCode, newPassword, deviceKey, tokensCb, statusCb, clientMetadata, }) {
const { userPoolId, debug } = configure();
if (!userPoolId) {
throw new Error("UserPoolId must be configured");
}
const abort = new AbortController();
const signedIn = (async () => {
try {
statusCb?.("SIGNING_IN_WITH_PASSWORD");
// We'll add device key later after we have username context
// Do initial authentication without device parameters
debug?.(`Invoking initiateAuth with username and password only...`);
const authParameters = {
USERNAME: username,
PASSWORD: password,
};
const authResponse = await initiateAuth({
authflow: "USER_PASSWORD_AUTH",
authParameters,
clientMetadata,
abort: abort.signal,
});
debug?.(`Response from initiateAuth:`, authResponse);
// Now that we have initiated authentication, we can look up device key
// using the confirmed username for this session
const actualDeviceKey = deviceKey ?? (username ? await retrieveDeviceKey(username) : undefined);
// Pre-create device SRP handler if we have a device key
const deviceHandler = actualDeviceKey
? await createDeviceSrpAuthHandler(username, actualDeviceKey)
: undefined;
const tokens = await handleAuthResponse({
authResponse,
username,
smsMfaCode,
otpMfaCode,
newPassword,
deviceHandler,
clientMetadata,
abort: abort.signal,
});
// Check for new device metadata in the response
if (isAuthenticatedResponse(authResponse) &&
authResponse.AuthenticationResult.NewDeviceMetadata) {
debug?.("Got new device metadata in authentication response. This can be used for device authentication in future requests.");
}
// Always process tokens first - this handles device confirmation, storage, and refresh scheduling
const processedTokens = (await processTokens({
...tokens,
authMethod: "PLAINTEXT",
}, abort.signal));
// Then call the custom tokensCb if provided (for application-specific needs only)
if (tokensCb) {
await tokensCb(processedTokens);
}
statusCb?.("SIGNED_IN_WITH_PLAINTEXT_PASSWORD");
return processedTokens;
}
catch (err) {
statusCb?.("PASSWORD_SIGNIN_FAILED");
throw err;
}
})();
return {
signedIn,
abort: () => abort.abort(),
};
}