UNPKG

@ideem/plugins.passkeys-plus

Version:

Extend your ZSM Client SDK with Passkeys Plus functionality, enabling advanced passkey management and integration, with optional, sync-proof device-binding and attestation, invisible second-factor authentication, and user identity verification!

103 lines (87 loc) 7.08 kB
import eventCoordinator from '@ideem/zsm-client-sdk/EventCoordinator.js'; import PasskeysPlusClient from './PasskeysPlusClient.js'; import WASMRustInterface from '@ideem/zsm-client-sdk/WASMRustInterface.js'; import WebAuthnClientBase from '@ideem/zsm-client-sdk/WebAuthnClientBase.js'; import { uid2iid, iid4uid } from '@ideem/zsm-client-sdk/IdentityIndexing.js'; class WebAuthnClient extends WebAuthnClientBase { /** * Constructs a WebAuthnClient object by extending the WebAuthnClientBase class. * @param {Object} config - The configuration for ZSM initialization. */ constructor(config) { super(config); eventCoordinator.update('WebAuthnClient'); this.passkeysPlus = new PasskeysPlusClient(); eventCoordinator.update('WebAuthnClient', 'READY'); } /** * @name getZsmApi * @description Retrieves the ZSM API instance. * @param {Object} config The configuration object for ZSM initialization. * @returns {Promise<ZSMAPI>} Resolves with the initialized ZSM API. * @overrides {function} getZsmApi (ref: WebAuthnClientBase) * @memberOf WebAuthnClient */ getZsmApi = async (config) => WASMRustInterface(config); /** * @name pkpCreate * @description Creates a Passkeys+ credential for the user, if the user is not already enrolled, then creates a ZSM credential. * @param {string} userIdentifier The identifier for the user. * @param {boolean} pkpOnly If true, only creates the Passkeys+ credential without creating a ZSM credential. * @returns {Promise<Object>} Call to WebAuthnClientBase's webauthnCreate method * @throws {Error} If the userIdentifier is not provided or is empty. * @throws {Error} If no challenge is received from the Relying Party Server. * @throws {Error} If the public key is not found in the Relying Party Challenge. * @throws {Error} If the Platform Authenticator Credential is not created successfully. * @throws {Error} If the Platform Authenticator response is invalid. * @extends WebAuthnClientBase Adds this function to base class. * @memberOf WebAuthnClient, PasskeysPlus */ pkpCreate = async (userIdentifier, pkpOnly=false) => { if(userIdentifier == null || userIdentifier === '') return new Error(`[PK+ WebAuthnClient] :: pkpCreate :: A userIdentifier String is required! Received: ${userIdentifier}.`); this.userIdentifier = userIdentifier; const relyingPartyChallenge = await this.relyingParty.pkpRegistrationStart(userIdentifier); if(!relyingPartyChallenge) return new Error(`[PK+ WebAuthnClient] :: pkpCreate :: No response from ZSM API's attempt to interface with Relying Party Server!`); const paChallengePublicKey = relyingPartyChallenge.publicKey; if(!paChallengePublicKey) return new Error(`[PK+ WebAuthnClient] :: pkpCreate :: No publicKey found in Relying Party Challenge!`); const paCredential = await this.passkeysPlus.pkpCreatePasskeyCredential(paChallengePublicKey); if (!paCredential) return new Error('[PK+ WebAuthnClient] :: pkpCreate :: Unable to call Platform Authenticator to create Passkey!'); const paRegistrationResult = await this.relyingParty.pkpRegistrationFinish(paCredential); if (!paRegistrationResult) return new Error('[PK+ WebAuthnClient] :: pkpCreate :: Platform Authenticator response was invalid!'); uid2iid('pk:' + userIdentifier, paCredential.id); return pkpOnly ? paCredential.id : this.webauthnCreate(userIdentifier); } /** * @name pkpAuthenticate * @description Authenticates a user using Passkeys+ credentials. * @param {string} userIdentifier The identifier for the user. * @returns {Promise<Object>} Call to WebAuthnClientBase's webauthnGet method * @throws {Error} If the userIdentifier is not provided or is empty. * @throws {Error} If no Platform Authenticator Credential ID is found for the user. * @throws {Error} If the Platform Authority Challenge is not retrieved successfully. * @throws {Error} If the Platform Authenticator Credential is not created successfully. * @throws {Error} If the Platform Authenticator response is invalid. * @extends WebAuthnClientBase Adds this function to base class. * @memberOf WebAuthnClient, PasskeysPlus */ pkpAuthenticate = async (userIdentifier) => { if(userIdentifier == null || userIdentifier === '') return new Error(`[PK+ WebAuthnClient] :: pkpCreate :: A userIdentifier String is required! Received: ${userIdentifier}.`); this.userIdentifier = userIdentifier; const storedCredID = await iid4uid('pk:' + userIdentifier); if (!storedCredID) return new Error(`[PK+ WebAuthnClient] :: pkpAuthenticate :: No Platform Authenticator Credential ID found for ${userIdentifier}!`); // Initialize Relying Party Challenge const platformAuthorityChallenge = await this.relyingParty.pkpAuthenticationStart(storedCredID); if(!platformAuthorityChallenge) return new Error(`[PK+ WebAuthnClient] :: pkpAuthenticate :: Unable to retrieve Platform Authority Challenge from Relying Party Server!`); // Perform Platform Authenticator create() const paCredential = await this.passkeysPlus.pkpGetPasskeyCredential(platformAuthorityChallenge); if (!paCredential) return new Error('[PK+ WebAuthnClient] :: pkpAuthenticate :: Unable to call Platform Authenticator to create Passkey!'); // Finalize ZSM Credential const paAuthenticationResult = await this.relyingParty.pkpAuthenticationFinish(paCredential); if (!paAuthenticationResult) return new Error('[PK+ WebAuthnClient] :: pkpAuthenticate :: Platform Authenticator response was invalid!'); return this.webauthnGet(userIdentifier); } } // Export the WebAuthnClient class export default WebAuthnClient;