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!

105 lines (93 loc) 7.47 kB
import eventCoordinator from '@ideem/zsm-client-sdk/EventCoordinator.js'; import FIDO2ClientBase from '@ideem/zsm-client-sdk/FIDO2ClientBase.js'; import { iid4uid } from '@ideem/zsm-client-sdk/IdentityIndexing.js'; class FIDO2Client extends FIDO2ClientBase { /** * Constructs a FIDO2Client object by extending the FIDO2ClientBase class. * @param {Object} config - The configuration for ZSM initialization (ref: ./zsm_app_config.json) */ constructor(config) { super(config); eventCoordinator.update('FIDO2Client', 'PENDING'); this.webauthnPasskeyCreate = this.webauthnPasskeyCreate.bind(this); this.webauthnPasskeyGet = this.webauthnPasskeyGet.bind(this); if(!(this.config?.use_passkeys??undefined) && window.location.hostname === 'localhost') console.warn('[PK+ FIDO2Client] :: use_passkeys is not enabled, but you are running on localhost. This may lead to unexpected behavior when directing requests to production environments.'); eventCoordinator.update('FIDO2Client', 'READY'); } /** * @name webauthnCreate * @description Creates a ZSM credential for a user on the device, or acts as a proxy for Passkeys+ enrollment when usePasskeys flag is set * @param {string} userIdentifier The identifier for the user * @param {boolean} usePasskeys Whether to use Passkeys+ enrollment method instead of base class method * @param {boolean} pkpOnly Whether to only use Passkeys+ enrollment method * @returns {Promise<Object>} The results of the call to FIDO2ClientBase's create or the webauthnPasskeyCreate methods * @overrides {function} webauthnCreate (ref: FIDO2ClientBase) * @memberOf FIDO2Client, PasskeysPlus */ webauthnCreate (userIdentifier, usePasskeys=false, pkpOnly=false) { return usePasskeys ? this.webauthnPasskeyCreate(userIdentifier, pkpOnly) : super.webauthnCreate(userIdentifier); } /** * @name webauthnPasskeyCreate * @description Creates a Passkeys+ credential for the user, then creates a ZSM credential * @param {string} userIdentifier The identifier for the user * @param {boolean} pkpOnly Whether to only use Passkeys+ enrollment method * @returns {Promise<Object>} The results of the call to WebAuthnClient's pkpCreate method or an Error * @throws {Error} If the userIdentifier is not provided or is empty * @throws {Error} If an error occurs during Passkeys+ creation * @extends FIDO2ClientBase Adds this function to base class * @memberOf FIDO2Client, PasskeysPlus */ async webauthnPasskeyCreate (userIdentifier, pkpOnly=false) { try { if(userIdentifier == null || userIdentifier === '') throw new Error(`[PK+ FIDO2Client] :: webauthnPasskeyCreate :: A userIdentifier String is required! Received: ${userIdentifier}.`); const pkpCreateResult = await this.zsmAPI.pkpCreate(userIdentifier, pkpOnly); if (pkpCreateResult instanceof Error) throw new Error(`[PK+ FIDO2Client] :: webauthnPasskeyCreate :: Error occurred during Passkeys+ creation! \nDetails:\n${pkpCreateResult.message}`); return pkpCreateResult; } catch (e) { return new Error(e.message || e || '[PK+ FIDO2Client] :: webauthnPasskeyCreate :: An error occurred during Passkeys+ creation.'); } } /** * @name webauthnGet * @description Authenticates a ZSM credential for a user from the device, or acts as a proxy for Passkeys+ authentication when usePasskeys flag is set * @param {string} userIdentifier The identifier for the user * @param {boolean} usePasskeys Whether to use Passkeys+ authentication method instead of base class method * @returns {Promise<Object>} The results of the call to FIDO2ClientBase's get or the webauthnPasskeyGet methods * @overrides {function} webauthnGet (ref: FIDO2ClientBase) * @memberOf FIDO2Client, PasskeysPlus */ webauthnGet (userIdentifier, usePasskeys=false) { return usePasskeys ? this.webauthnPasskeyGet(userIdentifier) : super.webauthnGet(userIdentifier); } /** * @name webauthnPasskeyGet * @description Authenticates a ZSM credential on the device for the specified user * @param {string} userIdentifier The identifier for the user * @returns {Promise<Object>} The results of the call to WebAuthnClient's pkpAuthenticate method or an Error * @throws {Error} If the userIdentifier is not provided or is empty * @throws {Error} If an error occurs during Passkeys+ authentication * @extends FIDO2ClientBase Adds this function to base class * @memberOf FIDO2Client, PasskeysPlus */ async webauthnPasskeyGet (userIdentifier) { try { if(userIdentifier == null || userIdentifier === '') throw new Error(`[PK+ FIDO2Client] :: webauthnPasskeyGet :: A userIdentifier String is required! Received: ${userIdentifier}.`); const storedZSMCred = await iid4uid(userIdentifier); const storedPKPCred = await iid4uid(`pk:${userIdentifier}`); const hasZSMCred = (storedZSMCred !== userIdentifier); const hasPKPCred = (storedPKPCred !== `pk:${userIdentifier}`); if(!hasPKPCred && !hasZSMCred) throw new Error(`[PK+ FIDO2Client] :: webauthnPasskeyGet :: No ZSM credentials found for userIdentifier: ${userIdentifier}. Please enroll the user first.`); if(!hasPKPCred && hasZSMCred) return new Error(`[PK+ FIDO2Client] :: webauthnPasskeyGet :: ZSM credentials WERE found for userIdentifier: ${userIdentifier}, but no Passkeys+ credential found on this device. webauthnCreate the user using Passkeys+ in addition to their ZSM credentials.`, {cause: 'PKP_UNREGISTERED'}); if(hasPKPCred === `pk:${userIdentifier}`) throw new Error(`[PK+ FIDO2Client] :: webauthnPasskeyGet :: No Passkeys+ credentials found for userIdentifier: ${userIdentifier}.`); const pkpAuthResult = await this.zsmAPI.pkpAuthenticate(userIdentifier); if (pkpAuthResult instanceof Error) throw new Error(`[PK+ FIDO2Client] :: webauthnPasskeyGet :: Error occurred during Passkeys+ authentication! \nDetails:\n${pkpAuthResult.message}`); return pkpAuthResult; } catch (e) { return new Error(e.message || e || '[PK+ FIDO2Client] :: webauthnPasskeyGet :: An error occurred during Passkeys+ authentication.'); } } } export {FIDO2Client};