@metamask-previews/passkey-controller 3.0.0-preview-5ae42f53d → 3.0.0-preview-a3ea429
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PasskeyController-method-action-types.cjs.map +1 -1
- package/dist/PasskeyController-method-action-types.d.cts.map +1 -1
- package/dist/PasskeyController-method-action-types.d.mts.map +1 -1
- package/dist/PasskeyController-method-action-types.mjs.map +1 -1
- package/dist/PasskeyController.cjs +67 -67
- package/dist/PasskeyController.cjs.map +1 -1
- package/dist/PasskeyController.d.cts.map +1 -1
- package/dist/PasskeyController.d.mts.map +1 -1
- package/dist/PasskeyController.mjs.map +1 -1
- package/dist/ceremony-manager.cjs.map +1 -1
- package/dist/ceremony-manager.d.cts.map +1 -1
- package/dist/ceremony-manager.d.mts.map +1 -1
- package/dist/ceremony-manager.mjs.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts.map +1 -1
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs.map +1 -1
- package/dist/index.cjs +9 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/key-derivation.cjs +9 -9
- package/dist/key-derivation.cjs.map +1 -1
- package/dist/key-derivation.d.cts.map +1 -1
- package/dist/key-derivation.d.mts.map +1 -1
- package/dist/key-derivation.mjs.map +1 -1
- package/dist/logger.cjs +2 -2
- package/dist/logger.cjs.map +1 -1
- package/dist/logger.mjs.map +1 -1
- package/dist/types.cjs +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/utils/crypto.cjs +2 -2
- package/dist/utils/crypto.cjs.map +1 -1
- package/dist/utils/crypto.mjs.map +1 -1
- package/dist/webauthn/decode-attestation-object.cjs.map +1 -1
- package/dist/webauthn/decode-attestation-object.d.cts.map +1 -1
- package/dist/webauthn/decode-attestation-object.d.mts.map +1 -1
- package/dist/webauthn/decode-attestation-object.mjs.map +1 -1
- package/dist/webauthn/decode-client-data-json.cjs +2 -2
- package/dist/webauthn/decode-client-data-json.cjs.map +1 -1
- package/dist/webauthn/decode-client-data-json.d.cts.map +1 -1
- package/dist/webauthn/decode-client-data-json.d.mts.map +1 -1
- package/dist/webauthn/decode-client-data-json.mjs.map +1 -1
- package/dist/webauthn/match-expected-rp-id.cjs +2 -2
- package/dist/webauthn/match-expected-rp-id.cjs.map +1 -1
- package/dist/webauthn/match-expected-rp-id.mjs.map +1 -1
- package/dist/webauthn/parse-authenticator-data.cjs.map +1 -1
- package/dist/webauthn/parse-authenticator-data.d.cts.map +1 -1
- package/dist/webauthn/parse-authenticator-data.d.mts.map +1 -1
- package/dist/webauthn/parse-authenticator-data.mjs.map +1 -1
- package/dist/webauthn/types.cjs.map +1 -1
- package/dist/webauthn/types.d.cts.map +1 -1
- package/dist/webauthn/types.d.mts.map +1 -1
- package/dist/webauthn/types.mjs.map +1 -1
- package/dist/webauthn/verify-authentication-response.cjs +12 -12
- package/dist/webauthn/verify-authentication-response.cjs.map +1 -1
- package/dist/webauthn/verify-authentication-response.d.cts.map +1 -1
- package/dist/webauthn/verify-authentication-response.d.mts.map +1 -1
- package/dist/webauthn/verify-authentication-response.mjs.map +1 -1
- package/dist/webauthn/verify-registration-response.cjs +19 -19
- package/dist/webauthn/verify-registration-response.cjs.map +1 -1
- package/dist/webauthn/verify-registration-response.d.cts.map +1 -1
- package/dist/webauthn/verify-registration-response.d.mts.map +1 -1
- package/dist/webauthn/verify-registration-response.mjs.map +1 -1
- package/dist/webauthn/verify-signature.cjs +30 -30
- package/dist/webauthn/verify-signature.cjs.map +1 -1
- package/dist/webauthn/verify-signature.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match-expected-rp-id.cjs","sourceRoot":"","sources":["../../src/webauthn/match-expected-rp-id.ts"],"names":[],"mappings":";;;AAAA,2CAAsD;AACtD,6CAA4C;AAE5C,
|
|
1
|
+
{"version":3,"file":"match-expected-rp-id.cjs","sourceRoot":"","sources":["../../src/webauthn/match-expected-rp-id.ts"],"names":[],"mappings":";;;AAAA,2CAAsD;AACtD,6CAA4C;AAE5C,uDAAkD;AAElD;;;;;;;;GAQG;AACH,SAAgB,iBAAiB,CAC/B,QAAoB,EACpB,aAAuB;IAEvB,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,YAAY,GAAG,IAAA,aAAM,EAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5D,IAAI,IAAA,2BAAmB,EAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,CAAC;YAChD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAA,wBAAU,EAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC7E,CAAC;AAXD,8CAWC","sourcesContent":["import { areUint8ArraysEqual } from '@metamask/utils';\nimport { sha256 } from '@noble/hashes/sha2';\n\nimport { bytesToHex } from '../utils/encoding.js';\n\n/**\n * Verify that an authenticator data rpIdHash matches one of the expected\n * RP IDs by SHA-256 hashing each candidate and comparing.\n *\n * @param rpIdHash - The rpIdHash from authenticatorData (32 bytes).\n * @param expectedRPIDs - One or more RP ID strings to check against.\n * @returns The matching RP ID string.\n * @throws If no expected RP ID matches.\n */\nexport function matchExpectedRPID(\n rpIdHash: Uint8Array,\n expectedRPIDs: string[],\n): string {\n for (const rpID of expectedRPIDs) {\n const expectedHash = sha256(new TextEncoder().encode(rpID));\n if (areUint8ArraysEqual(rpIdHash, expectedHash)) {\n return rpID;\n }\n }\n throw new Error(`Unexpected RP ID hash: received ${bytesToHex(rpIdHash)}`);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match-expected-rp-id.mjs","sourceRoot":"","sources":["../../src/webauthn/match-expected-rp-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,wBAAwB;AACtD,OAAO,EAAE,MAAM,EAAE,2BAA2B;AAE5C,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"match-expected-rp-id.mjs","sourceRoot":"","sources":["../../src/webauthn/match-expected-rp-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,wBAAwB;AACtD,OAAO,EAAE,MAAM,EAAE,2BAA2B;AAE5C,OAAO,EAAE,UAAU,EAAE,8BAA6B;AAElD;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAoB,EACpB,aAAuB;IAEvB,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5D,IAAI,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,CAAC;YAChD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,mCAAmC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["import { areUint8ArraysEqual } from '@metamask/utils';\nimport { sha256 } from '@noble/hashes/sha2';\n\nimport { bytesToHex } from '../utils/encoding.js';\n\n/**\n * Verify that an authenticator data rpIdHash matches one of the expected\n * RP IDs by SHA-256 hashing each candidate and comparing.\n *\n * @param rpIdHash - The rpIdHash from authenticatorData (32 bytes).\n * @param expectedRPIDs - One or more RP ID strings to check against.\n * @returns The matching RP ID string.\n * @throws If no expected RP ID matches.\n */\nexport function matchExpectedRPID(\n rpIdHash: Uint8Array,\n expectedRPIDs: string[],\n): string {\n for (const rpID of expectedRPIDs) {\n const expectedHash = sha256(new TextEncoder().encode(rpID));\n if (areUint8ArraysEqual(rpIdHash, expectedHash)) {\n return rpID;\n }\n }\n throw new Error(`Unexpected RP ID hash: received ${bytesToHex(rpIdHash)}`);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-authenticator-data.cjs","sourceRoot":"","sources":["../../src/webauthn/parse-authenticator-data.ts"],"names":[],"mappings":";;;AAAA,qDAA0D;
|
|
1
|
+
{"version":3,"file":"parse-authenticator-data.cjs","sourceRoot":"","sources":["../../src/webauthn/parse-authenticator-data.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodePartialCBOR } from '@levischuck/tiny-cbor';\n\nimport type {\n ParsedAuthenticatorData,\n AuthenticatorDataFlags,\n} from './types.js';\n\n/* eslint-disable no-bitwise */\n\n/**\n * Parse an authenticator data buffer per §6.1 of the WebAuthn spec.\n *\n * @param authData - Raw authenticator data bytes.\n * @returns Parsed authenticator data with flags, rpIdHash, counter, and\n * optional attested credential data.\n */\nexport function parseAuthenticatorData(\n authData: Uint8Array,\n): ParsedAuthenticatorData {\n if (authData.byteLength < 37) {\n throw new Error(\n `authenticatorData is ${authData.byteLength} bytes, expected at least 37`,\n );\n }\n\n let pointer = 0;\n\n const rpIdHash = authData.slice(pointer, pointer + 32);\n pointer += 32;\n\n const flagsByte = authData[pointer];\n const flags: AuthenticatorDataFlags = {\n up: Boolean(flagsByte & (1 << 0)),\n uv: Boolean(flagsByte & (1 << 2)),\n be: Boolean(flagsByte & (1 << 3)),\n bs: Boolean(flagsByte & (1 << 4)),\n at: Boolean(flagsByte & (1 << 6)),\n ed: Boolean(flagsByte & (1 << 7)),\n flagsByte,\n };\n pointer += 1;\n\n const counterView = new DataView(\n authData.buffer,\n authData.byteOffset + pointer,\n 4,\n );\n const counter = counterView.getUint32(0, false);\n pointer += 4;\n\n const result: ParsedAuthenticatorData = {\n rpIdHash,\n flags,\n counter,\n };\n\n if (flags.at) {\n const aaguid = authData.slice(pointer, pointer + 16);\n pointer += 16;\n\n const credIDLenView = new DataView(\n authData.buffer,\n authData.byteOffset + pointer,\n 2,\n );\n const credIDLen = credIDLenView.getUint16(0, false);\n pointer += 2;\n\n const credentialID = authData.slice(pointer, pointer + credIDLen);\n pointer += credIDLen;\n\n const pubKeyBytes = authData.slice(pointer);\n const [, nextOffset] = decodePartialCBOR(\n new Uint8Array(pubKeyBytes),\n 0,\n ) as [unknown, number];\n const credentialPublicKey = authData.slice(pointer, pointer + nextOffset);\n pointer += nextOffset;\n\n result.aaguid = aaguid;\n result.credentialID = credentialID;\n result.credentialPublicKey = credentialPublicKey;\n }\n\n if (flags.ed) {\n const remaining = authData.slice(pointer);\n const [decoded, consumed] = decodePartialCBOR(\n new Uint8Array(remaining),\n 0,\n ) as [Map<string, unknown>, number];\n result.extensionsData = decoded;\n result.extensionsDataBuffer = remaining.slice(0, consumed);\n pointer += consumed;\n }\n\n if (authData.byteLength > pointer) {\n throw new Error('Leftover bytes detected while parsing authenticator data');\n }\n\n return result;\n}\n\n/* eslint-enable no-bitwise */\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-authenticator-data.d.cts","sourceRoot":"","sources":["../../src/webauthn/parse-authenticator-data.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"parse-authenticator-data.d.cts","sourceRoot":"","sources":["../../src/webauthn/parse-authenticator-data.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,uBAAuB,EAExB,oBAAmB;AAIpB;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,UAAU,GACnB,uBAAuB,CAkFzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-authenticator-data.d.mts","sourceRoot":"","sources":["../../src/webauthn/parse-authenticator-data.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"parse-authenticator-data.d.mts","sourceRoot":"","sources":["../../src/webauthn/parse-authenticator-data.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,uBAAuB,EAExB,oBAAmB;AAIpB;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,UAAU,GACnB,uBAAuB,CAkFzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-authenticator-data.mjs","sourceRoot":"","sources":["../../src/webauthn/parse-authenticator-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,8BAA8B;
|
|
1
|
+
{"version":3,"file":"parse-authenticator-data.mjs","sourceRoot":"","sources":["../../src/webauthn/parse-authenticator-data.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodePartialCBOR } from '@levischuck/tiny-cbor';\n\nimport type {\n ParsedAuthenticatorData,\n AuthenticatorDataFlags,\n} from './types.js';\n\n/* eslint-disable no-bitwise */\n\n/**\n * Parse an authenticator data buffer per §6.1 of the WebAuthn spec.\n *\n * @param authData - Raw authenticator data bytes.\n * @returns Parsed authenticator data with flags, rpIdHash, counter, and\n * optional attested credential data.\n */\nexport function parseAuthenticatorData(\n authData: Uint8Array,\n): ParsedAuthenticatorData {\n if (authData.byteLength < 37) {\n throw new Error(\n `authenticatorData is ${authData.byteLength} bytes, expected at least 37`,\n );\n }\n\n let pointer = 0;\n\n const rpIdHash = authData.slice(pointer, pointer + 32);\n pointer += 32;\n\n const flagsByte = authData[pointer];\n const flags: AuthenticatorDataFlags = {\n up: Boolean(flagsByte & (1 << 0)),\n uv: Boolean(flagsByte & (1 << 2)),\n be: Boolean(flagsByte & (1 << 3)),\n bs: Boolean(flagsByte & (1 << 4)),\n at: Boolean(flagsByte & (1 << 6)),\n ed: Boolean(flagsByte & (1 << 7)),\n flagsByte,\n };\n pointer += 1;\n\n const counterView = new DataView(\n authData.buffer,\n authData.byteOffset + pointer,\n 4,\n );\n const counter = counterView.getUint32(0, false);\n pointer += 4;\n\n const result: ParsedAuthenticatorData = {\n rpIdHash,\n flags,\n counter,\n };\n\n if (flags.at) {\n const aaguid = authData.slice(pointer, pointer + 16);\n pointer += 16;\n\n const credIDLenView = new DataView(\n authData.buffer,\n authData.byteOffset + pointer,\n 2,\n );\n const credIDLen = credIDLenView.getUint16(0, false);\n pointer += 2;\n\n const credentialID = authData.slice(pointer, pointer + credIDLen);\n pointer += credIDLen;\n\n const pubKeyBytes = authData.slice(pointer);\n const [, nextOffset] = decodePartialCBOR(\n new Uint8Array(pubKeyBytes),\n 0,\n ) as [unknown, number];\n const credentialPublicKey = authData.slice(pointer, pointer + nextOffset);\n pointer += nextOffset;\n\n result.aaguid = aaguid;\n result.credentialID = credentialID;\n result.credentialPublicKey = credentialPublicKey;\n }\n\n if (flags.ed) {\n const remaining = authData.slice(pointer);\n const [decoded, consumed] = decodePartialCBOR(\n new Uint8Array(remaining),\n 0,\n ) as [Map<string, unknown>, number];\n result.extensionsData = decoded;\n result.extensionsDataBuffer = remaining.slice(0, consumed);\n pointer += consumed;\n }\n\n if (authData.byteLength > pointer) {\n throw new Error('Leftover bytes detected while parsing authenticator data');\n }\n\n return result;\n}\n\n/* eslint-enable no-bitwise */\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.cjs","sourceRoot":"","sources":["../../src/webauthn/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n AuthenticatorTransportFuture,\n Base64URLString as Base64URL,\n} from '../types';\n\nexport type PublicKeyCredentialDescriptorJSON = {\n id: Base64URL;\n type: 'public-key';\n transports?: AuthenticatorTransportFuture[];\n};\n\nexport type PublicKeyCredentialHint =\n | 'hybrid'\n | 'security-key'\n | 'client-device';\n\nexport type PasskeyRegistrationOptions = {\n rp: { name: string; id?: string };\n user: {\n id: Base64URL;\n name: string;\n displayName: string;\n };\n challenge: Base64URL;\n pubKeyCredParams: { alg: number; type: 'public-key' }[];\n timeout?: number;\n excludeCredentials?: PublicKeyCredentialDescriptorJSON[];\n authenticatorSelection?: {\n authenticatorAttachment?: 'cross-platform' | 'platform';\n residentKey?: 'discouraged' | 'preferred' | 'required';\n requireResidentKey?: boolean;\n userVerification?: 'discouraged' | 'preferred' | 'required';\n };\n hints?: PublicKeyCredentialHint[];\n attestation?: 'direct' | 'enterprise' | 'indirect' | 'none';\n extensions?: Record<string, unknown>;\n};\n\nexport type PasskeyRegistrationResponse = {\n id: Base64URL;\n rawId: Base64URL;\n type: 'public-key';\n response: {\n clientDataJSON: Base64URL;\n attestationObject: Base64URL;\n transports?: string[];\n publicKeyAlgorithm?: number;\n publicKey?: Base64URL;\n authenticatorData?: Base64URL;\n };\n authenticatorAttachment?: 'cross-platform' | 'platform';\n clientExtensionResults: Record<string, unknown>;\n};\n\nexport type PasskeyAuthenticationOptions = {\n challenge: Base64URL;\n timeout?: number;\n rpId?: string;\n allowCredentials?: PublicKeyCredentialDescriptorJSON[];\n userVerification?: 'discouraged' | 'preferred' | 'required';\n hints?: PublicKeyCredentialHint[];\n extensions?: Record<string, unknown>;\n};\n\nexport type PasskeyAuthenticationResponse = {\n id: Base64URL;\n rawId: Base64URL;\n type: 'public-key';\n response: {\n clientDataJSON: Base64URL;\n authenticatorData: Base64URL;\n signature: Base64URL;\n userHandle?: Base64URL;\n };\n authenticatorAttachment?: 'cross-platform' | 'platform';\n clientExtensionResults: Record<string, unknown>;\n};\n\nexport type ClientDataJSON = {\n type: string;\n challenge: string;\n origin: string;\n crossOrigin?: boolean;\n tokenBinding?: {\n id?: string;\n status: 'present' | 'supported' | 'not-supported';\n };\n};\n\nexport type AttestationFormat =\n | 'fido-u2f'\n | 'packed'\n | 'android-safetynet'\n | 'android-key'\n | 'tpm'\n | 'apple'\n | 'none';\n\nexport type AttestationObject = {\n get(key: 'fmt'): AttestationFormat;\n get(key: 'attStmt'): AttestationStatement;\n get(key: 'authData'): Uint8Array;\n};\n\nexport type AttestationStatement = {\n get(key: 'sig'): Uint8Array | undefined;\n get(key: 'x5c'): Uint8Array[] | undefined;\n get(key: 'alg'): number | undefined;\n readonly size: number;\n};\n\nexport type AuthenticatorDataFlags = {\n up: boolean;\n uv: boolean;\n be: boolean;\n bs: boolean;\n at: boolean;\n ed: boolean;\n flagsByte: number;\n};\n\nexport type ParsedAuthenticatorData = {\n rpIdHash: Uint8Array;\n flags: AuthenticatorDataFlags;\n counter: number;\n aaguid?: Uint8Array;\n credentialID?: Uint8Array;\n credentialPublicKey?: Uint8Array;\n extensionsData?: Map<string, unknown>;\n extensionsDataBuffer?: Uint8Array;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"types.cjs","sourceRoot":"","sources":["../../src/webauthn/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n AuthenticatorTransportFuture,\n Base64URLString as Base64URL,\n} from '../types.js';\n\nexport type PublicKeyCredentialDescriptorJSON = {\n id: Base64URL;\n type: 'public-key';\n transports?: AuthenticatorTransportFuture[];\n};\n\nexport type PublicKeyCredentialHint =\n | 'hybrid'\n | 'security-key'\n | 'client-device';\n\nexport type PasskeyRegistrationOptions = {\n rp: { name: string; id?: string };\n user: {\n id: Base64URL;\n name: string;\n displayName: string;\n };\n challenge: Base64URL;\n pubKeyCredParams: { alg: number; type: 'public-key' }[];\n timeout?: number;\n excludeCredentials?: PublicKeyCredentialDescriptorJSON[];\n authenticatorSelection?: {\n authenticatorAttachment?: 'cross-platform' | 'platform';\n residentKey?: 'discouraged' | 'preferred' | 'required';\n requireResidentKey?: boolean;\n userVerification?: 'discouraged' | 'preferred' | 'required';\n };\n hints?: PublicKeyCredentialHint[];\n attestation?: 'direct' | 'enterprise' | 'indirect' | 'none';\n extensions?: Record<string, unknown>;\n};\n\nexport type PasskeyRegistrationResponse = {\n id: Base64URL;\n rawId: Base64URL;\n type: 'public-key';\n response: {\n clientDataJSON: Base64URL;\n attestationObject: Base64URL;\n transports?: string[];\n publicKeyAlgorithm?: number;\n publicKey?: Base64URL;\n authenticatorData?: Base64URL;\n };\n authenticatorAttachment?: 'cross-platform' | 'platform';\n clientExtensionResults: Record<string, unknown>;\n};\n\nexport type PasskeyAuthenticationOptions = {\n challenge: Base64URL;\n timeout?: number;\n rpId?: string;\n allowCredentials?: PublicKeyCredentialDescriptorJSON[];\n userVerification?: 'discouraged' | 'preferred' | 'required';\n hints?: PublicKeyCredentialHint[];\n extensions?: Record<string, unknown>;\n};\n\nexport type PasskeyAuthenticationResponse = {\n id: Base64URL;\n rawId: Base64URL;\n type: 'public-key';\n response: {\n clientDataJSON: Base64URL;\n authenticatorData: Base64URL;\n signature: Base64URL;\n userHandle?: Base64URL;\n };\n authenticatorAttachment?: 'cross-platform' | 'platform';\n clientExtensionResults: Record<string, unknown>;\n};\n\nexport type ClientDataJSON = {\n type: string;\n challenge: string;\n origin: string;\n crossOrigin?: boolean;\n tokenBinding?: {\n id?: string;\n status: 'present' | 'supported' | 'not-supported';\n };\n};\n\nexport type AttestationFormat =\n | 'fido-u2f'\n | 'packed'\n | 'android-safetynet'\n | 'android-key'\n | 'tpm'\n | 'apple'\n | 'none';\n\nexport type AttestationObject = {\n get(key: 'fmt'): AttestationFormat;\n get(key: 'attStmt'): AttestationStatement;\n get(key: 'authData'): Uint8Array;\n};\n\nexport type AttestationStatement = {\n get(key: 'sig'): Uint8Array | undefined;\n get(key: 'x5c'): Uint8Array[] | undefined;\n get(key: 'alg'): number | undefined;\n readonly size: number;\n};\n\nexport type AuthenticatorDataFlags = {\n up: boolean;\n uv: boolean;\n be: boolean;\n bs: boolean;\n at: boolean;\n ed: boolean;\n flagsByte: number;\n};\n\nexport type ParsedAuthenticatorData = {\n rpIdHash: Uint8Array;\n flags: AuthenticatorDataFlags;\n counter: number;\n aaguid?: Uint8Array;\n credentialID?: Uint8Array;\n credentialPublicKey?: Uint8Array;\n extensionsData?: Map<string, unknown>;\n extensionsDataBuffer?: Uint8Array;\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.cts","sourceRoot":"","sources":["../../src/webauthn/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAC5B,eAAe,IAAI,SAAS,EAC7B,
|
|
1
|
+
{"version":3,"file":"types.d.cts","sourceRoot":"","sources":["../../src/webauthn/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAC5B,eAAe,IAAI,SAAS,EAC7B,qBAAoB;AAErB,MAAM,MAAM,iCAAiC,GAAG;IAC9C,EAAE,EAAE,SAAS,CAAC;IACd,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,CAAC,EAAE,4BAA4B,EAAE,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAC/B,QAAQ,GACR,cAAc,GACd,eAAe,CAAC;AAEpB,MAAM,MAAM,0BAA0B,GAAG;IACvC,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,IAAI,EAAE;QACJ,EAAE,EAAE,SAAS,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,SAAS,EAAE,SAAS,CAAC;IACrB,gBAAgB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,YAAY,CAAA;KAAE,EAAE,CAAC;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,iCAAiC,EAAE,CAAC;IACzD,sBAAsB,CAAC,EAAE;QACvB,uBAAuB,CAAC,EAAE,gBAAgB,GAAG,UAAU,CAAC;QACxD,WAAW,CAAC,EAAE,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;QACvD,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,gBAAgB,CAAC,EAAE,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;KAC7D,CAAC;IACF,KAAK,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAClC,WAAW,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,UAAU,GAAG,MAAM,CAAC;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,EAAE,EAAE,SAAS,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE;QACR,cAAc,EAAE,SAAS,CAAC;QAC1B,iBAAiB,EAAE,SAAS,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,iBAAiB,CAAC,EAAE,SAAS,CAAC;KAC/B,CAAC;IACF,uBAAuB,CAAC,EAAE,gBAAgB,GAAG,UAAU,CAAC;IACxD,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,iCAAiC,EAAE,CAAC;IACvD,gBAAgB,CAAC,EAAE,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;IAC5D,KAAK,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,EAAE,EAAE,SAAS,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE;QACR,cAAc,EAAE,SAAS,CAAC;QAC1B,iBAAiB,EAAE,SAAS,CAAC;QAC7B,SAAS,EAAE,SAAS,CAAC;QACrB,UAAU,CAAC,EAAE,SAAS,CAAC;KACxB,CAAC;IACF,uBAAuB,CAAC,EAAE,gBAAgB,GAAG,UAAU,CAAC;IACxD,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE;QACb,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,eAAe,CAAC;KACnD,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,iBAAiB,GACzB,UAAU,GACV,QAAQ,GACR,mBAAmB,GACnB,aAAa,GACb,KAAK,GACL,OAAO,GACP,MAAM,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,iBAAiB,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,SAAS,GAAG,oBAAoB,CAAC;IAC1C,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,UAAU,GAAG,SAAS,CAAC;IACxC,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,UAAU,EAAE,GAAG,SAAS,CAAC;IAC1C,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,EAAE,EAAE,OAAO,CAAC;IACZ,EAAE,EAAE,OAAO,CAAC;IACZ,EAAE,EAAE,OAAO,CAAC;IACZ,EAAE,EAAE,OAAO,CAAC;IACZ,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,EAAE,UAAU,CAAC;IACrB,KAAK,EAAE,sBAAsB,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,mBAAmB,CAAC,EAAE,UAAU,CAAC;IACjC,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,oBAAoB,CAAC,EAAE,UAAU,CAAC;CACnC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../../src/webauthn/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAC5B,eAAe,IAAI,SAAS,EAC7B,
|
|
1
|
+
{"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../../src/webauthn/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.mjs","sourceRoot":"","sources":["../../src/webauthn/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n AuthenticatorTransportFuture,\n Base64URLString as Base64URL,\n} from '../types';\n\nexport type PublicKeyCredentialDescriptorJSON = {\n id: Base64URL;\n type: 'public-key';\n transports?: AuthenticatorTransportFuture[];\n};\n\nexport type PublicKeyCredentialHint =\n | 'hybrid'\n | 'security-key'\n | 'client-device';\n\nexport type PasskeyRegistrationOptions = {\n rp: { name: string; id?: string };\n user: {\n id: Base64URL;\n name: string;\n displayName: string;\n };\n challenge: Base64URL;\n pubKeyCredParams: { alg: number; type: 'public-key' }[];\n timeout?: number;\n excludeCredentials?: PublicKeyCredentialDescriptorJSON[];\n authenticatorSelection?: {\n authenticatorAttachment?: 'cross-platform' | 'platform';\n residentKey?: 'discouraged' | 'preferred' | 'required';\n requireResidentKey?: boolean;\n userVerification?: 'discouraged' | 'preferred' | 'required';\n };\n hints?: PublicKeyCredentialHint[];\n attestation?: 'direct' | 'enterprise' | 'indirect' | 'none';\n extensions?: Record<string, unknown>;\n};\n\nexport type PasskeyRegistrationResponse = {\n id: Base64URL;\n rawId: Base64URL;\n type: 'public-key';\n response: {\n clientDataJSON: Base64URL;\n attestationObject: Base64URL;\n transports?: string[];\n publicKeyAlgorithm?: number;\n publicKey?: Base64URL;\n authenticatorData?: Base64URL;\n };\n authenticatorAttachment?: 'cross-platform' | 'platform';\n clientExtensionResults: Record<string, unknown>;\n};\n\nexport type PasskeyAuthenticationOptions = {\n challenge: Base64URL;\n timeout?: number;\n rpId?: string;\n allowCredentials?: PublicKeyCredentialDescriptorJSON[];\n userVerification?: 'discouraged' | 'preferred' | 'required';\n hints?: PublicKeyCredentialHint[];\n extensions?: Record<string, unknown>;\n};\n\nexport type PasskeyAuthenticationResponse = {\n id: Base64URL;\n rawId: Base64URL;\n type: 'public-key';\n response: {\n clientDataJSON: Base64URL;\n authenticatorData: Base64URL;\n signature: Base64URL;\n userHandle?: Base64URL;\n };\n authenticatorAttachment?: 'cross-platform' | 'platform';\n clientExtensionResults: Record<string, unknown>;\n};\n\nexport type ClientDataJSON = {\n type: string;\n challenge: string;\n origin: string;\n crossOrigin?: boolean;\n tokenBinding?: {\n id?: string;\n status: 'present' | 'supported' | 'not-supported';\n };\n};\n\nexport type AttestationFormat =\n | 'fido-u2f'\n | 'packed'\n | 'android-safetynet'\n | 'android-key'\n | 'tpm'\n | 'apple'\n | 'none';\n\nexport type AttestationObject = {\n get(key: 'fmt'): AttestationFormat;\n get(key: 'attStmt'): AttestationStatement;\n get(key: 'authData'): Uint8Array;\n};\n\nexport type AttestationStatement = {\n get(key: 'sig'): Uint8Array | undefined;\n get(key: 'x5c'): Uint8Array[] | undefined;\n get(key: 'alg'): number | undefined;\n readonly size: number;\n};\n\nexport type AuthenticatorDataFlags = {\n up: boolean;\n uv: boolean;\n be: boolean;\n bs: boolean;\n at: boolean;\n ed: boolean;\n flagsByte: number;\n};\n\nexport type ParsedAuthenticatorData = {\n rpIdHash: Uint8Array;\n flags: AuthenticatorDataFlags;\n counter: number;\n aaguid?: Uint8Array;\n credentialID?: Uint8Array;\n credentialPublicKey?: Uint8Array;\n extensionsData?: Map<string, unknown>;\n extensionsDataBuffer?: Uint8Array;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"types.mjs","sourceRoot":"","sources":["../../src/webauthn/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n AuthenticatorTransportFuture,\n Base64URLString as Base64URL,\n} from '../types.js';\n\nexport type PublicKeyCredentialDescriptorJSON = {\n id: Base64URL;\n type: 'public-key';\n transports?: AuthenticatorTransportFuture[];\n};\n\nexport type PublicKeyCredentialHint =\n | 'hybrid'\n | 'security-key'\n | 'client-device';\n\nexport type PasskeyRegistrationOptions = {\n rp: { name: string; id?: string };\n user: {\n id: Base64URL;\n name: string;\n displayName: string;\n };\n challenge: Base64URL;\n pubKeyCredParams: { alg: number; type: 'public-key' }[];\n timeout?: number;\n excludeCredentials?: PublicKeyCredentialDescriptorJSON[];\n authenticatorSelection?: {\n authenticatorAttachment?: 'cross-platform' | 'platform';\n residentKey?: 'discouraged' | 'preferred' | 'required';\n requireResidentKey?: boolean;\n userVerification?: 'discouraged' | 'preferred' | 'required';\n };\n hints?: PublicKeyCredentialHint[];\n attestation?: 'direct' | 'enterprise' | 'indirect' | 'none';\n extensions?: Record<string, unknown>;\n};\n\nexport type PasskeyRegistrationResponse = {\n id: Base64URL;\n rawId: Base64URL;\n type: 'public-key';\n response: {\n clientDataJSON: Base64URL;\n attestationObject: Base64URL;\n transports?: string[];\n publicKeyAlgorithm?: number;\n publicKey?: Base64URL;\n authenticatorData?: Base64URL;\n };\n authenticatorAttachment?: 'cross-platform' | 'platform';\n clientExtensionResults: Record<string, unknown>;\n};\n\nexport type PasskeyAuthenticationOptions = {\n challenge: Base64URL;\n timeout?: number;\n rpId?: string;\n allowCredentials?: PublicKeyCredentialDescriptorJSON[];\n userVerification?: 'discouraged' | 'preferred' | 'required';\n hints?: PublicKeyCredentialHint[];\n extensions?: Record<string, unknown>;\n};\n\nexport type PasskeyAuthenticationResponse = {\n id: Base64URL;\n rawId: Base64URL;\n type: 'public-key';\n response: {\n clientDataJSON: Base64URL;\n authenticatorData: Base64URL;\n signature: Base64URL;\n userHandle?: Base64URL;\n };\n authenticatorAttachment?: 'cross-platform' | 'platform';\n clientExtensionResults: Record<string, unknown>;\n};\n\nexport type ClientDataJSON = {\n type: string;\n challenge: string;\n origin: string;\n crossOrigin?: boolean;\n tokenBinding?: {\n id?: string;\n status: 'present' | 'supported' | 'not-supported';\n };\n};\n\nexport type AttestationFormat =\n | 'fido-u2f'\n | 'packed'\n | 'android-safetynet'\n | 'android-key'\n | 'tpm'\n | 'apple'\n | 'none';\n\nexport type AttestationObject = {\n get(key: 'fmt'): AttestationFormat;\n get(key: 'attStmt'): AttestationStatement;\n get(key: 'authData'): Uint8Array;\n};\n\nexport type AttestationStatement = {\n get(key: 'sig'): Uint8Array | undefined;\n get(key: 'x5c'): Uint8Array[] | undefined;\n get(key: 'alg'): number | undefined;\n readonly size: number;\n};\n\nexport type AuthenticatorDataFlags = {\n up: boolean;\n uv: boolean;\n be: boolean;\n bs: boolean;\n at: boolean;\n ed: boolean;\n flagsByte: number;\n};\n\nexport type ParsedAuthenticatorData = {\n rpIdHash: Uint8Array;\n flags: AuthenticatorDataFlags;\n counter: number;\n aaguid?: Uint8Array;\n credentialID?: Uint8Array;\n credentialPublicKey?: Uint8Array;\n extensionsData?: Map<string, unknown>;\n extensionsDataBuffer?: Uint8Array;\n};\n"]}
|
|
@@ -4,11 +4,11 @@ exports.verifyAuthenticationResponse = void 0;
|
|
|
4
4
|
const tiny_cbor_1 = require("@levischuck/tiny-cbor");
|
|
5
5
|
const utils_1 = require("@metamask/utils");
|
|
6
6
|
const sha2_1 = require("@noble/hashes/sha2");
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
7
|
+
const encoding_js_1 = require("../utils/encoding.cjs");
|
|
8
|
+
const decode_client_data_json_js_1 = require("./decode-client-data-json.cjs");
|
|
9
|
+
const match_expected_rp_id_js_1 = require("./match-expected-rp-id.cjs");
|
|
10
|
+
const parse_authenticator_data_js_1 = require("./parse-authenticator-data.cjs");
|
|
11
|
+
const verify_signature_js_1 = require("./verify-signature.cjs");
|
|
12
12
|
/**
|
|
13
13
|
* Verifies a WebAuthn authentication (assertion) response per
|
|
14
14
|
* W3C WebAuthn Level 3 §7.2.
|
|
@@ -61,7 +61,7 @@ async function verifyAuthenticationResponse(opts) {
|
|
|
61
61
|
if (typeof assertionResponse?.clientDataJSON !== 'string') {
|
|
62
62
|
throw new Error('Credential response clientDataJSON was not a string');
|
|
63
63
|
}
|
|
64
|
-
const clientDataJSON = (0,
|
|
64
|
+
const clientDataJSON = (0, decode_client_data_json_js_1.decodeClientDataJSON)(assertionResponse.clientDataJSON);
|
|
65
65
|
const { type, challenge, origin, tokenBinding } = clientDataJSON;
|
|
66
66
|
// Make sure we're handling an authentication
|
|
67
67
|
if (type !== 'webauthn.get') {
|
|
@@ -90,10 +90,10 @@ async function verifyAuthenticationResponse(opts) {
|
|
|
90
90
|
throw new Error(`Unexpected tokenBinding status ${tokenBinding.status}`);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
const authDataBuffer = (0,
|
|
94
|
-
const parsedAuthData = (0,
|
|
93
|
+
const authDataBuffer = (0, encoding_js_1.base64URLToBytes)(assertionResponse.authenticatorData);
|
|
94
|
+
const parsedAuthData = (0, parse_authenticator_data_js_1.parseAuthenticatorData)(authDataBuffer);
|
|
95
95
|
const { rpIdHash, flags, counter } = parsedAuthData;
|
|
96
|
-
const matchedRPID = expectedRPIDs.length > 0 ? (0,
|
|
96
|
+
const matchedRPID = expectedRPIDs.length > 0 ? (0, match_expected_rp_id_js_1.matchExpectedRPID)(rpIdHash, expectedRPIDs) : '';
|
|
97
97
|
// WebAuthn only requires the user presence flag be true
|
|
98
98
|
if (!flags.up) {
|
|
99
99
|
throw new Error('User not present during authentication');
|
|
@@ -102,11 +102,11 @@ async function verifyAuthenticationResponse(opts) {
|
|
|
102
102
|
if (requireUserVerification && !flags.uv) {
|
|
103
103
|
throw new Error('User verification required, but user could not be verified');
|
|
104
104
|
}
|
|
105
|
-
const clientDataHash = (0, sha2_1.sha256)((0,
|
|
105
|
+
const clientDataHash = (0, sha2_1.sha256)((0, encoding_js_1.base64URLToBytes)(assertionResponse.clientDataJSON));
|
|
106
106
|
const signatureBase = (0, utils_1.concatBytes)([authDataBuffer, clientDataHash]);
|
|
107
|
-
const signature = (0,
|
|
107
|
+
const signature = (0, encoding_js_1.base64URLToBytes)(assertionResponse.signature);
|
|
108
108
|
const cosePublicKey = (0, tiny_cbor_1.decodePartialCBOR)(new Uint8Array(credential.publicKey), 0)[0];
|
|
109
|
-
const verified = await (0,
|
|
109
|
+
const verified = await (0, verify_signature_js_1.verifySignature)({
|
|
110
110
|
cosePublicKey,
|
|
111
111
|
signature,
|
|
112
112
|
data: signatureBase,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-authentication-response.cjs","sourceRoot":"","sources":["../../src/webauthn/verify-authentication-response.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodePartialCBOR } from '@levischuck/tiny-cbor';\nimport { concatBytes } from '@metamask/utils';\nimport { sha256 } from '@noble/hashes/sha2';\n\nimport type { AuthenticatorTransportFuture } from '../types';\nimport { base64URLToBytes } from '../utils/encoding';\nimport { decodeClientDataJSON } from './decode-client-data-json';\nimport { matchExpectedRPID } from './match-expected-rp-id';\nimport { parseAuthenticatorData } from './parse-authenticator-data';\nimport type { ParsedAuthenticatorData } from './types';\nimport type { PasskeyAuthenticationResponse } from './types';\nimport { verifySignature } from './verify-signature';\n\nexport type VerifiedAuthenticationResponse =\n | { verified: false; authenticationInfo?: never }\n | {\n verified: true;\n authenticationInfo: {\n credentialId: string;\n newCounter: number;\n userVerified: boolean;\n origin: string;\n /** Matched RP ID, or `\"\"` when `expectedRPIDs` is empty (RP ID hash check skipped). */\n rpID: string;\n };\n };\n\n/**\n * Verifies a WebAuthn authentication (assertion) response per\n * W3C WebAuthn Level 3 §7.2.\n *\n * Performs the following checks in order:\n * 1. Credential ID presence, base64url consistency, and type.\n * 2. `clientDataJSON` -- type is `\"webauthn.get\"`, challenge and origin\n * match.\n * 3. `authenticatorData` -- RP ID hash matches, user-presence flag is\n * set, and optional user-verification flag is checked.\n * 4. Signature verification -- `signature` is verified over\n * `authData || SHA-256(clientDataJSON)` using the stored credential\n * public key (COSE-encoded).\n * 5. Counter monotonicity -- if either the stored or returned counter\n * is non-zero, the new counter must exceed the stored value.\n *\n * @param opts - Verification options.\n * @param opts.response - The `PublicKeyCredential` result from\n * `navigator.credentials.get()`, serialized as JSON.\n * @param opts.expectedChallenge - The base64url challenge that was issued\n * for this ceremony.\n * @param opts.expectedOrigin - One or more acceptable origins.\n * @param opts.expectedRPIDs - Relying Party ID strings to match against `rpIdHash`.\n * @param opts.credential - The stored credential record to verify against.\n * @param opts.credential.id - The credential ID (base64url).\n * @param opts.credential.publicKey - The COSE-encoded public key bytes\n * persisted during registration.\n * @param opts.credential.counter - The last known signature counter value.\n * @param opts.credential.transports - Optional authenticator transports.\n * @param opts.requireUserVerification - When `true`, verification fails\n * if the UV flag is not set. Defaults to `false`.\n * @returns Verification result containing `verified` status and parsed\n * authentication info (new counter, origin, RP ID).\n */\nexport async function verifyAuthenticationResponse(opts: {\n response: PasskeyAuthenticationResponse;\n expectedChallenge: string;\n expectedOrigin: string | string[];\n expectedRPIDs: string[];\n credential: {\n id: string;\n publicKey: Uint8Array;\n counter: number;\n transports?: AuthenticatorTransportFuture[];\n };\n requireUserVerification?: boolean;\n}): Promise<VerifiedAuthenticationResponse> {\n const {\n response,\n expectedChallenge,\n expectedOrigin,\n expectedRPIDs,\n credential,\n requireUserVerification = false,\n } = opts;\n\n const {\n id,\n rawId,\n type: credentialType,\n response: assertionResponse,\n } = response;\n\n // Ensure credential specified an ID\n if (!id) {\n throw new Error('Missing credential ID');\n }\n\n // Ensure ID is base64url-encoded\n if (id !== rawId) {\n throw new Error('Credential ID was not base64url-encoded');\n }\n\n // Make sure credential type is public-key\n if (credentialType !== 'public-key') {\n throw new Error(\n `Unexpected credential type ${String(credentialType)}, expected \"public-key\"`,\n );\n }\n\n if (typeof assertionResponse?.clientDataJSON !== 'string') {\n throw new Error('Credential response clientDataJSON was not a string');\n }\n\n const clientDataJSON = decodeClientDataJSON(assertionResponse.clientDataJSON);\n const { type, challenge, origin, tokenBinding } = clientDataJSON;\n\n // Make sure we're handling an authentication\n if (type !== 'webauthn.get') {\n throw new Error(`Unexpected authentication response type: ${type}`);\n }\n\n // Ensure the device provided the challenge we gave it\n if (challenge !== expectedChallenge) {\n throw new Error(\n `Unexpected authentication response challenge \"${challenge}\", expected \"${expectedChallenge}\"`,\n );\n }\n\n // Check that the origin is our site\n const expectedOrigins = Array.isArray(expectedOrigin)\n ? expectedOrigin\n : [expectedOrigin];\n if (!expectedOrigins.includes(origin)) {\n throw new Error(\n `Unexpected authentication response origin \"${origin}\", expected one of: ${expectedOrigins.join(', ')}`,\n );\n }\n\n if (\n assertionResponse.userHandle &&\n typeof assertionResponse.userHandle !== 'string'\n ) {\n throw new Error('Credential response userHandle was not a string');\n }\n\n if (tokenBinding) {\n if (typeof tokenBinding !== 'object') {\n throw new Error('ClientDataJSON tokenBinding was not an object');\n }\n\n if (\n !['present', 'supported', 'not-supported'].includes(tokenBinding.status)\n ) {\n throw new Error(`Unexpected tokenBinding status ${tokenBinding.status}`);\n }\n }\n\n const authDataBuffer = base64URLToBytes(assertionResponse.authenticatorData);\n const parsedAuthData: ParsedAuthenticatorData =\n parseAuthenticatorData(authDataBuffer);\n const { rpIdHash, flags, counter } = parsedAuthData;\n\n const matchedRPID =\n expectedRPIDs.length > 0 ? matchExpectedRPID(rpIdHash, expectedRPIDs) : '';\n\n // WebAuthn only requires the user presence flag be true\n if (!flags.up) {\n throw new Error('User not present during authentication');\n }\n\n // Enforce user verification if required\n if (requireUserVerification && !flags.uv) {\n throw new Error(\n 'User verification required, but user could not be verified',\n );\n }\n\n const clientDataHash = sha256(\n base64URLToBytes(assertionResponse.clientDataJSON),\n );\n const signatureBase = concatBytes([authDataBuffer, clientDataHash]);\n\n const signature = base64URLToBytes(assertionResponse.signature);\n\n const cosePublicKey = decodePartialCBOR(\n new Uint8Array(credential.publicKey),\n 0,\n )[0] as Map<number, number | Uint8Array>;\n\n const verified = await verifySignature({\n cosePublicKey,\n signature,\n data: signatureBase,\n });\n\n if (!verified) {\n return { verified: false };\n }\n\n if (\n (counter > 0 || credential.counter > 0) &&\n counter <= credential.counter\n ) {\n throw new Error(\n `Response counter value ${counter} must be greater than stored counter ${credential.counter}`,\n );\n }\n\n return {\n verified: true,\n authenticationInfo: {\n credentialId: credential.id,\n newCounter: counter,\n userVerified: flags.uv,\n origin: clientDataJSON.origin,\n rpID: matchedRPID,\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"verify-authentication-response.cjs","sourceRoot":"","sources":["../../src/webauthn/verify-authentication-response.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodePartialCBOR } from '@levischuck/tiny-cbor';\nimport { concatBytes } from '@metamask/utils';\nimport { sha256 } from '@noble/hashes/sha2';\n\nimport type { AuthenticatorTransportFuture } from '../types.js';\nimport { base64URLToBytes } from '../utils/encoding.js';\nimport { decodeClientDataJSON } from './decode-client-data-json.js';\nimport { matchExpectedRPID } from './match-expected-rp-id.js';\nimport { parseAuthenticatorData } from './parse-authenticator-data.js';\nimport type { ParsedAuthenticatorData } from './types.js';\nimport type { PasskeyAuthenticationResponse } from './types.js';\nimport { verifySignature } from './verify-signature.js';\n\nexport type VerifiedAuthenticationResponse =\n | { verified: false; authenticationInfo?: never }\n | {\n verified: true;\n authenticationInfo: {\n credentialId: string;\n newCounter: number;\n userVerified: boolean;\n origin: string;\n /** Matched RP ID, or `\"\"` when `expectedRPIDs` is empty (RP ID hash check skipped). */\n rpID: string;\n };\n };\n\n/**\n * Verifies a WebAuthn authentication (assertion) response per\n * W3C WebAuthn Level 3 §7.2.\n *\n * Performs the following checks in order:\n * 1. Credential ID presence, base64url consistency, and type.\n * 2. `clientDataJSON` -- type is `\"webauthn.get\"`, challenge and origin\n * match.\n * 3. `authenticatorData` -- RP ID hash matches, user-presence flag is\n * set, and optional user-verification flag is checked.\n * 4. Signature verification -- `signature` is verified over\n * `authData || SHA-256(clientDataJSON)` using the stored credential\n * public key (COSE-encoded).\n * 5. Counter monotonicity -- if either the stored or returned counter\n * is non-zero, the new counter must exceed the stored value.\n *\n * @param opts - Verification options.\n * @param opts.response - The `PublicKeyCredential` result from\n * `navigator.credentials.get()`, serialized as JSON.\n * @param opts.expectedChallenge - The base64url challenge that was issued\n * for this ceremony.\n * @param opts.expectedOrigin - One or more acceptable origins.\n * @param opts.expectedRPIDs - Relying Party ID strings to match against `rpIdHash`.\n * @param opts.credential - The stored credential record to verify against.\n * @param opts.credential.id - The credential ID (base64url).\n * @param opts.credential.publicKey - The COSE-encoded public key bytes\n * persisted during registration.\n * @param opts.credential.counter - The last known signature counter value.\n * @param opts.credential.transports - Optional authenticator transports.\n * @param opts.requireUserVerification - When `true`, verification fails\n * if the UV flag is not set. Defaults to `false`.\n * @returns Verification result containing `verified` status and parsed\n * authentication info (new counter, origin, RP ID).\n */\nexport async function verifyAuthenticationResponse(opts: {\n response: PasskeyAuthenticationResponse;\n expectedChallenge: string;\n expectedOrigin: string | string[];\n expectedRPIDs: string[];\n credential: {\n id: string;\n publicKey: Uint8Array;\n counter: number;\n transports?: AuthenticatorTransportFuture[];\n };\n requireUserVerification?: boolean;\n}): Promise<VerifiedAuthenticationResponse> {\n const {\n response,\n expectedChallenge,\n expectedOrigin,\n expectedRPIDs,\n credential,\n requireUserVerification = false,\n } = opts;\n\n const {\n id,\n rawId,\n type: credentialType,\n response: assertionResponse,\n } = response;\n\n // Ensure credential specified an ID\n if (!id) {\n throw new Error('Missing credential ID');\n }\n\n // Ensure ID is base64url-encoded\n if (id !== rawId) {\n throw new Error('Credential ID was not base64url-encoded');\n }\n\n // Make sure credential type is public-key\n if (credentialType !== 'public-key') {\n throw new Error(\n `Unexpected credential type ${String(credentialType)}, expected \"public-key\"`,\n );\n }\n\n if (typeof assertionResponse?.clientDataJSON !== 'string') {\n throw new Error('Credential response clientDataJSON was not a string');\n }\n\n const clientDataJSON = decodeClientDataJSON(assertionResponse.clientDataJSON);\n const { type, challenge, origin, tokenBinding } = clientDataJSON;\n\n // Make sure we're handling an authentication\n if (type !== 'webauthn.get') {\n throw new Error(`Unexpected authentication response type: ${type}`);\n }\n\n // Ensure the device provided the challenge we gave it\n if (challenge !== expectedChallenge) {\n throw new Error(\n `Unexpected authentication response challenge \"${challenge}\", expected \"${expectedChallenge}\"`,\n );\n }\n\n // Check that the origin is our site\n const expectedOrigins = Array.isArray(expectedOrigin)\n ? expectedOrigin\n : [expectedOrigin];\n if (!expectedOrigins.includes(origin)) {\n throw new Error(\n `Unexpected authentication response origin \"${origin}\", expected one of: ${expectedOrigins.join(', ')}`,\n );\n }\n\n if (\n assertionResponse.userHandle &&\n typeof assertionResponse.userHandle !== 'string'\n ) {\n throw new Error('Credential response userHandle was not a string');\n }\n\n if (tokenBinding) {\n if (typeof tokenBinding !== 'object') {\n throw new Error('ClientDataJSON tokenBinding was not an object');\n }\n\n if (\n !['present', 'supported', 'not-supported'].includes(tokenBinding.status)\n ) {\n throw new Error(`Unexpected tokenBinding status ${tokenBinding.status}`);\n }\n }\n\n const authDataBuffer = base64URLToBytes(assertionResponse.authenticatorData);\n const parsedAuthData: ParsedAuthenticatorData =\n parseAuthenticatorData(authDataBuffer);\n const { rpIdHash, flags, counter } = parsedAuthData;\n\n const matchedRPID =\n expectedRPIDs.length > 0 ? matchExpectedRPID(rpIdHash, expectedRPIDs) : '';\n\n // WebAuthn only requires the user presence flag be true\n if (!flags.up) {\n throw new Error('User not present during authentication');\n }\n\n // Enforce user verification if required\n if (requireUserVerification && !flags.uv) {\n throw new Error(\n 'User verification required, but user could not be verified',\n );\n }\n\n const clientDataHash = sha256(\n base64URLToBytes(assertionResponse.clientDataJSON),\n );\n const signatureBase = concatBytes([authDataBuffer, clientDataHash]);\n\n const signature = base64URLToBytes(assertionResponse.signature);\n\n const cosePublicKey = decodePartialCBOR(\n new Uint8Array(credential.publicKey),\n 0,\n )[0] as Map<number, number | Uint8Array>;\n\n const verified = await verifySignature({\n cosePublicKey,\n signature,\n data: signatureBase,\n });\n\n if (!verified) {\n return { verified: false };\n }\n\n if (\n (counter > 0 || credential.counter > 0) &&\n counter <= credential.counter\n ) {\n throw new Error(\n `Response counter value ${counter} must be greater than stored counter ${credential.counter}`,\n );\n }\n\n return {\n verified: true,\n authenticationInfo: {\n credentialId: credential.id,\n newCounter: counter,\n userVerified: flags.uv,\n origin: clientDataJSON.origin,\n rpID: matchedRPID,\n },\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-authentication-response.d.cts","sourceRoot":"","sources":["../../src/webauthn/verify-authentication-response.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,4BAA4B,EAAE,
|
|
1
|
+
{"version":3,"file":"verify-authentication-response.d.cts","sourceRoot":"","sources":["../../src/webauthn/verify-authentication-response.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,4BAA4B,EAAE,qBAAoB;AAMhE,OAAO,KAAK,EAAE,6BAA6B,EAAE,oBAAmB;AAGhE,MAAM,MAAM,8BAA8B,GACtC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,kBAAkB,CAAC,EAAE,KAAK,CAAA;CAAE,GAC/C;IACE,QAAQ,EAAE,IAAI,CAAC;IACf,kBAAkB,EAAE;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,OAAO,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,uFAAuF;QACvF,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,4BAA4B,CAAC,IAAI,EAAE;IACvD,QAAQ,EAAE,6BAA6B,CAAC;IACxC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE;QACV,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,EAAE,UAAU,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,4BAA4B,EAAE,CAAC;KAC7C,CAAC;IACF,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,GAAG,OAAO,CAAC,8BAA8B,CAAC,CA+I1C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-authentication-response.d.mts","sourceRoot":"","sources":["../../src/webauthn/verify-authentication-response.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,4BAA4B,EAAE,
|
|
1
|
+
{"version":3,"file":"verify-authentication-response.d.mts","sourceRoot":"","sources":["../../src/webauthn/verify-authentication-response.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,4BAA4B,EAAE,qBAAoB;AAMhE,OAAO,KAAK,EAAE,6BAA6B,EAAE,oBAAmB;AAGhE,MAAM,MAAM,8BAA8B,GACtC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,kBAAkB,CAAC,EAAE,KAAK,CAAA;CAAE,GAC/C;IACE,QAAQ,EAAE,IAAI,CAAC;IACf,kBAAkB,EAAE;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,OAAO,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,uFAAuF;QACvF,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,4BAA4B,CAAC,IAAI,EAAE;IACvD,QAAQ,EAAE,6BAA6B,CAAC;IACxC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE;QACV,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,EAAE,UAAU,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,4BAA4B,EAAE,CAAC;KAC7C,CAAC;IACF,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,GAAG,OAAO,CAAC,8BAA8B,CAAC,CA+I1C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-authentication-response.mjs","sourceRoot":"","sources":["../../src/webauthn/verify-authentication-response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,8BAA8B;AAC1D,OAAO,EAAE,WAAW,EAAE,wBAAwB;AAC9C,OAAO,EAAE,MAAM,EAAE,2BAA2B;AAG5C,OAAO,EAAE,gBAAgB,EAAE,8BAA0B;AACrD,OAAO,EAAE,oBAAoB,EAAE,sCAAkC;AACjE,OAAO,EAAE,iBAAiB,EAAE,mCAA+B;AAC3D,OAAO,EAAE,sBAAsB,EAAE,uCAAmC;AAGpE,OAAO,EAAE,eAAe,EAAE,+BAA2B;AAgBrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,IAYlD;IACC,MAAM,EACJ,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,UAAU,EACV,uBAAuB,GAAG,KAAK,GAChC,GAAG,IAAI,CAAC;IAET,MAAM,EACJ,EAAE,EACF,KAAK,EACL,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,iBAAiB,GAC5B,GAAG,QAAQ,CAAC;IAEb,oCAAoC;IACpC,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IAED,iCAAiC;IACjC,IAAI,EAAE,KAAK,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,0CAA0C;IAC1C,IAAI,cAAc,KAAK,YAAY,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CACb,8BAA8B,MAAM,CAAC,cAAc,CAAC,yBAAyB,CAC9E,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,iBAAiB,EAAE,cAAc,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,cAAc,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;IAC9E,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,cAAc,CAAC;IAEjE,6CAA6C;IAC7C,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,4CAA4C,IAAI,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,sDAAsD;IACtD,IAAI,SAAS,KAAK,iBAAiB,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CACb,iDAAiD,SAAS,gBAAgB,iBAAiB,GAAG,CAC/F,CAAC;IACJ,CAAC;IAED,oCAAoC;IACpC,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;QACnD,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IACrB,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CACb,8CAA8C,MAAM,uBAAuB,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACxG,CAAC;IACJ,CAAC;IAED,IACE,iBAAiB,CAAC,UAAU;QAC5B,OAAO,iBAAiB,CAAC,UAAU,KAAK,QAAQ,EAChD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACnE,CAAC;QAED,IACE,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,EACxE,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,kCAAkC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,MAAM,cAAc,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAC7E,MAAM,cAAc,GAClB,sBAAsB,CAAC,cAAc,CAAC,CAAC;IACzC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC;IAEpD,MAAM,WAAW,GACf,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE7E,wDAAwD;IACxD,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAED,wCAAwC;IACxC,IAAI,uBAAuB,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAC3B,gBAAgB,CAAC,iBAAiB,CAAC,cAAc,CAAC,CACnD,CAAC;IACF,MAAM,aAAa,GAAG,WAAW,CAAC,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC;IAEpE,MAAM,SAAS,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAEhE,MAAM,aAAa,GAAG,iBAAiB,CACrC,IAAI,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EACpC,CAAC,CACF,CAAC,CAAC,CAAqC,CAAC;IAEzC,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC;QACrC,aAAa;QACb,SAAS;QACT,IAAI,EAAE,aAAa;KACpB,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC7B,CAAC;IAED,IACE,CAAC,OAAO,GAAG,CAAC,IAAI,UAAU,CAAC,OAAO,GAAG,CAAC,CAAC;QACvC,OAAO,IAAI,UAAU,CAAC,OAAO,EAC7B,CAAC;QACD,MAAM,IAAI,KAAK,CACb,0BAA0B,OAAO,wCAAwC,UAAU,CAAC,OAAO,EAAE,CAC9F,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,IAAI;QACd,kBAAkB,EAAE;YAClB,YAAY,EAAE,UAAU,CAAC,EAAE;YAC3B,UAAU,EAAE,OAAO;YACnB,YAAY,EAAE,KAAK,CAAC,EAAE;YACtB,MAAM,EAAE,cAAc,CAAC,MAAM;YAC7B,IAAI,EAAE,WAAW;SAClB;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { decodePartialCBOR } from '@levischuck/tiny-cbor';\nimport { concatBytes } from '@metamask/utils';\nimport { sha256 } from '@noble/hashes/sha2';\n\nimport type { AuthenticatorTransportFuture } from '../types';\nimport { base64URLToBytes } from '../utils/encoding';\nimport { decodeClientDataJSON } from './decode-client-data-json';\nimport { matchExpectedRPID } from './match-expected-rp-id';\nimport { parseAuthenticatorData } from './parse-authenticator-data';\nimport type { ParsedAuthenticatorData } from './types';\nimport type { PasskeyAuthenticationResponse } from './types';\nimport { verifySignature } from './verify-signature';\n\nexport type VerifiedAuthenticationResponse =\n | { verified: false; authenticationInfo?: never }\n | {\n verified: true;\n authenticationInfo: {\n credentialId: string;\n newCounter: number;\n userVerified: boolean;\n origin: string;\n /** Matched RP ID, or `\"\"` when `expectedRPIDs` is empty (RP ID hash check skipped). */\n rpID: string;\n };\n };\n\n/**\n * Verifies a WebAuthn authentication (assertion) response per\n * W3C WebAuthn Level 3 §7.2.\n *\n * Performs the following checks in order:\n * 1. Credential ID presence, base64url consistency, and type.\n * 2. `clientDataJSON` -- type is `\"webauthn.get\"`, challenge and origin\n * match.\n * 3. `authenticatorData` -- RP ID hash matches, user-presence flag is\n * set, and optional user-verification flag is checked.\n * 4. Signature verification -- `signature` is verified over\n * `authData || SHA-256(clientDataJSON)` using the stored credential\n * public key (COSE-encoded).\n * 5. Counter monotonicity -- if either the stored or returned counter\n * is non-zero, the new counter must exceed the stored value.\n *\n * @param opts - Verification options.\n * @param opts.response - The `PublicKeyCredential` result from\n * `navigator.credentials.get()`, serialized as JSON.\n * @param opts.expectedChallenge - The base64url challenge that was issued\n * for this ceremony.\n * @param opts.expectedOrigin - One or more acceptable origins.\n * @param opts.expectedRPIDs - Relying Party ID strings to match against `rpIdHash`.\n * @param opts.credential - The stored credential record to verify against.\n * @param opts.credential.id - The credential ID (base64url).\n * @param opts.credential.publicKey - The COSE-encoded public key bytes\n * persisted during registration.\n * @param opts.credential.counter - The last known signature counter value.\n * @param opts.credential.transports - Optional authenticator transports.\n * @param opts.requireUserVerification - When `true`, verification fails\n * if the UV flag is not set. Defaults to `false`.\n * @returns Verification result containing `verified` status and parsed\n * authentication info (new counter, origin, RP ID).\n */\nexport async function verifyAuthenticationResponse(opts: {\n response: PasskeyAuthenticationResponse;\n expectedChallenge: string;\n expectedOrigin: string | string[];\n expectedRPIDs: string[];\n credential: {\n id: string;\n publicKey: Uint8Array;\n counter: number;\n transports?: AuthenticatorTransportFuture[];\n };\n requireUserVerification?: boolean;\n}): Promise<VerifiedAuthenticationResponse> {\n const {\n response,\n expectedChallenge,\n expectedOrigin,\n expectedRPIDs,\n credential,\n requireUserVerification = false,\n } = opts;\n\n const {\n id,\n rawId,\n type: credentialType,\n response: assertionResponse,\n } = response;\n\n // Ensure credential specified an ID\n if (!id) {\n throw new Error('Missing credential ID');\n }\n\n // Ensure ID is base64url-encoded\n if (id !== rawId) {\n throw new Error('Credential ID was not base64url-encoded');\n }\n\n // Make sure credential type is public-key\n if (credentialType !== 'public-key') {\n throw new Error(\n `Unexpected credential type ${String(credentialType)}, expected \"public-key\"`,\n );\n }\n\n if (typeof assertionResponse?.clientDataJSON !== 'string') {\n throw new Error('Credential response clientDataJSON was not a string');\n }\n\n const clientDataJSON = decodeClientDataJSON(assertionResponse.clientDataJSON);\n const { type, challenge, origin, tokenBinding } = clientDataJSON;\n\n // Make sure we're handling an authentication\n if (type !== 'webauthn.get') {\n throw new Error(`Unexpected authentication response type: ${type}`);\n }\n\n // Ensure the device provided the challenge we gave it\n if (challenge !== expectedChallenge) {\n throw new Error(\n `Unexpected authentication response challenge \"${challenge}\", expected \"${expectedChallenge}\"`,\n );\n }\n\n // Check that the origin is our site\n const expectedOrigins = Array.isArray(expectedOrigin)\n ? expectedOrigin\n : [expectedOrigin];\n if (!expectedOrigins.includes(origin)) {\n throw new Error(\n `Unexpected authentication response origin \"${origin}\", expected one of: ${expectedOrigins.join(', ')}`,\n );\n }\n\n if (\n assertionResponse.userHandle &&\n typeof assertionResponse.userHandle !== 'string'\n ) {\n throw new Error('Credential response userHandle was not a string');\n }\n\n if (tokenBinding) {\n if (typeof tokenBinding !== 'object') {\n throw new Error('ClientDataJSON tokenBinding was not an object');\n }\n\n if (\n !['present', 'supported', 'not-supported'].includes(tokenBinding.status)\n ) {\n throw new Error(`Unexpected tokenBinding status ${tokenBinding.status}`);\n }\n }\n\n const authDataBuffer = base64URLToBytes(assertionResponse.authenticatorData);\n const parsedAuthData: ParsedAuthenticatorData =\n parseAuthenticatorData(authDataBuffer);\n const { rpIdHash, flags, counter } = parsedAuthData;\n\n const matchedRPID =\n expectedRPIDs.length > 0 ? matchExpectedRPID(rpIdHash, expectedRPIDs) : '';\n\n // WebAuthn only requires the user presence flag be true\n if (!flags.up) {\n throw new Error('User not present during authentication');\n }\n\n // Enforce user verification if required\n if (requireUserVerification && !flags.uv) {\n throw new Error(\n 'User verification required, but user could not be verified',\n );\n }\n\n const clientDataHash = sha256(\n base64URLToBytes(assertionResponse.clientDataJSON),\n );\n const signatureBase = concatBytes([authDataBuffer, clientDataHash]);\n\n const signature = base64URLToBytes(assertionResponse.signature);\n\n const cosePublicKey = decodePartialCBOR(\n new Uint8Array(credential.publicKey),\n 0,\n )[0] as Map<number, number | Uint8Array>;\n\n const verified = await verifySignature({\n cosePublicKey,\n signature,\n data: signatureBase,\n });\n\n if (!verified) {\n return { verified: false };\n }\n\n if (\n (counter > 0 || credential.counter > 0) &&\n counter <= credential.counter\n ) {\n throw new Error(\n `Response counter value ${counter} must be greater than stored counter ${credential.counter}`,\n );\n }\n\n return {\n verified: true,\n authenticationInfo: {\n credentialId: credential.id,\n newCounter: counter,\n userVerified: flags.uv,\n origin: clientDataJSON.origin,\n rpID: matchedRPID,\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"verify-authentication-response.mjs","sourceRoot":"","sources":["../../src/webauthn/verify-authentication-response.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodePartialCBOR } from '@levischuck/tiny-cbor';\nimport { concatBytes } from '@metamask/utils';\nimport { sha256 } from '@noble/hashes/sha2';\n\nimport type { AuthenticatorTransportFuture } from '../types.js';\nimport { base64URLToBytes } from '../utils/encoding.js';\nimport { decodeClientDataJSON } from './decode-client-data-json.js';\nimport { matchExpectedRPID } from './match-expected-rp-id.js';\nimport { parseAuthenticatorData } from './parse-authenticator-data.js';\nimport type { ParsedAuthenticatorData } from './types.js';\nimport type { PasskeyAuthenticationResponse } from './types.js';\nimport { verifySignature } from './verify-signature.js';\n\nexport type VerifiedAuthenticationResponse =\n | { verified: false; authenticationInfo?: never }\n | {\n verified: true;\n authenticationInfo: {\n credentialId: string;\n newCounter: number;\n userVerified: boolean;\n origin: string;\n /** Matched RP ID, or `\"\"` when `expectedRPIDs` is empty (RP ID hash check skipped). */\n rpID: string;\n };\n };\n\n/**\n * Verifies a WebAuthn authentication (assertion) response per\n * W3C WebAuthn Level 3 §7.2.\n *\n * Performs the following checks in order:\n * 1. Credential ID presence, base64url consistency, and type.\n * 2. `clientDataJSON` -- type is `\"webauthn.get\"`, challenge and origin\n * match.\n * 3. `authenticatorData` -- RP ID hash matches, user-presence flag is\n * set, and optional user-verification flag is checked.\n * 4. Signature verification -- `signature` is verified over\n * `authData || SHA-256(clientDataJSON)` using the stored credential\n * public key (COSE-encoded).\n * 5. Counter monotonicity -- if either the stored or returned counter\n * is non-zero, the new counter must exceed the stored value.\n *\n * @param opts - Verification options.\n * @param opts.response - The `PublicKeyCredential` result from\n * `navigator.credentials.get()`, serialized as JSON.\n * @param opts.expectedChallenge - The base64url challenge that was issued\n * for this ceremony.\n * @param opts.expectedOrigin - One or more acceptable origins.\n * @param opts.expectedRPIDs - Relying Party ID strings to match against `rpIdHash`.\n * @param opts.credential - The stored credential record to verify against.\n * @param opts.credential.id - The credential ID (base64url).\n * @param opts.credential.publicKey - The COSE-encoded public key bytes\n * persisted during registration.\n * @param opts.credential.counter - The last known signature counter value.\n * @param opts.credential.transports - Optional authenticator transports.\n * @param opts.requireUserVerification - When `true`, verification fails\n * if the UV flag is not set. Defaults to `false`.\n * @returns Verification result containing `verified` status and parsed\n * authentication info (new counter, origin, RP ID).\n */\nexport async function verifyAuthenticationResponse(opts: {\n response: PasskeyAuthenticationResponse;\n expectedChallenge: string;\n expectedOrigin: string | string[];\n expectedRPIDs: string[];\n credential: {\n id: string;\n publicKey: Uint8Array;\n counter: number;\n transports?: AuthenticatorTransportFuture[];\n };\n requireUserVerification?: boolean;\n}): Promise<VerifiedAuthenticationResponse> {\n const {\n response,\n expectedChallenge,\n expectedOrigin,\n expectedRPIDs,\n credential,\n requireUserVerification = false,\n } = opts;\n\n const {\n id,\n rawId,\n type: credentialType,\n response: assertionResponse,\n } = response;\n\n // Ensure credential specified an ID\n if (!id) {\n throw new Error('Missing credential ID');\n }\n\n // Ensure ID is base64url-encoded\n if (id !== rawId) {\n throw new Error('Credential ID was not base64url-encoded');\n }\n\n // Make sure credential type is public-key\n if (credentialType !== 'public-key') {\n throw new Error(\n `Unexpected credential type ${String(credentialType)}, expected \"public-key\"`,\n );\n }\n\n if (typeof assertionResponse?.clientDataJSON !== 'string') {\n throw new Error('Credential response clientDataJSON was not a string');\n }\n\n const clientDataJSON = decodeClientDataJSON(assertionResponse.clientDataJSON);\n const { type, challenge, origin, tokenBinding } = clientDataJSON;\n\n // Make sure we're handling an authentication\n if (type !== 'webauthn.get') {\n throw new Error(`Unexpected authentication response type: ${type}`);\n }\n\n // Ensure the device provided the challenge we gave it\n if (challenge !== expectedChallenge) {\n throw new Error(\n `Unexpected authentication response challenge \"${challenge}\", expected \"${expectedChallenge}\"`,\n );\n }\n\n // Check that the origin is our site\n const expectedOrigins = Array.isArray(expectedOrigin)\n ? expectedOrigin\n : [expectedOrigin];\n if (!expectedOrigins.includes(origin)) {\n throw new Error(\n `Unexpected authentication response origin \"${origin}\", expected one of: ${expectedOrigins.join(', ')}`,\n );\n }\n\n if (\n assertionResponse.userHandle &&\n typeof assertionResponse.userHandle !== 'string'\n ) {\n throw new Error('Credential response userHandle was not a string');\n }\n\n if (tokenBinding) {\n if (typeof tokenBinding !== 'object') {\n throw new Error('ClientDataJSON tokenBinding was not an object');\n }\n\n if (\n !['present', 'supported', 'not-supported'].includes(tokenBinding.status)\n ) {\n throw new Error(`Unexpected tokenBinding status ${tokenBinding.status}`);\n }\n }\n\n const authDataBuffer = base64URLToBytes(assertionResponse.authenticatorData);\n const parsedAuthData: ParsedAuthenticatorData =\n parseAuthenticatorData(authDataBuffer);\n const { rpIdHash, flags, counter } = parsedAuthData;\n\n const matchedRPID =\n expectedRPIDs.length > 0 ? matchExpectedRPID(rpIdHash, expectedRPIDs) : '';\n\n // WebAuthn only requires the user presence flag be true\n if (!flags.up) {\n throw new Error('User not present during authentication');\n }\n\n // Enforce user verification if required\n if (requireUserVerification && !flags.uv) {\n throw new Error(\n 'User verification required, but user could not be verified',\n );\n }\n\n const clientDataHash = sha256(\n base64URLToBytes(assertionResponse.clientDataJSON),\n );\n const signatureBase = concatBytes([authDataBuffer, clientDataHash]);\n\n const signature = base64URLToBytes(assertionResponse.signature);\n\n const cosePublicKey = decodePartialCBOR(\n new Uint8Array(credential.publicKey),\n 0,\n )[0] as Map<number, number | Uint8Array>;\n\n const verified = await verifySignature({\n cosePublicKey,\n signature,\n data: signatureBase,\n });\n\n if (!verified) {\n return { verified: false };\n }\n\n if (\n (counter > 0 || credential.counter > 0) &&\n counter <= credential.counter\n ) {\n throw new Error(\n `Response counter value ${counter} must be greater than stored counter ${credential.counter}`,\n );\n }\n\n return {\n verified: true,\n authenticationInfo: {\n credentialId: credential.id,\n newCounter: counter,\n userVerified: flags.uv,\n origin: clientDataJSON.origin,\n rpID: matchedRPID,\n },\n };\n}\n"]}
|
|
@@ -4,13 +4,13 @@ exports.verifyRegistrationResponse = void 0;
|
|
|
4
4
|
const tiny_cbor_1 = require("@levischuck/tiny-cbor");
|
|
5
5
|
const utils_1 = require("@metamask/utils");
|
|
6
6
|
const sha2_1 = require("@noble/hashes/sha2");
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
7
|
+
const encoding_js_1 = require("../utils/encoding.cjs");
|
|
8
|
+
const constants_js_1 = require("./constants.cjs");
|
|
9
|
+
const decode_attestation_object_js_1 = require("./decode-attestation-object.cjs");
|
|
10
|
+
const decode_client_data_json_js_1 = require("./decode-client-data-json.cjs");
|
|
11
|
+
const match_expected_rp_id_js_1 = require("./match-expected-rp-id.cjs");
|
|
12
|
+
const parse_authenticator_data_js_1 = require("./parse-authenticator-data.cjs");
|
|
13
|
+
const verify_signature_js_1 = require("./verify-signature.cjs");
|
|
14
14
|
/**
|
|
15
15
|
* Verifies a WebAuthn registration (attestation) response per
|
|
16
16
|
* W3C WebAuthn Level 3 §7.1.
|
|
@@ -46,7 +46,7 @@ const verify_signature_1 = require("./verify-signature.cjs");
|
|
|
46
46
|
* hints. On failure, `{ verified: false }`.
|
|
47
47
|
*/
|
|
48
48
|
async function verifyRegistrationResponse(opts) {
|
|
49
|
-
const { response, expectedChallenge, expectedOrigin, expectedRPIDs, requireUserVerification = false, supportedAlgorithmIDs = [
|
|
49
|
+
const { response, expectedChallenge, expectedOrigin, expectedRPIDs, requireUserVerification = false, supportedAlgorithmIDs = [constants_js_1.COSEALG.EdDSA, constants_js_1.COSEALG.ES256, constants_js_1.COSEALG.RS256], } = opts;
|
|
50
50
|
const { id, rawId, type: credentialType, response: attestationResponse, } = response;
|
|
51
51
|
// Ensure credential specified an ID
|
|
52
52
|
if (!id) {
|
|
@@ -60,7 +60,7 @@ async function verifyRegistrationResponse(opts) {
|
|
|
60
60
|
if (credentialType !== 'public-key') {
|
|
61
61
|
throw new Error(`Unexpected credential type ${String(credentialType)}, expected "public-key"`);
|
|
62
62
|
}
|
|
63
|
-
const clientDataJSON = (0,
|
|
63
|
+
const clientDataJSON = (0, decode_client_data_json_js_1.decodeClientDataJSON)(attestationResponse.clientDataJSON);
|
|
64
64
|
const { type, challenge, origin, tokenBinding } = clientDataJSON;
|
|
65
65
|
// Make sure we're handling an registration
|
|
66
66
|
if (type !== 'webauthn.create') {
|
|
@@ -85,15 +85,15 @@ async function verifyRegistrationResponse(opts) {
|
|
|
85
85
|
throw new Error(`Unexpected tokenBinding.status value of "${tokenBinding.status}"`);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
const attestationObjectBytes = (0,
|
|
89
|
-
const decodedAttObj = (0,
|
|
88
|
+
const attestationObjectBytes = (0, encoding_js_1.base64URLToBytes)(attestationResponse.attestationObject);
|
|
89
|
+
const decodedAttObj = (0, decode_attestation_object_js_1.decodeAttestationObject)(attestationObjectBytes);
|
|
90
90
|
const fmt = decodedAttObj.get('fmt');
|
|
91
91
|
const authData = decodedAttObj.get('authData');
|
|
92
92
|
const attStmt = decodedAttObj.get('attStmt');
|
|
93
|
-
const parsedAuthData = (0,
|
|
93
|
+
const parsedAuthData = (0, parse_authenticator_data_js_1.parseAuthenticatorData)(authData);
|
|
94
94
|
const { rpIdHash, flags, counter, credentialID, credentialPublicKey, aaguid, } = parsedAuthData;
|
|
95
95
|
if (expectedRPIDs.length > 0) {
|
|
96
|
-
(0,
|
|
96
|
+
(0, match_expected_rp_id_js_1.matchExpectedRPID)(rpIdHash, expectedRPIDs);
|
|
97
97
|
}
|
|
98
98
|
// Make sure someone was physically present
|
|
99
99
|
if (!flags.up) {
|
|
@@ -106,7 +106,7 @@ async function verifyRegistrationResponse(opts) {
|
|
|
106
106
|
if (!credentialID) {
|
|
107
107
|
throw new Error('No credential ID was provided by authenticator');
|
|
108
108
|
}
|
|
109
|
-
const attestedCredentialId = (0,
|
|
109
|
+
const attestedCredentialId = (0, encoding_js_1.bytesToBase64URL)(credentialID);
|
|
110
110
|
if (id !== attestedCredentialId) {
|
|
111
111
|
throw new Error('Credential id does not match the credential id in authenticator data');
|
|
112
112
|
}
|
|
@@ -117,7 +117,7 @@ async function verifyRegistrationResponse(opts) {
|
|
|
117
117
|
throw new Error('No AAGUID was present during registration');
|
|
118
118
|
}
|
|
119
119
|
const decodedPublicKey = (0, tiny_cbor_1.decodePartialCBOR)(new Uint8Array(credentialPublicKey), 0)[0];
|
|
120
|
-
const alg = decodedPublicKey.get(
|
|
120
|
+
const alg = decodedPublicKey.get(constants_js_1.COSEKEYS.Alg);
|
|
121
121
|
if (typeof alg !== 'number') {
|
|
122
122
|
throw new Error('Credential public key was missing numeric alg');
|
|
123
123
|
}
|
|
@@ -141,7 +141,7 @@ async function verifyRegistrationResponse(opts) {
|
|
|
141
141
|
if (!verified) {
|
|
142
142
|
return { verified: false };
|
|
143
143
|
}
|
|
144
|
-
const aaguidHex = (0,
|
|
144
|
+
const aaguidHex = (0, encoding_js_1.bytesToHex)(aaguid);
|
|
145
145
|
const aaguidStr = [
|
|
146
146
|
aaguidHex.slice(0, 8),
|
|
147
147
|
aaguidHex.slice(8, 12),
|
|
@@ -192,13 +192,13 @@ async function verifyPackedAttestation(attStmt, authData, clientDataJSONB64url,
|
|
|
192
192
|
if (x5c && x5c.length > 0) {
|
|
193
193
|
throw new Error('Packed attestation with certificate chain (x5c) is not supported; only self-attestation is accepted');
|
|
194
194
|
}
|
|
195
|
-
const credAlg = cosePublicKey.get(
|
|
195
|
+
const credAlg = cosePublicKey.get(constants_js_1.COSEKEYS.Alg);
|
|
196
196
|
if (attStmtAlg !== credAlg) {
|
|
197
197
|
throw new Error(`Packed attestation alg ${attStmtAlg} does not match credential alg ${credAlg}`);
|
|
198
198
|
}
|
|
199
|
-
const clientDataHash = (0, sha2_1.sha256)((0,
|
|
199
|
+
const clientDataHash = (0, sha2_1.sha256)((0, encoding_js_1.base64URLToBytes)(clientDataJSONB64url));
|
|
200
200
|
const signatureBase = (0, utils_1.concatBytes)([authData, clientDataHash]);
|
|
201
|
-
return (0,
|
|
201
|
+
return (0, verify_signature_js_1.verifySignature)({
|
|
202
202
|
cosePublicKey,
|
|
203
203
|
signature,
|
|
204
204
|
data: signatureBase,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-registration-response.cjs","sourceRoot":"","sources":["../../src/webauthn/verify-registration-response.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodePartialCBOR } from '@levischuck/tiny-cbor';\nimport { concatBytes } from '@metamask/utils';\nimport { sha256 } from '@noble/hashes/sha2';\n\nimport type { AuthenticatorTransportFuture } from '../types';\nimport {\n base64URLToBytes,\n bytesToBase64URL,\n bytesToHex,\n} from '../utils/encoding';\nimport { COSEALG, COSEKEYS } from './constants';\nimport { decodeAttestationObject } from './decode-attestation-object';\nimport { decodeClientDataJSON } from './decode-client-data-json';\nimport { matchExpectedRPID } from './match-expected-rp-id';\nimport { parseAuthenticatorData } from './parse-authenticator-data';\nimport type { PasskeyRegistrationResponse } from './types';\nimport { verifySignature } from './verify-signature';\n\nexport type VerifiedRegistrationResponse =\n | { verified: false; registrationInfo?: never }\n | {\n verified: true;\n registrationInfo: {\n credentialId: string;\n publicKey: Uint8Array;\n counter: number;\n transports?: AuthenticatorTransportFuture[];\n aaguid: string;\n attestationFormat: string;\n userVerified: boolean;\n };\n };\n\n/**\n * Verifies a WebAuthn registration (attestation) response per\n * W3C WebAuthn Level 3 §7.1.\n *\n * Performs the following checks in order:\n * 1. Credential ID presence and base64url consistency (`id === rawId`), and\n * that `id` matches the credential id inside parsed authenticator data.\n * 2. Credential type is `\"public-key\"`.\n * 3. `clientDataJSON` -- type is `\"webauthn.create\"`, challenge and origin\n * match the expected values.\n * 4. Attestation object -- CBOR-decodes and parses `authData` to verify\n * the RP ID hash, user-presence flag, optional user-verification flag,\n * and the attested credential public key algorithm.\n * 5. Attestation statement -- supports `\"none\"` (no signature) and\n * `\"packed\"` self-attestation (signature verified against the\n * credential's own public key).\n *\n * @param opts - Verification options.\n * @param opts.response - The `PublicKeyCredential` result from\n * `navigator.credentials.create()`, serialized as JSON.\n * @param opts.expectedChallenge - The base64url challenge that was passed\n * to the authenticator (must match `clientDataJSON.challenge`).\n * @param opts.expectedOrigin - One or more acceptable values for\n * `clientDataJSON.origin` (WebAuthn). Extension and HTTPS contexts differ by scheme.\n * @param opts.expectedRPIDs - Relying Party ID strings. The authenticator's\n * `rpIdHash` must equal `SHA-256(rpID)` for at least one entry.\n * @param opts.requireUserVerification - When `true`, verification fails\n * if the UV flag is not set. Defaults to `false`.\n * @param opts.supportedAlgorithmIDs - COSE algorithm identifiers accepted\n * for the credential public key. Defaults to EdDSA, ES256, and RS256.\n * @returns On success, `{ verified: true, registrationInfo }` with the\n * parsed credential ID, public key, counter, AAGUID, and transport\n * hints. On failure, `{ verified: false }`.\n */\nexport async function verifyRegistrationResponse(opts: {\n response: PasskeyRegistrationResponse;\n expectedChallenge: string;\n expectedOrigin: string | string[];\n expectedRPIDs: string[];\n requireUserVerification?: boolean;\n supportedAlgorithmIDs?: number[];\n}): Promise<VerifiedRegistrationResponse> {\n const {\n response,\n expectedChallenge,\n expectedOrigin,\n expectedRPIDs,\n requireUserVerification = false,\n supportedAlgorithmIDs = [COSEALG.EdDSA, COSEALG.ES256, COSEALG.RS256],\n } = opts;\n\n const {\n id,\n rawId,\n type: credentialType,\n response: attestationResponse,\n } = response;\n\n // Ensure credential specified an ID\n if (!id) {\n throw new Error('Missing credential ID');\n }\n\n // Ensure ID is base64url-encoded\n if (id !== rawId) {\n throw new Error('Credential ID was not base64url-encoded');\n }\n\n // Make sure credential type is public-key\n if (credentialType !== 'public-key') {\n throw new Error(\n `Unexpected credential type ${String(credentialType)}, expected \"public-key\"`,\n );\n }\n\n const clientDataJSON = decodeClientDataJSON(\n attestationResponse.clientDataJSON,\n );\n const { type, challenge, origin, tokenBinding } = clientDataJSON;\n\n // Make sure we're handling an registration\n if (type !== 'webauthn.create') {\n throw new Error(`Unexpected registration response type: ${type}`);\n }\n\n // Ensure the device provided the challenge we gave it\n if (challenge !== expectedChallenge) {\n throw new Error(\n `Unexpected registration response challenge \"${challenge}\", expected \"${expectedChallenge}\"`,\n );\n }\n\n // Check that the origin is our site\n const expectedOrigins = Array.isArray(expectedOrigin)\n ? expectedOrigin\n : [expectedOrigin];\n if (!expectedOrigins.includes(origin)) {\n throw new Error(\n `Unexpected registration response origin \"${origin}\", expected one of: ${expectedOrigins.join(', ')}`,\n );\n }\n\n if (tokenBinding) {\n if (typeof tokenBinding !== 'object') {\n throw new Error('ClientDataJSON tokenBinding was not an object');\n }\n\n if (\n !['present', 'supported', 'not-supported'].includes(tokenBinding.status)\n ) {\n throw new Error(\n `Unexpected tokenBinding.status value of \"${tokenBinding.status}\"`,\n );\n }\n }\n\n const attestationObjectBytes = base64URLToBytes(\n attestationResponse.attestationObject,\n );\n const decodedAttObj = decodeAttestationObject(attestationObjectBytes);\n const fmt = decodedAttObj.get('fmt');\n const authData = decodedAttObj.get('authData');\n const attStmt = decodedAttObj.get('attStmt');\n\n const parsedAuthData = parseAuthenticatorData(authData);\n const {\n rpIdHash,\n flags,\n counter,\n credentialID,\n credentialPublicKey,\n aaguid,\n } = parsedAuthData;\n\n if (expectedRPIDs.length > 0) {\n matchExpectedRPID(rpIdHash, expectedRPIDs);\n }\n\n // Make sure someone was physically present\n if (!flags.up) {\n throw new Error('User presence was required, but user was not present');\n }\n\n // Enforce user verification if specified\n if (requireUserVerification && !flags.uv) {\n throw new Error(\n 'User verification was required, but user could not be verified',\n );\n }\n\n if (!credentialID) {\n throw new Error('No credential ID was provided by authenticator');\n }\n\n const attestedCredentialId = bytesToBase64URL(credentialID);\n if (id !== attestedCredentialId) {\n throw new Error(\n 'Credential id does not match the credential id in authenticator data',\n );\n }\n\n if (!credentialPublicKey) {\n throw new Error('No public key was provided by authenticator');\n }\n if (!aaguid) {\n throw new Error('No AAGUID was present during registration');\n }\n\n const decodedPublicKey = decodePartialCBOR(\n new Uint8Array(credentialPublicKey),\n 0,\n )[0] as Map<number, number | Uint8Array>;\n const alg = decodedPublicKey.get(COSEKEYS.Alg);\n\n if (typeof alg !== 'number') {\n throw new Error('Credential public key was missing numeric alg');\n }\n\n // Make sure the key algorithm is one we specified within the registration options\n if (!supportedAlgorithmIDs.includes(alg)) {\n throw new Error(\n `Unexpected public key alg \"${alg}\", expected one of \"${supportedAlgorithmIDs.join(', ')}\"`,\n );\n }\n\n let verified = false;\n if (fmt === 'none') {\n if (attStmt.size > 0) {\n throw new Error('None attestation had unexpected attestation statement');\n }\n verified = true;\n } else if (fmt === 'packed') {\n verified = await verifyPackedAttestation(\n attStmt,\n authData,\n attestationResponse.clientDataJSON,\n decodedPublicKey,\n );\n } else {\n throw new Error(`Unsupported attestation format: ${fmt}`);\n }\n\n if (!verified) {\n return { verified: false };\n }\n\n const aaguidHex = bytesToHex(aaguid);\n const aaguidStr = [\n aaguidHex.slice(0, 8),\n aaguidHex.slice(8, 12),\n aaguidHex.slice(12, 16),\n aaguidHex.slice(16, 20),\n aaguidHex.slice(20),\n ].join('-');\n\n return {\n verified: true,\n registrationInfo: {\n credentialId: attestedCredentialId,\n publicKey: credentialPublicKey,\n counter,\n transports:\n attestationResponse.transports as AuthenticatorTransportFuture[],\n aaguid: aaguidStr,\n attestationFormat: fmt,\n userVerified: flags.uv,\n },\n };\n}\n\n/**\n * Verify packed self-attestation per WebAuthn §8.2: no x5c certificate\n * chain, signature over `authData || SHA-256(clientDataJSON)` verified\n * with the credential's own public key, and `alg` in the attestation\n * statement must match the credential key's algorithm.\n *\n * @param attStmt - The attestation statement map from the attestation\n * object.\n * @param attStmt.get - Accessor to retrieve statement fields by key.\n * @param attStmt.size - Number of entries in the statement.\n * @param authData - Raw authenticator data bytes.\n * @param clientDataJSONB64url - Base64url-encoded clientDataJSON.\n * @param cosePublicKey - Decoded COSE public key map from authenticator\n * data.\n * @returns Whether the packed attestation signature is valid.\n */\nasync function verifyPackedAttestation(\n attStmt: { get(key: string): unknown; size: number },\n authData: Uint8Array,\n clientDataJSONB64url: string,\n cosePublicKey: Map<number, number | Uint8Array>,\n): Promise<boolean> {\n const attStmtAlg = attStmt.get('alg') as number | undefined;\n const signature = attStmt.get('sig') as Uint8Array | undefined;\n const x5c = attStmt.get('x5c') as Uint8Array[] | undefined;\n\n if (typeof attStmtAlg !== 'number') {\n throw new Error('Packed attestation statement missing alg');\n }\n\n if (!signature) {\n throw new Error('Packed attestation missing signature');\n }\n\n if (x5c && x5c.length > 0) {\n throw new Error(\n 'Packed attestation with certificate chain (x5c) is not supported; only self-attestation is accepted',\n );\n }\n\n const credAlg = cosePublicKey.get(COSEKEYS.Alg) as number;\n if (attStmtAlg !== credAlg) {\n throw new Error(\n `Packed attestation alg ${attStmtAlg} does not match credential alg ${credAlg}`,\n );\n }\n\n const clientDataHash = sha256(base64URLToBytes(clientDataJSONB64url));\n const signatureBase = concatBytes([authData, clientDataHash]);\n\n return verifySignature({\n cosePublicKey,\n signature,\n data: signatureBase,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"verify-registration-response.cjs","sourceRoot":"","sources":["../../src/webauthn/verify-registration-response.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodePartialCBOR } from '@levischuck/tiny-cbor';\nimport { concatBytes } from '@metamask/utils';\nimport { sha256 } from '@noble/hashes/sha2';\n\nimport type { AuthenticatorTransportFuture } from '../types.js';\nimport {\n base64URLToBytes,\n bytesToBase64URL,\n bytesToHex,\n} from '../utils/encoding.js';\nimport { COSEALG, COSEKEYS } from './constants.js';\nimport { decodeAttestationObject } from './decode-attestation-object.js';\nimport { decodeClientDataJSON } from './decode-client-data-json.js';\nimport { matchExpectedRPID } from './match-expected-rp-id.js';\nimport { parseAuthenticatorData } from './parse-authenticator-data.js';\nimport type { PasskeyRegistrationResponse } from './types.js';\nimport { verifySignature } from './verify-signature.js';\n\nexport type VerifiedRegistrationResponse =\n | { verified: false; registrationInfo?: never }\n | {\n verified: true;\n registrationInfo: {\n credentialId: string;\n publicKey: Uint8Array;\n counter: number;\n transports?: AuthenticatorTransportFuture[];\n aaguid: string;\n attestationFormat: string;\n userVerified: boolean;\n };\n };\n\n/**\n * Verifies a WebAuthn registration (attestation) response per\n * W3C WebAuthn Level 3 §7.1.\n *\n * Performs the following checks in order:\n * 1. Credential ID presence and base64url consistency (`id === rawId`), and\n * that `id` matches the credential id inside parsed authenticator data.\n * 2. Credential type is `\"public-key\"`.\n * 3. `clientDataJSON` -- type is `\"webauthn.create\"`, challenge and origin\n * match the expected values.\n * 4. Attestation object -- CBOR-decodes and parses `authData` to verify\n * the RP ID hash, user-presence flag, optional user-verification flag,\n * and the attested credential public key algorithm.\n * 5. Attestation statement -- supports `\"none\"` (no signature) and\n * `\"packed\"` self-attestation (signature verified against the\n * credential's own public key).\n *\n * @param opts - Verification options.\n * @param opts.response - The `PublicKeyCredential` result from\n * `navigator.credentials.create()`, serialized as JSON.\n * @param opts.expectedChallenge - The base64url challenge that was passed\n * to the authenticator (must match `clientDataJSON.challenge`).\n * @param opts.expectedOrigin - One or more acceptable values for\n * `clientDataJSON.origin` (WebAuthn). Extension and HTTPS contexts differ by scheme.\n * @param opts.expectedRPIDs - Relying Party ID strings. The authenticator's\n * `rpIdHash` must equal `SHA-256(rpID)` for at least one entry.\n * @param opts.requireUserVerification - When `true`, verification fails\n * if the UV flag is not set. Defaults to `false`.\n * @param opts.supportedAlgorithmIDs - COSE algorithm identifiers accepted\n * for the credential public key. Defaults to EdDSA, ES256, and RS256.\n * @returns On success, `{ verified: true, registrationInfo }` with the\n * parsed credential ID, public key, counter, AAGUID, and transport\n * hints. On failure, `{ verified: false }`.\n */\nexport async function verifyRegistrationResponse(opts: {\n response: PasskeyRegistrationResponse;\n expectedChallenge: string;\n expectedOrigin: string | string[];\n expectedRPIDs: string[];\n requireUserVerification?: boolean;\n supportedAlgorithmIDs?: number[];\n}): Promise<VerifiedRegistrationResponse> {\n const {\n response,\n expectedChallenge,\n expectedOrigin,\n expectedRPIDs,\n requireUserVerification = false,\n supportedAlgorithmIDs = [COSEALG.EdDSA, COSEALG.ES256, COSEALG.RS256],\n } = opts;\n\n const {\n id,\n rawId,\n type: credentialType,\n response: attestationResponse,\n } = response;\n\n // Ensure credential specified an ID\n if (!id) {\n throw new Error('Missing credential ID');\n }\n\n // Ensure ID is base64url-encoded\n if (id !== rawId) {\n throw new Error('Credential ID was not base64url-encoded');\n }\n\n // Make sure credential type is public-key\n if (credentialType !== 'public-key') {\n throw new Error(\n `Unexpected credential type ${String(credentialType)}, expected \"public-key\"`,\n );\n }\n\n const clientDataJSON = decodeClientDataJSON(\n attestationResponse.clientDataJSON,\n );\n const { type, challenge, origin, tokenBinding } = clientDataJSON;\n\n // Make sure we're handling an registration\n if (type !== 'webauthn.create') {\n throw new Error(`Unexpected registration response type: ${type}`);\n }\n\n // Ensure the device provided the challenge we gave it\n if (challenge !== expectedChallenge) {\n throw new Error(\n `Unexpected registration response challenge \"${challenge}\", expected \"${expectedChallenge}\"`,\n );\n }\n\n // Check that the origin is our site\n const expectedOrigins = Array.isArray(expectedOrigin)\n ? expectedOrigin\n : [expectedOrigin];\n if (!expectedOrigins.includes(origin)) {\n throw new Error(\n `Unexpected registration response origin \"${origin}\", expected one of: ${expectedOrigins.join(', ')}`,\n );\n }\n\n if (tokenBinding) {\n if (typeof tokenBinding !== 'object') {\n throw new Error('ClientDataJSON tokenBinding was not an object');\n }\n\n if (\n !['present', 'supported', 'not-supported'].includes(tokenBinding.status)\n ) {\n throw new Error(\n `Unexpected tokenBinding.status value of \"${tokenBinding.status}\"`,\n );\n }\n }\n\n const attestationObjectBytes = base64URLToBytes(\n attestationResponse.attestationObject,\n );\n const decodedAttObj = decodeAttestationObject(attestationObjectBytes);\n const fmt = decodedAttObj.get('fmt');\n const authData = decodedAttObj.get('authData');\n const attStmt = decodedAttObj.get('attStmt');\n\n const parsedAuthData = parseAuthenticatorData(authData);\n const {\n rpIdHash,\n flags,\n counter,\n credentialID,\n credentialPublicKey,\n aaguid,\n } = parsedAuthData;\n\n if (expectedRPIDs.length > 0) {\n matchExpectedRPID(rpIdHash, expectedRPIDs);\n }\n\n // Make sure someone was physically present\n if (!flags.up) {\n throw new Error('User presence was required, but user was not present');\n }\n\n // Enforce user verification if specified\n if (requireUserVerification && !flags.uv) {\n throw new Error(\n 'User verification was required, but user could not be verified',\n );\n }\n\n if (!credentialID) {\n throw new Error('No credential ID was provided by authenticator');\n }\n\n const attestedCredentialId = bytesToBase64URL(credentialID);\n if (id !== attestedCredentialId) {\n throw new Error(\n 'Credential id does not match the credential id in authenticator data',\n );\n }\n\n if (!credentialPublicKey) {\n throw new Error('No public key was provided by authenticator');\n }\n if (!aaguid) {\n throw new Error('No AAGUID was present during registration');\n }\n\n const decodedPublicKey = decodePartialCBOR(\n new Uint8Array(credentialPublicKey),\n 0,\n )[0] as Map<number, number | Uint8Array>;\n const alg = decodedPublicKey.get(COSEKEYS.Alg);\n\n if (typeof alg !== 'number') {\n throw new Error('Credential public key was missing numeric alg');\n }\n\n // Make sure the key algorithm is one we specified within the registration options\n if (!supportedAlgorithmIDs.includes(alg)) {\n throw new Error(\n `Unexpected public key alg \"${alg}\", expected one of \"${supportedAlgorithmIDs.join(', ')}\"`,\n );\n }\n\n let verified = false;\n if (fmt === 'none') {\n if (attStmt.size > 0) {\n throw new Error('None attestation had unexpected attestation statement');\n }\n verified = true;\n } else if (fmt === 'packed') {\n verified = await verifyPackedAttestation(\n attStmt,\n authData,\n attestationResponse.clientDataJSON,\n decodedPublicKey,\n );\n } else {\n throw new Error(`Unsupported attestation format: ${fmt}`);\n }\n\n if (!verified) {\n return { verified: false };\n }\n\n const aaguidHex = bytesToHex(aaguid);\n const aaguidStr = [\n aaguidHex.slice(0, 8),\n aaguidHex.slice(8, 12),\n aaguidHex.slice(12, 16),\n aaguidHex.slice(16, 20),\n aaguidHex.slice(20),\n ].join('-');\n\n return {\n verified: true,\n registrationInfo: {\n credentialId: attestedCredentialId,\n publicKey: credentialPublicKey,\n counter,\n transports:\n attestationResponse.transports as AuthenticatorTransportFuture[],\n aaguid: aaguidStr,\n attestationFormat: fmt,\n userVerified: flags.uv,\n },\n };\n}\n\n/**\n * Verify packed self-attestation per WebAuthn §8.2: no x5c certificate\n * chain, signature over `authData || SHA-256(clientDataJSON)` verified\n * with the credential's own public key, and `alg` in the attestation\n * statement must match the credential key's algorithm.\n *\n * @param attStmt - The attestation statement map from the attestation\n * object.\n * @param attStmt.get - Accessor to retrieve statement fields by key.\n * @param attStmt.size - Number of entries in the statement.\n * @param authData - Raw authenticator data bytes.\n * @param clientDataJSONB64url - Base64url-encoded clientDataJSON.\n * @param cosePublicKey - Decoded COSE public key map from authenticator\n * data.\n * @returns Whether the packed attestation signature is valid.\n */\nasync function verifyPackedAttestation(\n attStmt: { get(key: string): unknown; size: number },\n authData: Uint8Array,\n clientDataJSONB64url: string,\n cosePublicKey: Map<number, number | Uint8Array>,\n): Promise<boolean> {\n const attStmtAlg = attStmt.get('alg') as number | undefined;\n const signature = attStmt.get('sig') as Uint8Array | undefined;\n const x5c = attStmt.get('x5c') as Uint8Array[] | undefined;\n\n if (typeof attStmtAlg !== 'number') {\n throw new Error('Packed attestation statement missing alg');\n }\n\n if (!signature) {\n throw new Error('Packed attestation missing signature');\n }\n\n if (x5c && x5c.length > 0) {\n throw new Error(\n 'Packed attestation with certificate chain (x5c) is not supported; only self-attestation is accepted',\n );\n }\n\n const credAlg = cosePublicKey.get(COSEKEYS.Alg) as number;\n if (attStmtAlg !== credAlg) {\n throw new Error(\n `Packed attestation alg ${attStmtAlg} does not match credential alg ${credAlg}`,\n );\n }\n\n const clientDataHash = sha256(base64URLToBytes(clientDataJSONB64url));\n const signatureBase = concatBytes([authData, clientDataHash]);\n\n return verifySignature({\n cosePublicKey,\n signature,\n data: signatureBase,\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-registration-response.d.cts","sourceRoot":"","sources":["../../src/webauthn/verify-registration-response.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,4BAA4B,EAAE,
|
|
1
|
+
{"version":3,"file":"verify-registration-response.d.cts","sourceRoot":"","sources":["../../src/webauthn/verify-registration-response.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,4BAA4B,EAAE,qBAAoB;AAWhE,OAAO,KAAK,EAAE,2BAA2B,EAAE,oBAAmB;AAG9D,MAAM,MAAM,4BAA4B,GACpC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,gBAAgB,CAAC,EAAE,KAAK,CAAA;CAAE,GAC7C;IACE,QAAQ,EAAE,IAAI,CAAC;IACf,gBAAgB,EAAE;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,UAAU,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,4BAA4B,EAAE,CAAC;QAC5C,MAAM,EAAE,MAAM,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,YAAY,EAAE,OAAO,CAAC;KACvB,CAAC;CACH,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,QAAQ,EAAE,2BAA2B,CAAC;IACtC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CAClC,GAAG,OAAO,CAAC,4BAA4B,CAAC,CA2LxC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-registration-response.d.mts","sourceRoot":"","sources":["../../src/webauthn/verify-registration-response.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,4BAA4B,EAAE,
|
|
1
|
+
{"version":3,"file":"verify-registration-response.d.mts","sourceRoot":"","sources":["../../src/webauthn/verify-registration-response.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,4BAA4B,EAAE,qBAAoB;AAWhE,OAAO,KAAK,EAAE,2BAA2B,EAAE,oBAAmB;AAG9D,MAAM,MAAM,4BAA4B,GACpC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,gBAAgB,CAAC,EAAE,KAAK,CAAA;CAAE,GAC7C;IACE,QAAQ,EAAE,IAAI,CAAC;IACf,gBAAgB,EAAE;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,UAAU,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,4BAA4B,EAAE,CAAC;QAC5C,MAAM,EAAE,MAAM,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,YAAY,EAAE,OAAO,CAAC;KACvB,CAAC;CACH,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,QAAQ,EAAE,2BAA2B,CAAC;IACtC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CAClC,GAAG,OAAO,CAAC,4BAA4B,CAAC,CA2LxC"}
|