UNPKG

@authaction/passkey-plus-sdk

Version:

A lightweight frontend SDK for passkey-based passwordless authentication with AuthAction

131 lines (109 loc) 3.47 kB
import { IPasskeyAuthenticateOptions, IPasskeyCreationOptions, PasskeyPlusOptions, } from "./types"; import { startRegistration, startAuthentication, } from "@simplewebauthn/browser"; export class PasskeyPlus { private baseUrl: string; constructor(options: PasskeyPlusOptions) { const { tenantDomain, appId } = options; this.baseUrl = `https://${tenantDomain}/api/v1/passkey-plus-public/${appId}`; } async register( transactionID: string, opts?: IPasskeyCreationOptions ): Promise<string> { const publicKey = await this.getRegistrationOptions(transactionID); if (opts?.authenticatorAttachment) { publicKey.authenticatorSelection = { ...publicKey.authenticatorSelection, authenticatorAttachment: opts.authenticatorAttachment, }; } const attestationResponse = await startRegistration({ optionsJSON: publicKey, }); const res = await fetch( `${this.baseUrl}/transaction/${transactionID}/register`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(attestationResponse), } ); const responseJson = await res.json(); const { nonce } = responseJson.data; return nonce; } async authenticate( transactionId: string, opts?: IPasskeyAuthenticateOptions ): Promise<string> { const publicKey = await this.getAuthenticationOptions(transactionId); const assertionResponse = await startAuthentication({ optionsJSON: publicKey, }); const res = await fetch( `${this.baseUrl}/transaction/${transactionId}/authenticate`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(assertionResponse), } ); if (!res.ok) { const error = await res.json(); throw new Error( `Authentication request failed: ${error.message || res.statusText}` ); } const responseJson = await res.json(); const { nonce } = responseJson.data; return nonce; } async canAuthenticateWithPasskey(): Promise<boolean> { return !!window.PublicKeyCredential; } async canRegisterPasskey(): Promise<boolean> { return ( !!window.PublicKeyCredential && typeof navigator.credentials.create === "function" ); } async canUseConditionalMediation(): Promise<boolean> { return typeof window?.PublicKeyCredential ?.isConditionalMediationAvailable === "function" ? await window.PublicKeyCredential.isConditionalMediationAvailable() : false; } private async getRegistrationOptions(transactionID: string) { const res = await fetch( `${this.baseUrl}/transaction/${transactionID}/registration-options` ); if (!res.ok) { const error = await res.json(); throw new Error( `Registration options request failed: ${error.message || res.statusText}` ); } const responseJson = await res.json(); return responseJson.data; } private async getAuthenticationOptions(transactionID: string) { const res = await fetch( `${this.baseUrl}/transaction/${transactionID}/authentication-options` ); if (!res.ok) { const error = await res.json(); throw new Error( `Authentication options request failed: ${error.message || res.statusText}` ); } const responseJson = await res.json(); return responseJson.data; } }