UNPKG

@joinmeow/cognito-passwordless-auth

Version:

Passwordless authentication with Amazon Cognito: FIDO2 (WebAuthn, support for Passkeys)

145 lines (144 loc) 5.74 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"; // Helper function to safely check if we're in a browser environment const isBrowser = () => typeof globalThis !== "undefined" && !!globalThis.document; // Safe access to global browser objects const getWindow = () => isBrowser() ? globalThis : undefined; const getDocument = () => isBrowser() ? globalThis.document : undefined; /** * Manages the Amazon Cognito Advanced Security data collection */ export class CognitoSecurityProvider { // Private constructor for singleton constructor() { this.initialized = false; this.scriptLoaded = false; this.scriptLoading = false; } /** * Get the singleton instance */ static getInstance() { if (!CognitoSecurityProvider.instance) { CognitoSecurityProvider.instance = new CognitoSecurityProvider(); } return CognitoSecurityProvider.instance; } /** * Initialize the security provider */ async initialize() { if (this.initialized || this.scriptLoading) { return; } const { debug } = configure(); const win = getWindow(); if (!win) { debug?.("CognitoSecurityProvider: Window not available, skipping initialization"); return; } // Check if script is already loaded if (typeof win.AmazonCognitoAdvancedSecurityData !== "undefined") { this.scriptLoaded = true; this.initialized = true; debug?.("CognitoSecurityProvider: Amazon Cognito Advanced Security already available"); return; } const { cognitoIdpEndpoint } = configure(); // Extract region from endpoint or use default region const regionMatch = cognitoIdpEndpoint.match(/^[a-z]{2}-[a-z]+-\d$/); const region = regionMatch ? cognitoIdpEndpoint : "us-east-1"; try { this.scriptLoading = true; debug?.(`CognitoSecurityProvider: Loading security script for region ${region}`); const doc = getDocument(); if (!doc) { throw new Error("Document not available"); } // Create script element const script = doc.createElement("script"); script.src = `https://amazon-cognito-assets.${region}.amazoncognito.com/amazon-cognito-advanced-security-data.min.js`; script.async = true; // Create promise to track script loading const scriptLoadPromise = new Promise((resolve, reject) => { script.onload = () => { this.scriptLoaded = true; this.initialized = true; debug?.("CognitoSecurityProvider: Security script loaded successfully"); resolve(); }; script.onerror = () => { const error = new Error(`Failed to load Amazon Cognito Advanced Security script for region ${region}`); debug?.("CognitoSecurityProvider:", error); reject(error); }; }); // Append script to document doc.head.appendChild(script); // Wait for script to load with timeout const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject(new Error("Security script loading timed out after 5000ms")); }, 5000); }); await Promise.race([scriptLoadPromise, timeoutPromise]); } catch (error) { debug?.("CognitoSecurityProvider: Error initializing security script:", error); } finally { this.scriptLoading = false; } } /** * Get the encoded security data for the given user */ getEncodedData(username) { const { clientId, userPoolId, debug } = configure(); const win = getWindow(); if (!userPoolId) { debug?.("CognitoSecurityProvider: Missing userPoolId, cannot generate security data"); return undefined; } if (!this.scriptLoaded || !win || !win.AmazonCognitoAdvancedSecurityData) { debug?.("CognitoSecurityProvider: Security script not loaded, cannot generate security data"); return undefined; } try { return win.AmazonCognitoAdvancedSecurityData.getData(username, userPoolId, clientId); } catch (error) { debug?.("CognitoSecurityProvider: Error generating security data:", error); return undefined; } } /** * Ensures the security provider is initialized and returns encoded data */ async getSecurityData(username) { await this.initialize(); return this.getEncodedData(username); } } // Immediately initialize the provider in browser environments if (isBrowser()) { // Don't await - let it initialize in the background CognitoSecurityProvider.getInstance() .initialize() .catch(() => { // Errors are already logged in the provider }); }