@simplewebauthn/server 10.0.1 → 11.0.0

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.
@@ -1,11 +1,11 @@
1
- import type { AuthenticationResponseJSON, AuthenticatorDevice, Base64URLString, CredentialDeviceType, UserVerificationRequirement } from '../deps.js';
1
+ import type { AuthenticationResponseJSON, Base64URLString, CredentialDeviceType, UserVerificationRequirement, WebAuthnCredential } from '../deps.js';
2
2
  import { AuthenticationExtensionsAuthenticatorOutputs } from '../helpers/decodeAuthenticatorExtensions.js';
3
3
  export type VerifyAuthenticationResponseOpts = {
4
4
  response: AuthenticationResponseJSON;
5
5
  expectedChallenge: string | ((challenge: string) => boolean | Promise<boolean>);
6
6
  expectedOrigin: string | string[];
7
7
  expectedRPID: string | string[];
8
- authenticator: AuthenticatorDevice;
8
+ credential: WebAuthnCredential;
9
9
  expectedType?: string | string[];
10
10
  requireUserVerification?: boolean;
11
11
  advancedFIDOConfig?: {
@@ -21,7 +21,7 @@ export type VerifyAuthenticationResponseOpts = {
21
21
  * @param expectedChallenge - The base64url-encoded `options.challenge` returned by `generateAuthenticationOptions()`
22
22
  * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
23
23
  * @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
24
- * @param authenticator - An internal {@link AuthenticatorDevice} matching the credential's ID
24
+ * @param credential - An internal {@link WebAuthnCredential} corresponding to `id` in the authentication response
25
25
  * @param expectedType **(Optional)** - The response type expected ('webauthn.get')
26
26
  * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
27
27
  * @param advancedFIDOConfig **(Optional)** - Options for satisfying more stringent FIDO RP feature requirements
@@ -14,14 +14,14 @@ import { isoBase64URL, isoUint8Array } from '../helpers/iso/index.js';
14
14
  * @param expectedChallenge - The base64url-encoded `options.challenge` returned by `generateAuthenticationOptions()`
15
15
  * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
16
16
  * @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
17
- * @param authenticator - An internal {@link AuthenticatorDevice} matching the credential's ID
17
+ * @param credential - An internal {@link WebAuthnCredential} corresponding to `id` in the authentication response
18
18
  * @param expectedType **(Optional)** - The response type expected ('webauthn.get')
19
19
  * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
20
20
  * @param advancedFIDOConfig **(Optional)** - Options for satisfying more stringent FIDO RP feature requirements
21
21
  * @param advancedFIDOConfig.userVerification **(Optional)** - Enable alternative rules for evaluating the User Presence and User Verified flags in authenticator data: UV (and UP) flags are optional unless this value is `"required"`
22
22
  */
23
23
  export async function verifyAuthenticationResponse(options) {
24
- const { response, expectedChallenge, expectedOrigin, expectedRPID, expectedType, authenticator, requireUserVerification = true, advancedFIDOConfig, } = options;
24
+ const { response, expectedChallenge, expectedOrigin, expectedRPID, expectedType, credential, requireUserVerification = true, advancedFIDOConfig, } = options;
25
25
  const { id, rawId, type: credentialType, response: assertionResponse } = response;
26
26
  // Ensure credential specified an ID
27
27
  if (!id) {
@@ -141,24 +141,24 @@ export async function verifyAuthenticationResponse(options) {
141
141
  const clientDataHash = await toHash(isoBase64URL.toBuffer(assertionResponse.clientDataJSON));
142
142
  const signatureBase = isoUint8Array.concat([authDataBuffer, clientDataHash]);
143
143
  const signature = isoBase64URL.toBuffer(assertionResponse.signature);
144
- if ((counter > 0 || authenticator.counter > 0) &&
145
- counter <= authenticator.counter) {
144
+ if ((counter > 0 || credential.counter > 0) &&
145
+ counter <= credential.counter) {
146
146
  // Error out when the counter in the DB is greater than or equal to the counter in the
147
147
  // dataStruct. It's related to how the authenticator maintains the number of times its been
148
148
  // used for this client. If this happens, then someone's somehow increased the counter
149
149
  // on the device without going through this site
150
- throw new Error(`Response counter value ${counter} was lower than expected ${authenticator.counter}`);
150
+ throw new Error(`Response counter value ${counter} was lower than expected ${credential.counter}`);
151
151
  }
152
152
  const { credentialDeviceType, credentialBackedUp } = parseBackupFlags(flags);
153
153
  const toReturn = {
154
154
  verified: await verifySignature({
155
155
  signature,
156
156
  data: signatureBase,
157
- credentialPublicKey: authenticator.credentialPublicKey,
157
+ credentialPublicKey: credential.publicKey,
158
158
  }),
159
159
  authenticationInfo: {
160
160
  newCounter: counter,
161
- credentialID: authenticator.credentialID,
161
+ credentialID: credential.id,
162
162
  userVerified: flags.uv,
163
163
  credentialDeviceType,
164
164
  credentialBackedUp,
package/esm/deps.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- export type { AttestationConveyancePreference, AuthenticationExtensionsClientInputs, AuthenticationResponseJSON, AuthenticatorDevice, AuthenticatorSelectionCriteria, AuthenticatorTransportFuture, Base64URLString, COSEAlgorithmIdentifier, CredentialDeviceType, Crypto, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialParameters, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON, UserVerificationRequirement, } from '@simplewebauthn/types';
1
+ export type { AttestationConveyancePreference, AuthenticationExtensionsClientInputs, AuthenticationResponseJSON, AuthenticatorSelectionCriteria, AuthenticatorTransportFuture, Base64URLString, COSEAlgorithmIdentifier, CredentialDeviceType, Crypto, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialParameters, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON, UserVerificationRequirement, WebAuthnCredential, } from '@simplewebauthn/types';
2
2
  export * as tinyCbor from '@levischuck/tiny-cbor';
3
3
  export { default as base64 } from '@hexagon/base64';
4
4
  export { fetch as crossFetch } from 'cross-fetch';
5
- export { AsnParser, AsnSerializer } from '@peculiar/asn1-schema';
6
- export { AuthorityKeyIdentifier, BasicConstraints, Certificate, CertificateList, CRLDistributionPoints, ExtendedKeyUsage, id_ce_authorityKeyIdentifier, id_ce_basicConstraints, id_ce_cRLDistributionPoints, id_ce_extKeyUsage, id_ce_subjectAltName, id_ce_subjectKeyIdentifier, Name, SubjectAlternativeName, SubjectKeyIdentifier, } from '@peculiar/asn1-x509';
5
+ export { AsnParser, AsnSerializer, OctetString } from '@peculiar/asn1-schema';
6
+ export { AuthorityKeyIdentifier, BasicConstraints, Certificate, CertificateList, CRLDistributionPoints, ExtendedKeyUsage, Extensions, id_ce_authorityKeyIdentifier, id_ce_basicConstraints, id_ce_cRLDistributionPoints, id_ce_extKeyUsage, id_ce_subjectAltName, id_ce_subjectKeyIdentifier, Name, SubjectAlternativeName, SubjectKeyIdentifier, } from '@peculiar/asn1-x509';
7
7
  export { ECDSASigValue, ECParameters, id_ecPublicKey, id_secp256r1, id_secp384r1, } from '@peculiar/asn1-ecc';
8
8
  export { RSAPublicKey } from '@peculiar/asn1-rsa';
9
9
  export { id_ce_keyDescription, KeyDescription } from '@peculiar/asn1-android';
package/esm/deps.js CHANGED
@@ -5,8 +5,8 @@ export { default as base64 } from '@hexagon/base64';
5
5
  // cross-fetch
6
6
  export { fetch as crossFetch } from 'cross-fetch';
7
7
  // @peculiar libraries
8
- export { AsnParser, AsnSerializer } from '@peculiar/asn1-schema';
9
- export { AuthorityKeyIdentifier, BasicConstraints, Certificate, CertificateList, CRLDistributionPoints, ExtendedKeyUsage, id_ce_authorityKeyIdentifier, id_ce_basicConstraints, id_ce_cRLDistributionPoints, id_ce_extKeyUsage, id_ce_subjectAltName, id_ce_subjectKeyIdentifier, Name, SubjectAlternativeName, SubjectKeyIdentifier, } from '@peculiar/asn1-x509';
8
+ export { AsnParser, AsnSerializer, OctetString } from '@peculiar/asn1-schema';
9
+ export { AuthorityKeyIdentifier, BasicConstraints, Certificate, CertificateList, CRLDistributionPoints, ExtendedKeyUsage, Extensions, id_ce_authorityKeyIdentifier, id_ce_basicConstraints, id_ce_cRLDistributionPoints, id_ce_extKeyUsage, id_ce_subjectAltName, id_ce_subjectKeyIdentifier, Name, SubjectAlternativeName, SubjectKeyIdentifier, } from '@peculiar/asn1-x509';
10
10
  export { ECDSASigValue, ECParameters, id_ecPublicKey, id_secp256r1, id_secp384r1, } from '@peculiar/asn1-ecc';
11
11
  export { RSAPublicKey } from '@peculiar/asn1-rsa';
12
12
  export { id_ce_keyDescription, KeyDescription } from '@peculiar/asn1-android';
@@ -4,17 +4,7 @@
4
4
  * @param extensionData Authenticator Extension Data buffer
5
5
  */
6
6
  export declare function decodeAuthenticatorExtensions(extensionData: Uint8Array): AuthenticationExtensionsAuthenticatorOutputs | undefined;
7
- export type AuthenticationExtensionsAuthenticatorOutputs = {
8
- devicePubKey?: DevicePublicKeyAuthenticatorOutput;
9
- uvm?: UVMAuthenticatorOutput;
10
- };
11
- export type DevicePublicKeyAuthenticatorOutput = {
12
- dpk?: Uint8Array;
13
- sig?: string;
14
- nonce?: Uint8Array;
15
- scope?: Uint8Array;
16
- aaguid?: Uint8Array;
17
- };
18
- export type UVMAuthenticatorOutput = {
19
- uvm?: Uint8Array[];
20
- };
7
+ /**
8
+ * Attempt to support authenticator extensions we might not know about in WebAuthn
9
+ */
10
+ export type AuthenticationExtensionsAuthenticatorOutputs = unknown;
@@ -0,0 +1,6 @@
1
+ import { Extensions } from '../deps.js';
2
+ /**
3
+ * Look for the id-fido-gen-ce-aaguid certificate extension. If it's present then check it against
4
+ * the attestation statement AAGUID.
5
+ */
6
+ export declare function validateExtFIDOGenCEAAGUID(certExtensions: Extensions | undefined, aaguid: Uint8Array): boolean;
@@ -0,0 +1,34 @@
1
+ import { AsnParser, OctetString } from '../deps.js';
2
+ import { isoUint8Array } from './iso/index.js';
3
+ /**
4
+ * Attestation Certificate Extension OID: `id-fido-gen-ce-aaguid`
5
+ *
6
+ * Sourced from https://fidoalliance.org/specs/fido-v2.0-ps-20150904/fido-key-attestation-v2.0-ps-20150904.html#verifying-an-attestation-statement
7
+ */
8
+ const id_fido_gen_ce_aaguid = '1.3.6.1.4.1.45724.1.1.4';
9
+ /**
10
+ * Look for the id-fido-gen-ce-aaguid certificate extension. If it's present then check it against
11
+ * the attestation statement AAGUID.
12
+ */
13
+ export function validateExtFIDOGenCEAAGUID(certExtensions, aaguid) {
14
+ // The certificate had no extensions so there's nothing to validate
15
+ if (!certExtensions) {
16
+ return true;
17
+ }
18
+ const extFIDOGenCEAAGUID = certExtensions.find((ext) => ext.extnID === id_fido_gen_ce_aaguid);
19
+ // The extension isn't present so there's nothing to validate
20
+ if (!extFIDOGenCEAAGUID) {
21
+ return true;
22
+ }
23
+ // Parse the extension value
24
+ const parsedExtFIDOGenCEAAGUID = AsnParser.parse(extFIDOGenCEAAGUID.extnValue, OctetString);
25
+ const extValue = new Uint8Array(parsedExtFIDOGenCEAAGUID.buffer);
26
+ // Compare the two values
27
+ const aaguidAndExtAreEqual = isoUint8Array.areEqual(aaguid, extValue);
28
+ if (!aaguidAndExtAreEqual) {
29
+ const _debugExtHex = isoUint8Array.toHex(extValue);
30
+ const _debugAAGUIDHex = isoUint8Array.toHex(aaguid);
31
+ throw new Error(`Certificate extension id-fido-gen-ce-aaguid (${id_fido_gen_ce_aaguid}) value of "${_debugExtHex}" was present but not equal to attestation statement AAGUID value of "${_debugAAGUIDHex}"`);
32
+ }
33
+ return true;
34
+ }
@@ -109,6 +109,10 @@ export const TPM_MANUFACTURERS = {
109
109
  name: 'IBM',
110
110
  id: 'IBM',
111
111
  },
112
+ 'id:49424D00': {
113
+ name: 'IBM',
114
+ id: 'IBM',
115
+ },
112
116
  'id:49465800': {
113
117
  name: 'Infineon',
114
118
  id: 'IFX',
@@ -7,6 +7,7 @@ import { validateCertificatePath } from '../../../helpers/validateCertificatePat
7
7
  import { getCertificateInfo } from '../../../helpers/getCertificateInfo.js';
8
8
  import { verifySignature } from '../../../helpers/verifySignature.js';
9
9
  import { isoUint8Array } from '../../../helpers/iso/index.js';
10
+ import { validateExtFIDOGenCEAAGUID } from '../../../helpers/validateExtFIDOGenCEAAGUID.js';
10
11
  import { MetadataService } from '../../../services/metadataService.js';
11
12
  import { verifyAttestationWithMetadata } from '../../../metadata/verifyAttestationWithMetadata.js';
12
13
  import { TPM_ECC_CURVE_COSE_CRV_MAP, TPM_MANUFACTURERS } from './constants.js';
@@ -206,8 +207,14 @@ export async function verifyAttestationTPM(options) {
206
207
  if (extKeyUsage[0] !== '2.23.133.8.3') {
207
208
  throw new Error(`Unexpected extKeyUsage "${extKeyUsage[0]}", expected "2.23.133.8.3" (TPM)`);
208
209
  }
209
- // TODO: If certificate contains id-fido-gen-ce-aaguid(1.3.6.1.4.1.45724.1.1.4) extension, check
210
- // that it’s value is set to the same AAGUID as in authData.
210
+ // Validate attestation statement AAGUID against leaf cert AAGUID
211
+ try {
212
+ await validateExtFIDOGenCEAAGUID(parsedCert.tbsCertificate.extensions, aaguid);
213
+ }
214
+ catch (err) {
215
+ const _err = err;
216
+ throw new Error(`${_err.message} (TPM)`);
217
+ }
211
218
  // Run some metadata checks if a statement exists for this authenticator
212
219
  const statement = await MetadataService.getStatement(aaguid);
213
220
  if (statement) {
@@ -4,6 +4,7 @@ import { validateCertificatePath } from '../../helpers/validateCertificatePath.j
4
4
  import { getCertificateInfo } from '../../helpers/getCertificateInfo.js';
5
5
  import { verifySignature } from '../../helpers/verifySignature.js';
6
6
  import { isoUint8Array } from '../../helpers/iso/index.js';
7
+ import { validateExtFIDOGenCEAAGUID } from '../../helpers/validateExtFIDOGenCEAAGUID.js';
7
8
  import { MetadataService } from '../../services/metadataService.js';
8
9
  import { verifyAttestationWithMetadata } from '../../metadata/verifyAttestationWithMetadata.js';
9
10
  /**
@@ -26,7 +27,7 @@ export async function verifyAttestationPacked(options) {
26
27
  const signatureBase = isoUint8Array.concat([authData, clientDataHash]);
27
28
  let verified = false;
28
29
  if (x5c) {
29
- const { subject, basicConstraintsCA, version, notBefore, notAfter } = getCertificateInfo(x5c[0]);
30
+ const { subject, basicConstraintsCA, version, notBefore, notAfter, parsedCertificate, } = getCertificateInfo(x5c[0]);
30
31
  const { OU, CN, O, C } = subject;
31
32
  if (OU !== 'Authenticator Attestation') {
32
33
  throw new Error('Certificate OU was not "Authenticator Attestation" (Packed|Full)');
@@ -54,8 +55,14 @@ export async function verifyAttestationPacked(options) {
54
55
  if (notAfter < now) {
55
56
  throw new Error(`Certificate not good after "${notAfter.toString()}" (Packed|Full)`);
56
57
  }
57
- // TODO: If certificate contains id-fido-gen-ce-aaguid(1.3.6.1.4.1.45724.1.1.4) extension, check
58
- // that it’s value is set to the same AAGUID as in authData.
58
+ // Validate attestation statement AAGUID against leaf cert AAGUID
59
+ try {
60
+ await validateExtFIDOGenCEAAGUID(parsedCertificate.tbsCertificate.extensions, aaguid);
61
+ }
62
+ catch (err) {
63
+ const _err = err;
64
+ throw new Error(`${_err.message} (Packed|Full)`);
65
+ }
59
66
  // If available, validate attestation alg and x5c with info in the metadata statement
60
67
  const statement = await MetadataService.getStatement(aaguid);
61
68
  if (statement) {
@@ -1,4 +1,4 @@
1
- import type { Base64URLString, COSEAlgorithmIdentifier, CredentialDeviceType, RegistrationResponseJSON } from '../deps.js';
1
+ import type { COSEAlgorithmIdentifier, CredentialDeviceType, RegistrationResponseJSON, WebAuthnCredential } from '../deps.js';
2
2
  import { AttestationFormat, AttestationStatement } from '../helpers/decodeAttestationObject.js';
3
3
  import { AuthenticationExtensionsAuthenticatorOutputs } from '../helpers/decodeAuthenticatorExtensions.js';
4
4
  export type VerifyRegistrationResponseOpts = {
@@ -7,6 +7,7 @@ export type VerifyRegistrationResponseOpts = {
7
7
  expectedOrigin: string | string[];
8
8
  expectedRPID?: string | string[];
9
9
  expectedType?: string | string[];
10
+ requireUserPresence?: boolean;
10
11
  requireUserVerification?: boolean;
11
12
  supportedAlgorithmIDs?: COSEAlgorithmIdentifier[];
12
13
  };
@@ -20,6 +21,7 @@ export type VerifyRegistrationResponseOpts = {
20
21
  * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
21
22
  * @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
22
23
  * @param expectedType **(Optional)** - The response type expected ('webauthn.create')
24
+ * @param requireUserPresence **(Optional)** - Enforce user presence by the authenticator (or skip it during auto registration) Defaults to `true`
23
25
  * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
24
26
  * @param supportedAlgorithmIDs **(Optional)** - Array of numeric COSE algorithm identifiers supported for attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms. Defaults to all supported algorithm IDs
25
27
  */
@@ -54,10 +56,8 @@ export type VerifiedRegistrationResponse = {
54
56
  verified: boolean;
55
57
  registrationInfo?: {
56
58
  fmt: AttestationFormat;
57
- counter: number;
58
59
  aaguid: string;
59
- credentialID: Base64URLString;
60
- credentialPublicKey: Uint8Array;
60
+ credential: WebAuthnCredential;
61
61
  credentialType: 'public-key';
62
62
  attestationObject: Uint8Array;
63
63
  userVerified: boolean;
@@ -26,11 +26,12 @@ import { verifyAttestationApple } from './verifications/verifyAttestationApple.j
26
26
  * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
27
27
  * @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
28
28
  * @param expectedType **(Optional)** - The response type expected ('webauthn.create')
29
+ * @param requireUserPresence **(Optional)** - Enforce user presence by the authenticator (or skip it during auto registration) Defaults to `true`
29
30
  * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
30
31
  * @param supportedAlgorithmIDs **(Optional)** - Array of numeric COSE algorithm identifiers supported for attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms. Defaults to all supported algorithm IDs
31
32
  */
32
33
  export async function verifyRegistrationResponse(options) {
33
- const { response, expectedChallenge, expectedOrigin, expectedRPID, expectedType, requireUserVerification = true, supportedAlgorithmIDs = supportedCOSEAlgorithmIdentifiers, } = options;
34
+ const { response, expectedChallenge, expectedOrigin, expectedRPID, expectedType, requireUserPresence = true, requireUserVerification = true, supportedAlgorithmIDs = supportedCOSEAlgorithmIdentifiers, } = options;
34
35
  const { id, rawId, type: credentialType, response: attestationResponse } = response;
35
36
  // Ensure credential specified an ID
36
37
  if (!id) {
@@ -109,12 +110,12 @@ export async function verifyRegistrationResponse(options) {
109
110
  matchedRPID = await matchExpectedRPID(rpIdHash, expectedRPIDs);
110
111
  }
111
112
  // Make sure someone was physically present
112
- if (!flags.up) {
113
- throw new Error('User not present during registration');
113
+ if (requireUserPresence && !flags.up) {
114
+ throw new Error('User presence was required, but user was not present');
114
115
  }
115
116
  // Enforce user verification if specified
116
117
  if (requireUserVerification && !flags.uv) {
117
- throw new Error('User verification required, but user could not be verified');
118
+ throw new Error('User verification was required, but user could not be verified');
118
119
  }
119
120
  if (!credentialID) {
120
121
  throw new Error('No credential ID was provided by authenticator');
@@ -189,11 +190,14 @@ export async function verifyRegistrationResponse(options) {
189
190
  const { credentialDeviceType, credentialBackedUp } = parseBackupFlags(flags);
190
191
  toReturn.registrationInfo = {
191
192
  fmt,
192
- counter,
193
193
  aaguid: convertAAGUIDToString(aaguid),
194
- credentialID: isoBase64URL.fromBuffer(credentialID),
195
- credentialPublicKey,
196
194
  credentialType,
195
+ credential: {
196
+ id: isoBase64URL.fromBuffer(credentialID),
197
+ publicKey: credentialPublicKey,
198
+ counter,
199
+ transports: response.response.transports,
200
+ },
197
201
  attestationObject,
198
202
  userVerified: flags.uv,
199
203
  credentialDeviceType,
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "module": "./esm/index.js",
3
3
  "main": "./script/index.js",
4
4
  "name": "@simplewebauthn/server",
5
- "version": "10.0.1",
5
+ "version": "11.0.0",
6
6
  "description": "SimpleWebAuthn for Servers",
7
7
  "license": "MIT",
8
8
  "author": "Matthew Miller <matthew@millerti.me>",
@@ -56,7 +56,7 @@
56
56
  "@peculiar/asn1-rsa": "^2.3.8",
57
57
  "@peculiar/asn1-schema": "^2.3.8",
58
58
  "@peculiar/asn1-x509": "^2.3.8",
59
- "@simplewebauthn/types": "^10.0.0",
59
+ "@simplewebauthn/types": "^11.0.0",
60
60
  "cross-fetch": "^4.0.0"
61
61
  },
62
62
  "devDependencies": {
@@ -1,11 +1,11 @@
1
- import type { AuthenticationResponseJSON, AuthenticatorDevice, Base64URLString, CredentialDeviceType, UserVerificationRequirement } from '../deps.js';
1
+ import type { AuthenticationResponseJSON, Base64URLString, CredentialDeviceType, UserVerificationRequirement, WebAuthnCredential } from '../deps.js';
2
2
  import { AuthenticationExtensionsAuthenticatorOutputs } from '../helpers/decodeAuthenticatorExtensions.js';
3
3
  export type VerifyAuthenticationResponseOpts = {
4
4
  response: AuthenticationResponseJSON;
5
5
  expectedChallenge: string | ((challenge: string) => boolean | Promise<boolean>);
6
6
  expectedOrigin: string | string[];
7
7
  expectedRPID: string | string[];
8
- authenticator: AuthenticatorDevice;
8
+ credential: WebAuthnCredential;
9
9
  expectedType?: string | string[];
10
10
  requireUserVerification?: boolean;
11
11
  advancedFIDOConfig?: {
@@ -21,7 +21,7 @@ export type VerifyAuthenticationResponseOpts = {
21
21
  * @param expectedChallenge - The base64url-encoded `options.challenge` returned by `generateAuthenticationOptions()`
22
22
  * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
23
23
  * @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
24
- * @param authenticator - An internal {@link AuthenticatorDevice} matching the credential's ID
24
+ * @param credential - An internal {@link WebAuthnCredential} corresponding to `id` in the authentication response
25
25
  * @param expectedType **(Optional)** - The response type expected ('webauthn.get')
26
26
  * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
27
27
  * @param advancedFIDOConfig **(Optional)** - Options for satisfying more stringent FIDO RP feature requirements
@@ -17,14 +17,14 @@ const index_js_1 = require("../helpers/iso/index.js");
17
17
  * @param expectedChallenge - The base64url-encoded `options.challenge` returned by `generateAuthenticationOptions()`
18
18
  * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
19
19
  * @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
20
- * @param authenticator - An internal {@link AuthenticatorDevice} matching the credential's ID
20
+ * @param credential - An internal {@link WebAuthnCredential} corresponding to `id` in the authentication response
21
21
  * @param expectedType **(Optional)** - The response type expected ('webauthn.get')
22
22
  * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
23
23
  * @param advancedFIDOConfig **(Optional)** - Options for satisfying more stringent FIDO RP feature requirements
24
24
  * @param advancedFIDOConfig.userVerification **(Optional)** - Enable alternative rules for evaluating the User Presence and User Verified flags in authenticator data: UV (and UP) flags are optional unless this value is `"required"`
25
25
  */
26
26
  async function verifyAuthenticationResponse(options) {
27
- const { response, expectedChallenge, expectedOrigin, expectedRPID, expectedType, authenticator, requireUserVerification = true, advancedFIDOConfig, } = options;
27
+ const { response, expectedChallenge, expectedOrigin, expectedRPID, expectedType, credential, requireUserVerification = true, advancedFIDOConfig, } = options;
28
28
  const { id, rawId, type: credentialType, response: assertionResponse } = response;
29
29
  // Ensure credential specified an ID
30
30
  if (!id) {
@@ -144,24 +144,24 @@ async function verifyAuthenticationResponse(options) {
144
144
  const clientDataHash = await (0, toHash_js_1.toHash)(index_js_1.isoBase64URL.toBuffer(assertionResponse.clientDataJSON));
145
145
  const signatureBase = index_js_1.isoUint8Array.concat([authDataBuffer, clientDataHash]);
146
146
  const signature = index_js_1.isoBase64URL.toBuffer(assertionResponse.signature);
147
- if ((counter > 0 || authenticator.counter > 0) &&
148
- counter <= authenticator.counter) {
147
+ if ((counter > 0 || credential.counter > 0) &&
148
+ counter <= credential.counter) {
149
149
  // Error out when the counter in the DB is greater than or equal to the counter in the
150
150
  // dataStruct. It's related to how the authenticator maintains the number of times its been
151
151
  // used for this client. If this happens, then someone's somehow increased the counter
152
152
  // on the device without going through this site
153
- throw new Error(`Response counter value ${counter} was lower than expected ${authenticator.counter}`);
153
+ throw new Error(`Response counter value ${counter} was lower than expected ${credential.counter}`);
154
154
  }
155
155
  const { credentialDeviceType, credentialBackedUp } = (0, parseBackupFlags_js_1.parseBackupFlags)(flags);
156
156
  const toReturn = {
157
157
  verified: await (0, verifySignature_js_1.verifySignature)({
158
158
  signature,
159
159
  data: signatureBase,
160
- credentialPublicKey: authenticator.credentialPublicKey,
160
+ credentialPublicKey: credential.publicKey,
161
161
  }),
162
162
  authenticationInfo: {
163
163
  newCounter: counter,
164
- credentialID: authenticator.credentialID,
164
+ credentialID: credential.id,
165
165
  userVerified: flags.uv,
166
166
  credentialDeviceType,
167
167
  credentialBackedUp,
package/script/deps.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- export type { AttestationConveyancePreference, AuthenticationExtensionsClientInputs, AuthenticationResponseJSON, AuthenticatorDevice, AuthenticatorSelectionCriteria, AuthenticatorTransportFuture, Base64URLString, COSEAlgorithmIdentifier, CredentialDeviceType, Crypto, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialParameters, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON, UserVerificationRequirement, } from '@simplewebauthn/types';
1
+ export type { AttestationConveyancePreference, AuthenticationExtensionsClientInputs, AuthenticationResponseJSON, AuthenticatorSelectionCriteria, AuthenticatorTransportFuture, Base64URLString, COSEAlgorithmIdentifier, CredentialDeviceType, Crypto, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialParameters, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON, UserVerificationRequirement, WebAuthnCredential, } from '@simplewebauthn/types';
2
2
  export * as tinyCbor from '@levischuck/tiny-cbor';
3
3
  export { default as base64 } from '@hexagon/base64';
4
4
  export { fetch as crossFetch } from 'cross-fetch';
5
- export { AsnParser, AsnSerializer } from '@peculiar/asn1-schema';
6
- export { AuthorityKeyIdentifier, BasicConstraints, Certificate, CertificateList, CRLDistributionPoints, ExtendedKeyUsage, id_ce_authorityKeyIdentifier, id_ce_basicConstraints, id_ce_cRLDistributionPoints, id_ce_extKeyUsage, id_ce_subjectAltName, id_ce_subjectKeyIdentifier, Name, SubjectAlternativeName, SubjectKeyIdentifier, } from '@peculiar/asn1-x509';
5
+ export { AsnParser, AsnSerializer, OctetString } from '@peculiar/asn1-schema';
6
+ export { AuthorityKeyIdentifier, BasicConstraints, Certificate, CertificateList, CRLDistributionPoints, ExtendedKeyUsage, Extensions, id_ce_authorityKeyIdentifier, id_ce_basicConstraints, id_ce_cRLDistributionPoints, id_ce_extKeyUsage, id_ce_subjectAltName, id_ce_subjectKeyIdentifier, Name, SubjectAlternativeName, SubjectKeyIdentifier, } from '@peculiar/asn1-x509';
7
7
  export { ECDSASigValue, ECParameters, id_ecPublicKey, id_secp256r1, id_secp384r1, } from '@peculiar/asn1-ecc';
8
8
  export { RSAPublicKey } from '@peculiar/asn1-rsa';
9
9
  export { id_ce_keyDescription, KeyDescription } from '@peculiar/asn1-android';
package/script/deps.js CHANGED
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.KeyDescription = exports.id_ce_keyDescription = exports.RSAPublicKey = exports.id_secp384r1 = exports.id_secp256r1 = exports.id_ecPublicKey = exports.ECParameters = exports.ECDSASigValue = exports.SubjectKeyIdentifier = exports.SubjectAlternativeName = exports.Name = exports.id_ce_subjectKeyIdentifier = exports.id_ce_subjectAltName = exports.id_ce_extKeyUsage = exports.id_ce_cRLDistributionPoints = exports.id_ce_basicConstraints = exports.id_ce_authorityKeyIdentifier = exports.ExtendedKeyUsage = exports.CRLDistributionPoints = exports.CertificateList = exports.Certificate = exports.BasicConstraints = exports.AuthorityKeyIdentifier = exports.AsnSerializer = exports.AsnParser = exports.crossFetch = exports.base64 = exports.tinyCbor = void 0;
29
+ exports.KeyDescription = exports.id_ce_keyDescription = exports.RSAPublicKey = exports.id_secp384r1 = exports.id_secp256r1 = exports.id_ecPublicKey = exports.ECParameters = exports.ECDSASigValue = exports.SubjectKeyIdentifier = exports.SubjectAlternativeName = exports.Name = exports.id_ce_subjectKeyIdentifier = exports.id_ce_subjectAltName = exports.id_ce_extKeyUsage = exports.id_ce_cRLDistributionPoints = exports.id_ce_basicConstraints = exports.id_ce_authorityKeyIdentifier = exports.Extensions = exports.ExtendedKeyUsage = exports.CRLDistributionPoints = exports.CertificateList = exports.Certificate = exports.BasicConstraints = exports.AuthorityKeyIdentifier = exports.OctetString = exports.AsnSerializer = exports.AsnParser = exports.crossFetch = exports.base64 = exports.tinyCbor = void 0;
30
30
  // tiny_cbor (a.k.a. tiny-cbor in Node land)
31
31
  exports.tinyCbor = __importStar(require("@levischuck/tiny-cbor"));
32
32
  // b64 (a.k.a. @hexagon/base64 in Node land)
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "crossFetch", { enumerable: true, get: function (
39
39
  var asn1_schema_1 = require("@peculiar/asn1-schema");
40
40
  Object.defineProperty(exports, "AsnParser", { enumerable: true, get: function () { return asn1_schema_1.AsnParser; } });
41
41
  Object.defineProperty(exports, "AsnSerializer", { enumerable: true, get: function () { return asn1_schema_1.AsnSerializer; } });
42
+ Object.defineProperty(exports, "OctetString", { enumerable: true, get: function () { return asn1_schema_1.OctetString; } });
42
43
  var asn1_x509_1 = require("@peculiar/asn1-x509");
43
44
  Object.defineProperty(exports, "AuthorityKeyIdentifier", { enumerable: true, get: function () { return asn1_x509_1.AuthorityKeyIdentifier; } });
44
45
  Object.defineProperty(exports, "BasicConstraints", { enumerable: true, get: function () { return asn1_x509_1.BasicConstraints; } });
@@ -46,6 +47,7 @@ Object.defineProperty(exports, "Certificate", { enumerable: true, get: function
46
47
  Object.defineProperty(exports, "CertificateList", { enumerable: true, get: function () { return asn1_x509_1.CertificateList; } });
47
48
  Object.defineProperty(exports, "CRLDistributionPoints", { enumerable: true, get: function () { return asn1_x509_1.CRLDistributionPoints; } });
48
49
  Object.defineProperty(exports, "ExtendedKeyUsage", { enumerable: true, get: function () { return asn1_x509_1.ExtendedKeyUsage; } });
50
+ Object.defineProperty(exports, "Extensions", { enumerable: true, get: function () { return asn1_x509_1.Extensions; } });
49
51
  Object.defineProperty(exports, "id_ce_authorityKeyIdentifier", { enumerable: true, get: function () { return asn1_x509_1.id_ce_authorityKeyIdentifier; } });
50
52
  Object.defineProperty(exports, "id_ce_basicConstraints", { enumerable: true, get: function () { return asn1_x509_1.id_ce_basicConstraints; } });
51
53
  Object.defineProperty(exports, "id_ce_cRLDistributionPoints", { enumerable: true, get: function () { return asn1_x509_1.id_ce_cRLDistributionPoints; } });
@@ -4,17 +4,7 @@
4
4
  * @param extensionData Authenticator Extension Data buffer
5
5
  */
6
6
  export declare function decodeAuthenticatorExtensions(extensionData: Uint8Array): AuthenticationExtensionsAuthenticatorOutputs | undefined;
7
- export type AuthenticationExtensionsAuthenticatorOutputs = {
8
- devicePubKey?: DevicePublicKeyAuthenticatorOutput;
9
- uvm?: UVMAuthenticatorOutput;
10
- };
11
- export type DevicePublicKeyAuthenticatorOutput = {
12
- dpk?: Uint8Array;
13
- sig?: string;
14
- nonce?: Uint8Array;
15
- scope?: Uint8Array;
16
- aaguid?: Uint8Array;
17
- };
18
- export type UVMAuthenticatorOutput = {
19
- uvm?: Uint8Array[];
20
- };
7
+ /**
8
+ * Attempt to support authenticator extensions we might not know about in WebAuthn
9
+ */
10
+ export type AuthenticationExtensionsAuthenticatorOutputs = unknown;
@@ -0,0 +1,6 @@
1
+ import { Extensions } from '../deps.js';
2
+ /**
3
+ * Look for the id-fido-gen-ce-aaguid certificate extension. If it's present then check it against
4
+ * the attestation statement AAGUID.
5
+ */
6
+ export declare function validateExtFIDOGenCEAAGUID(certExtensions: Extensions | undefined, aaguid: Uint8Array): boolean;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateExtFIDOGenCEAAGUID = void 0;
4
+ const deps_js_1 = require("../deps.js");
5
+ const index_js_1 = require("./iso/index.js");
6
+ /**
7
+ * Attestation Certificate Extension OID: `id-fido-gen-ce-aaguid`
8
+ *
9
+ * Sourced from https://fidoalliance.org/specs/fido-v2.0-ps-20150904/fido-key-attestation-v2.0-ps-20150904.html#verifying-an-attestation-statement
10
+ */
11
+ const id_fido_gen_ce_aaguid = '1.3.6.1.4.1.45724.1.1.4';
12
+ /**
13
+ * Look for the id-fido-gen-ce-aaguid certificate extension. If it's present then check it against
14
+ * the attestation statement AAGUID.
15
+ */
16
+ function validateExtFIDOGenCEAAGUID(certExtensions, aaguid) {
17
+ // The certificate had no extensions so there's nothing to validate
18
+ if (!certExtensions) {
19
+ return true;
20
+ }
21
+ const extFIDOGenCEAAGUID = certExtensions.find((ext) => ext.extnID === id_fido_gen_ce_aaguid);
22
+ // The extension isn't present so there's nothing to validate
23
+ if (!extFIDOGenCEAAGUID) {
24
+ return true;
25
+ }
26
+ // Parse the extension value
27
+ const parsedExtFIDOGenCEAAGUID = deps_js_1.AsnParser.parse(extFIDOGenCEAAGUID.extnValue, deps_js_1.OctetString);
28
+ const extValue = new Uint8Array(parsedExtFIDOGenCEAAGUID.buffer);
29
+ // Compare the two values
30
+ const aaguidAndExtAreEqual = index_js_1.isoUint8Array.areEqual(aaguid, extValue);
31
+ if (!aaguidAndExtAreEqual) {
32
+ const _debugExtHex = index_js_1.isoUint8Array.toHex(extValue);
33
+ const _debugAAGUIDHex = index_js_1.isoUint8Array.toHex(aaguid);
34
+ throw new Error(`Certificate extension id-fido-gen-ce-aaguid (${id_fido_gen_ce_aaguid}) value of "${_debugExtHex}" was present but not equal to attestation statement AAGUID value of "${_debugAAGUIDHex}"`);
35
+ }
36
+ return true;
37
+ }
38
+ exports.validateExtFIDOGenCEAAGUID = validateExtFIDOGenCEAAGUID;
@@ -112,6 +112,10 @@ exports.TPM_MANUFACTURERS = {
112
112
  name: 'IBM',
113
113
  id: 'IBM',
114
114
  },
115
+ 'id:49424D00': {
116
+ name: 'IBM',
117
+ id: 'IBM',
118
+ },
115
119
  'id:49465800': {
116
120
  name: 'Infineon',
117
121
  id: 'IFX',
@@ -10,6 +10,7 @@ const validateCertificatePath_js_1 = require("../../../helpers/validateCertifica
10
10
  const getCertificateInfo_js_1 = require("../../../helpers/getCertificateInfo.js");
11
11
  const verifySignature_js_1 = require("../../../helpers/verifySignature.js");
12
12
  const index_js_1 = require("../../../helpers/iso/index.js");
13
+ const validateExtFIDOGenCEAAGUID_js_1 = require("../../../helpers/validateExtFIDOGenCEAAGUID.js");
13
14
  const metadataService_js_1 = require("../../../services/metadataService.js");
14
15
  const verifyAttestationWithMetadata_js_1 = require("../../../metadata/verifyAttestationWithMetadata.js");
15
16
  const constants_js_1 = require("./constants.js");
@@ -209,8 +210,14 @@ async function verifyAttestationTPM(options) {
209
210
  if (extKeyUsage[0] !== '2.23.133.8.3') {
210
211
  throw new Error(`Unexpected extKeyUsage "${extKeyUsage[0]}", expected "2.23.133.8.3" (TPM)`);
211
212
  }
212
- // TODO: If certificate contains id-fido-gen-ce-aaguid(1.3.6.1.4.1.45724.1.1.4) extension, check
213
- // that it’s value is set to the same AAGUID as in authData.
213
+ // Validate attestation statement AAGUID against leaf cert AAGUID
214
+ try {
215
+ await (0, validateExtFIDOGenCEAAGUID_js_1.validateExtFIDOGenCEAAGUID)(parsedCert.tbsCertificate.extensions, aaguid);
216
+ }
217
+ catch (err) {
218
+ const _err = err;
219
+ throw new Error(`${_err.message} (TPM)`);
220
+ }
214
221
  // Run some metadata checks if a statement exists for this authenticator
215
222
  const statement = await metadataService_js_1.MetadataService.getStatement(aaguid);
216
223
  if (statement) {
@@ -7,6 +7,7 @@ const validateCertificatePath_js_1 = require("../../helpers/validateCertificateP
7
7
  const getCertificateInfo_js_1 = require("../../helpers/getCertificateInfo.js");
8
8
  const verifySignature_js_1 = require("../../helpers/verifySignature.js");
9
9
  const index_js_1 = require("../../helpers/iso/index.js");
10
+ const validateExtFIDOGenCEAAGUID_js_1 = require("../../helpers/validateExtFIDOGenCEAAGUID.js");
10
11
  const metadataService_js_1 = require("../../services/metadataService.js");
11
12
  const verifyAttestationWithMetadata_js_1 = require("../../metadata/verifyAttestationWithMetadata.js");
12
13
  /**
@@ -29,7 +30,7 @@ async function verifyAttestationPacked(options) {
29
30
  const signatureBase = index_js_1.isoUint8Array.concat([authData, clientDataHash]);
30
31
  let verified = false;
31
32
  if (x5c) {
32
- const { subject, basicConstraintsCA, version, notBefore, notAfter } = (0, getCertificateInfo_js_1.getCertificateInfo)(x5c[0]);
33
+ const { subject, basicConstraintsCA, version, notBefore, notAfter, parsedCertificate, } = (0, getCertificateInfo_js_1.getCertificateInfo)(x5c[0]);
33
34
  const { OU, CN, O, C } = subject;
34
35
  if (OU !== 'Authenticator Attestation') {
35
36
  throw new Error('Certificate OU was not "Authenticator Attestation" (Packed|Full)');
@@ -57,8 +58,14 @@ async function verifyAttestationPacked(options) {
57
58
  if (notAfter < now) {
58
59
  throw new Error(`Certificate not good after "${notAfter.toString()}" (Packed|Full)`);
59
60
  }
60
- // TODO: If certificate contains id-fido-gen-ce-aaguid(1.3.6.1.4.1.45724.1.1.4) extension, check
61
- // that it’s value is set to the same AAGUID as in authData.
61
+ // Validate attestation statement AAGUID against leaf cert AAGUID
62
+ try {
63
+ await (0, validateExtFIDOGenCEAAGUID_js_1.validateExtFIDOGenCEAAGUID)(parsedCertificate.tbsCertificate.extensions, aaguid);
64
+ }
65
+ catch (err) {
66
+ const _err = err;
67
+ throw new Error(`${_err.message} (Packed|Full)`);
68
+ }
62
69
  // If available, validate attestation alg and x5c with info in the metadata statement
63
70
  const statement = await metadataService_js_1.MetadataService.getStatement(aaguid);
64
71
  if (statement) {
@@ -1,4 +1,4 @@
1
- import type { Base64URLString, COSEAlgorithmIdentifier, CredentialDeviceType, RegistrationResponseJSON } from '../deps.js';
1
+ import type { COSEAlgorithmIdentifier, CredentialDeviceType, RegistrationResponseJSON, WebAuthnCredential } from '../deps.js';
2
2
  import { AttestationFormat, AttestationStatement } from '../helpers/decodeAttestationObject.js';
3
3
  import { AuthenticationExtensionsAuthenticatorOutputs } from '../helpers/decodeAuthenticatorExtensions.js';
4
4
  export type VerifyRegistrationResponseOpts = {
@@ -7,6 +7,7 @@ export type VerifyRegistrationResponseOpts = {
7
7
  expectedOrigin: string | string[];
8
8
  expectedRPID?: string | string[];
9
9
  expectedType?: string | string[];
10
+ requireUserPresence?: boolean;
10
11
  requireUserVerification?: boolean;
11
12
  supportedAlgorithmIDs?: COSEAlgorithmIdentifier[];
12
13
  };
@@ -20,6 +21,7 @@ export type VerifyRegistrationResponseOpts = {
20
21
  * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
21
22
  * @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
22
23
  * @param expectedType **(Optional)** - The response type expected ('webauthn.create')
24
+ * @param requireUserPresence **(Optional)** - Enforce user presence by the authenticator (or skip it during auto registration) Defaults to `true`
23
25
  * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
24
26
  * @param supportedAlgorithmIDs **(Optional)** - Array of numeric COSE algorithm identifiers supported for attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms. Defaults to all supported algorithm IDs
25
27
  */
@@ -54,10 +56,8 @@ export type VerifiedRegistrationResponse = {
54
56
  verified: boolean;
55
57
  registrationInfo?: {
56
58
  fmt: AttestationFormat;
57
- counter: number;
58
59
  aaguid: string;
59
- credentialID: Base64URLString;
60
- credentialPublicKey: Uint8Array;
60
+ credential: WebAuthnCredential;
61
61
  credentialType: 'public-key';
62
62
  attestationObject: Uint8Array;
63
63
  userVerified: boolean;
@@ -29,11 +29,12 @@ const verifyAttestationApple_js_1 = require("./verifications/verifyAttestationAp
29
29
  * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
30
30
  * @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
31
31
  * @param expectedType **(Optional)** - The response type expected ('webauthn.create')
32
+ * @param requireUserPresence **(Optional)** - Enforce user presence by the authenticator (or skip it during auto registration) Defaults to `true`
32
33
  * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
33
34
  * @param supportedAlgorithmIDs **(Optional)** - Array of numeric COSE algorithm identifiers supported for attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms. Defaults to all supported algorithm IDs
34
35
  */
35
36
  async function verifyRegistrationResponse(options) {
36
- const { response, expectedChallenge, expectedOrigin, expectedRPID, expectedType, requireUserVerification = true, supportedAlgorithmIDs = generateRegistrationOptions_js_1.supportedCOSEAlgorithmIdentifiers, } = options;
37
+ const { response, expectedChallenge, expectedOrigin, expectedRPID, expectedType, requireUserPresence = true, requireUserVerification = true, supportedAlgorithmIDs = generateRegistrationOptions_js_1.supportedCOSEAlgorithmIdentifiers, } = options;
37
38
  const { id, rawId, type: credentialType, response: attestationResponse } = response;
38
39
  // Ensure credential specified an ID
39
40
  if (!id) {
@@ -112,12 +113,12 @@ async function verifyRegistrationResponse(options) {
112
113
  matchedRPID = await (0, matchExpectedRPID_js_1.matchExpectedRPID)(rpIdHash, expectedRPIDs);
113
114
  }
114
115
  // Make sure someone was physically present
115
- if (!flags.up) {
116
- throw new Error('User not present during registration');
116
+ if (requireUserPresence && !flags.up) {
117
+ throw new Error('User presence was required, but user was not present');
117
118
  }
118
119
  // Enforce user verification if specified
119
120
  if (requireUserVerification && !flags.uv) {
120
- throw new Error('User verification required, but user could not be verified');
121
+ throw new Error('User verification was required, but user could not be verified');
121
122
  }
122
123
  if (!credentialID) {
123
124
  throw new Error('No credential ID was provided by authenticator');
@@ -192,11 +193,14 @@ async function verifyRegistrationResponse(options) {
192
193
  const { credentialDeviceType, credentialBackedUp } = (0, parseBackupFlags_js_1.parseBackupFlags)(flags);
193
194
  toReturn.registrationInfo = {
194
195
  fmt,
195
- counter,
196
196
  aaguid: (0, convertAAGUIDToString_js_1.convertAAGUIDToString)(aaguid),
197
- credentialID: index_js_1.isoBase64URL.fromBuffer(credentialID),
198
- credentialPublicKey,
199
197
  credentialType,
198
+ credential: {
199
+ id: index_js_1.isoBase64URL.fromBuffer(credentialID),
200
+ publicKey: credentialPublicKey,
201
+ counter,
202
+ transports: response.response.transports,
203
+ },
200
204
  attestationObject,
201
205
  userVerified: flags.uv,
202
206
  credentialDeviceType,