import { isoCBOR } from './iso/index.ts'; /** * Convert authenticator extension data buffer to a proper object * * @param extensionData Authenticator Extension Data buffer */ export function decodeAuthenticatorExtensions( extensionData: Uint8Array, ): AuthenticationExtensionsAuthenticatorOutputs | undefined { let toCBOR: Map; try { toCBOR = isoCBOR.decodeFirst(extensionData); } catch (err) { const _err = err as Error; throw new Error(`Error decoding authenticator extensions: ${_err.message}`); } return convertMapToObjectDeep(toCBOR); } export type AuthenticationExtensionsAuthenticatorOutputs = { devicePubKey?: DevicePublicKeyAuthenticatorOutput; uvm?: UVMAuthenticatorOutput; }; export type DevicePublicKeyAuthenticatorOutput = { dpk?: Uint8Array; sig?: string; nonce?: Uint8Array; scope?: Uint8Array; aaguid?: Uint8Array; }; // TODO: Need to verify this format // https://w3c.github.io/webauthn/#sctn-uvm-extension. export type UVMAuthenticatorOutput = { uvm?: Uint8Array[]; }; /** * CBOR-encoded extensions can be deeply-nested Maps, which are too deep for a simple * `Object.entries()`. This method will recursively make sure that all Maps are converted into * basic objects. */ function convertMapToObjectDeep( input: Map, ): { [key: string]: unknown } { const mapped: { [key: string]: unknown } = {}; for (const [key, value] of input) { if (value instanceof Map) { mapped[key] = convertMapToObjectDeep(value); } else { mapped[key] = value; } } return mapped; }