| |
| |
| |
|
|
| export function b64urlToBuf(s: string): ArrayBuffer { |
| const pad = '='.repeat((4 - (s.length % 4)) % 4) |
| const b64 = (s + pad).replace(/-/g, '+').replace(/_/g, '/') |
| const bin = atob(b64) |
| const bytes = new Uint8Array(bin.length) |
| for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i) |
| return bytes.buffer |
| } |
|
|
| export function bufToB64url(buf: ArrayBuffer): string { |
| const bytes = new Uint8Array(buf) |
| let s = '' |
| for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]) |
| return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') |
| } |
|
|
| export function isWebAuthnSupported(): boolean { |
| return typeof window !== 'undefined' && !!window.PublicKeyCredential |
| } |
|
|
| export async function platformAuthenticatorAvailable(): Promise<boolean> { |
| try { |
| return ( |
| isWebAuthnSupported() && |
| (await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()) |
| ) |
| } catch { |
| return false |
| } |
| } |
|
|
| |
| |
| export function toCreationOptions(opts: any): CredentialCreationOptions { |
| const publicKey = { ...opts } |
| publicKey.challenge = b64urlToBuf(opts.challenge) |
| publicKey.user = { ...opts.user, id: b64urlToBuf(opts.user.id) } |
| if (Array.isArray(opts.excludeCredentials)) { |
| publicKey.excludeCredentials = opts.excludeCredentials.map( |
| |
| (c: any) => ({ ...c, id: b64urlToBuf(c.id) }), |
| ) |
| } |
| return { publicKey } |
| } |
|
|
| |
| export function toRequestOptions(opts: any): CredentialRequestOptions { |
| const publicKey = { ...opts } |
| publicKey.challenge = b64urlToBuf(opts.challenge) |
| if (Array.isArray(opts.allowCredentials)) { |
| publicKey.allowCredentials = opts.allowCredentials.map( |
| |
| (c: any) => ({ ...c, id: b64urlToBuf(c.id) }), |
| ) |
| } |
| return { publicKey } |
| } |
|
|
| |
| export function serializeRegistration(cred: PublicKeyCredential): any { |
| const r = cred.response as AuthenticatorAttestationResponse |
| return { |
| id: cred.id, |
| rawId: bufToB64url(cred.rawId), |
| type: cred.type, |
| response: { |
| attestationObject: bufToB64url(r.attestationObject), |
| clientDataJSON: bufToB64url(r.clientDataJSON), |
| }, |
| clientExtensionResults: cred.getClientExtensionResults?.() ?? {}, |
| } |
| } |
|
|
| |
| export function serializeAssertion(cred: PublicKeyCredential): any { |
| const r = cred.response as AuthenticatorAssertionResponse |
| return { |
| id: cred.id, |
| rawId: bufToB64url(cred.rawId), |
| type: cred.type, |
| response: { |
| authenticatorData: bufToB64url(r.authenticatorData), |
| clientDataJSON: bufToB64url(r.clientDataJSON), |
| signature: bufToB64url(r.signature), |
| userHandle: r.userHandle ? bufToB64url(r.userHandle) : null, |
| }, |
| } |
| } |
|
|
| export class PasskeyCancelled extends Error {} |
| export class PasskeyUnsupported extends Error {} |
|
|