@simplewebauthn/server 9.0.2 → 10.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.
Files changed (54) hide show
  1. package/README.md +2 -2
  2. package/esm/authentication/generateAuthenticationOptions.d.ts +16 -14
  3. package/esm/authentication/generateAuthenticationOptions.js +21 -16
  4. package/esm/authentication/verifyAuthenticationResponse.d.ts +13 -18
  5. package/esm/authentication/verifyAuthenticationResponse.js +12 -17
  6. package/esm/deps.d.ts +2 -2
  7. package/esm/deps.js +2 -2
  8. package/esm/helpers/convertCertBufferToPEM.js +1 -1
  9. package/esm/helpers/decodeClientDataJSON.d.ts +2 -1
  10. package/esm/helpers/decodeClientDataJSON.js +1 -1
  11. package/esm/helpers/generateUserID.d.ts +7 -0
  12. package/esm/helpers/generateUserID.js +17 -0
  13. package/esm/helpers/index.d.ts +2 -1
  14. package/esm/helpers/index.js +2 -1
  15. package/esm/helpers/iso/isoBase64URL.d.ts +10 -5
  16. package/esm/helpers/iso/isoBase64URL.js +13 -7
  17. package/esm/helpers/iso/isoCBOR.d.ts +12 -1
  18. package/esm/helpers/iso/isoCBOR.js +4 -17
  19. package/esm/helpers/iso/isoCrypto/getWebCrypto.d.ts +0 -289
  20. package/esm/helpers/iso/isoCrypto/getWebCrypto.js +26 -41
  21. package/esm/helpers/parseAuthenticatorData.js +10 -1
  22. package/esm/metadata/parseJWT.js +2 -2
  23. package/esm/registration/generateRegistrationOptions.d.ts +19 -19
  24. package/esm/registration/generateRegistrationOptions.js +40 -22
  25. package/esm/registration/verifications/verifyAttestationAndroidSafetyNet.js +2 -2
  26. package/esm/registration/verifyRegistrationResponse.d.ts +9 -12
  27. package/esm/registration/verifyRegistrationResponse.js +8 -11
  28. package/package.json +4 -4
  29. package/script/authentication/generateAuthenticationOptions.d.ts +16 -14
  30. package/script/authentication/generateAuthenticationOptions.js +21 -16
  31. package/script/authentication/verifyAuthenticationResponse.d.ts +13 -18
  32. package/script/authentication/verifyAuthenticationResponse.js +12 -17
  33. package/script/deps.d.ts +2 -2
  34. package/script/deps.js +3 -3
  35. package/script/helpers/convertCertBufferToPEM.js +1 -1
  36. package/script/helpers/decodeClientDataJSON.d.ts +2 -1
  37. package/script/helpers/decodeClientDataJSON.js +1 -1
  38. package/script/helpers/generateUserID.d.ts +7 -0
  39. package/script/helpers/generateUserID.js +21 -0
  40. package/script/helpers/index.d.ts +2 -1
  41. package/script/helpers/index.js +3 -1
  42. package/script/helpers/iso/isoBase64URL.d.ts +10 -5
  43. package/script/helpers/iso/isoBase64URL.js +18 -11
  44. package/script/helpers/iso/isoCBOR.d.ts +12 -1
  45. package/script/helpers/iso/isoCBOR.js +3 -16
  46. package/script/helpers/iso/isoCrypto/getWebCrypto.d.ts +0 -288
  47. package/script/helpers/iso/isoCrypto/getWebCrypto.js +26 -64
  48. package/script/helpers/parseAuthenticatorData.js +10 -1
  49. package/script/metadata/parseJWT.js +2 -2
  50. package/script/registration/generateRegistrationOptions.d.ts +19 -19
  51. package/script/registration/generateRegistrationOptions.js +40 -22
  52. package/script/registration/verifications/verifyAttestationAndroidSafetyNet.js +2 -2
  53. package/script/registration/verifyRegistrationResponse.d.ts +9 -12
  54. package/script/registration/verifyRegistrationResponse.js +8 -11
@@ -23,8 +23,8 @@ export async function verifyAttestationAndroidSafetyNet(options) {
23
23
  // Prepare to verify a JWT
24
24
  const jwt = isoUint8Array.toUTF8String(response);
25
25
  const jwtParts = jwt.split('.');
26
- const HEADER = JSON.parse(isoBase64URL.toString(jwtParts[0]));
27
- const PAYLOAD = JSON.parse(isoBase64URL.toString(jwtParts[1]));
26
+ const HEADER = JSON.parse(isoBase64URL.toUTF8String(jwtParts[0]));
27
+ const PAYLOAD = JSON.parse(isoBase64URL.toUTF8String(jwtParts[1]));
28
28
  const SIGNATURE = jwtParts[2];
29
29
  /**
30
30
  * START Verify PAYLOAD
@@ -1,4 +1,4 @@
1
- import type { COSEAlgorithmIdentifier, CredentialDeviceType, RegistrationResponseJSON } from '../deps.js';
1
+ import type { Base64URLString, COSEAlgorithmIdentifier, CredentialDeviceType, RegistrationResponseJSON } 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 = {
@@ -15,16 +15,13 @@ export type VerifyRegistrationResponseOpts = {
15
15
  *
16
16
  * **Options:**
17
17
  *
18
- * @param response Response returned by **@simplewebauthn/browser**'s `startAuthentication()`
19
- * @param expectedChallenge The base64url-encoded `options.challenge` returned by
20
- * `generateRegistrationOptions()`
21
- * @param expectedOrigin Website URL (or array of URLs) that the registration should have occurred on
22
- * @param expectedRPID RP ID (or array of IDs) that was specified in the registration options
23
- * @param expectedType (Optional) The response type expected ('webauthn.create')
24
- * @param requireUserVerification (Optional) Enforce user verification by the authenticator
25
- * (via PIN, fingerprint, etc...)
26
- * @param supportedAlgorithmIDs Array of numeric COSE algorithm identifiers supported for
27
- * attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms
18
+ * @param response - Response returned by **@simplewebauthn/browser**'s `startAuthentication()`
19
+ * @param expectedChallenge - The base64url-encoded `options.challenge` returned by `generateRegistrationOptions()`
20
+ * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
21
+ * @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
22
+ * @param expectedType **(Optional)** - The response type expected ('webauthn.create')
23
+ * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
24
+ * @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
28
25
  */
29
26
  export declare function verifyRegistrationResponse(options: VerifyRegistrationResponseOpts): Promise<VerifiedRegistrationResponse>;
30
27
  /**
@@ -59,7 +56,7 @@ export type VerifiedRegistrationResponse = {
59
56
  fmt: AttestationFormat;
60
57
  counter: number;
61
58
  aaguid: string;
62
- credentialID: Uint8Array;
59
+ credentialID: Base64URLString;
63
60
  credentialPublicKey: Uint8Array;
64
61
  credentialType: 'public-key';
65
62
  attestationObject: Uint8Array;
@@ -21,16 +21,13 @@ import { verifyAttestationApple } from './verifications/verifyAttestationApple.j
21
21
  *
22
22
  * **Options:**
23
23
  *
24
- * @param response Response returned by **@simplewebauthn/browser**'s `startAuthentication()`
25
- * @param expectedChallenge The base64url-encoded `options.challenge` returned by
26
- * `generateRegistrationOptions()`
27
- * @param expectedOrigin Website URL (or array of URLs) that the registration should have occurred on
28
- * @param expectedRPID RP ID (or array of IDs) that was specified in the registration options
29
- * @param expectedType (Optional) The response type expected ('webauthn.create')
30
- * @param requireUserVerification (Optional) Enforce user verification by the authenticator
31
- * (via PIN, fingerprint, etc...)
32
- * @param supportedAlgorithmIDs Array of numeric COSE algorithm identifiers supported for
33
- * attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms
24
+ * @param response - Response returned by **@simplewebauthn/browser**'s `startAuthentication()`
25
+ * @param expectedChallenge - The base64url-encoded `options.challenge` returned by `generateRegistrationOptions()`
26
+ * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
27
+ * @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
28
+ * @param expectedType **(Optional)** - The response type expected ('webauthn.create')
29
+ * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
30
+ * @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
31
  */
35
32
  export async function verifyRegistrationResponse(options) {
36
33
  const { response, expectedChallenge, expectedOrigin, expectedRPID, expectedType, requireUserVerification = true, supportedAlgorithmIDs = supportedCOSEAlgorithmIdentifiers, } = options;
@@ -194,7 +191,7 @@ export async function verifyRegistrationResponse(options) {
194
191
  fmt,
195
192
  counter,
196
193
  aaguid: convertAAGUIDToString(aaguid),
197
- credentialID,
194
+ credentialID: isoBase64URL.fromBuffer(credentialID),
198
195
  credentialPublicKey,
199
196
  credentialType,
200
197
  attestationObject,
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": "9.0.2",
5
+ "version": "10.0.0",
6
6
  "description": "SimpleWebAuthn for Servers",
7
7
  "license": "MIT",
8
8
  "author": "Matthew Miller <matthew@millerti.me>",
@@ -16,7 +16,7 @@
16
16
  "access": "public"
17
17
  },
18
18
  "engines": {
19
- "node": ">=16.0.0"
19
+ "node": ">=20.0.0"
20
20
  },
21
21
  "bugs": {
22
22
  "url": "https://github.com/MasterKale/SimpleWebAuthn/issues"
@@ -50,13 +50,13 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@hexagon/base64": "^1.1.27",
53
+ "@levischuck/tiny-cbor": "^0.2.2",
53
54
  "@peculiar/asn1-android": "^2.3.10",
54
55
  "@peculiar/asn1-ecc": "^2.3.8",
55
56
  "@peculiar/asn1-rsa": "^2.3.8",
56
57
  "@peculiar/asn1-schema": "^2.3.8",
57
58
  "@peculiar/asn1-x509": "^2.3.8",
58
- "@simplewebauthn/types": "^9.0.1",
59
- "cbor-x": "^1.5.2",
59
+ "@simplewebauthn/types": "^10.0.0",
60
60
  "cross-fetch": "^4.0.0"
61
61
  },
62
62
  "devDependencies": {
@@ -1,23 +1,25 @@
1
- import type { AuthenticationExtensionsClientInputs, PublicKeyCredentialDescriptorFuture, PublicKeyCredentialRequestOptionsJSON, UserVerificationRequirement } from '../deps.js';
1
+ import type { AuthenticationExtensionsClientInputs, AuthenticatorTransportFuture, Base64URLString, PublicKeyCredentialRequestOptionsJSON, UserVerificationRequirement } from '../deps.js';
2
2
  export type GenerateAuthenticationOptionsOpts = {
3
- allowCredentials?: PublicKeyCredentialDescriptorFuture[];
3
+ rpID: string;
4
+ allowCredentials?: {
5
+ id: Base64URLString;
6
+ transports?: AuthenticatorTransportFuture[];
7
+ }[];
4
8
  challenge?: string | Uint8Array;
5
9
  timeout?: number;
6
10
  userVerification?: UserVerificationRequirement;
7
11
  extensions?: AuthenticationExtensionsClientInputs;
8
- rpID?: string;
9
12
  };
10
13
  /**
11
- * Prepare a value to pass into navigator.credentials.get(...) for authenticator "login"
14
+ * Prepare a value to pass into navigator.credentials.get(...) for authenticator authentication
12
15
  *
13
- * @param allowCredentials Authenticators previously registered by the user, if any. If undefined
14
- * the client will ask the user which credential they want to use
15
- * @param challenge Random value the authenticator needs to sign and pass back
16
- * user for authentication
17
- * @param timeout How long (in ms) the user can take to complete authentication
18
- * @param userVerification Set to `'discouraged'` when asserting as part of a 2FA flow, otherwise
19
- * set to `'preferred'` or `'required'` as desired.
20
- * @param extensions Additional plugins the authenticator or browser should use during authentication
21
- * @param rpID Valid domain name (after `https://`)
16
+ * **Options:**
17
+ *
18
+ * @param rpID - Valid domain name (after `https://`)
19
+ * @param allowCredentials **(Optional)** - Authenticators previously registered by the user, if any. If undefined the client will ask the user which credential they want to use
20
+ * @param challenge **(Optional)** - Random value the authenticator needs to sign and pass back user for authentication. Defaults to generating a random value
21
+ * @param timeout **(Optional)** - How long (in ms) the user can take to complete authentication. Defaults to `60000`
22
+ * @param userVerification **(Optional)** - Set to `'discouraged'` when asserting as part of a 2FA flow, otherwise set to `'preferred'` or `'required'` as desired. Defaults to `"preferred"`
23
+ * @param extensions **(Optional)** - Additional plugins the authenticator or browser should use during authentication
22
24
  */
23
- export declare function generateAuthenticationOptions(options?: GenerateAuthenticationOptionsOpts): Promise<PublicKeyCredentialRequestOptionsJSON>;
25
+ export declare function generateAuthenticationOptions(options: GenerateAuthenticationOptionsOpts): Promise<PublicKeyCredentialRequestOptionsJSON>;
@@ -4,19 +4,18 @@ exports.generateAuthenticationOptions = void 0;
4
4
  const index_js_1 = require("../helpers/iso/index.js");
5
5
  const generateChallenge_js_1 = require("../helpers/generateChallenge.js");
6
6
  /**
7
- * Prepare a value to pass into navigator.credentials.get(...) for authenticator "login"
7
+ * Prepare a value to pass into navigator.credentials.get(...) for authenticator authentication
8
8
  *
9
- * @param allowCredentials Authenticators previously registered by the user, if any. If undefined
10
- * the client will ask the user which credential they want to use
11
- * @param challenge Random value the authenticator needs to sign and pass back
12
- * user for authentication
13
- * @param timeout How long (in ms) the user can take to complete authentication
14
- * @param userVerification Set to `'discouraged'` when asserting as part of a 2FA flow, otherwise
15
- * set to `'preferred'` or `'required'` as desired.
16
- * @param extensions Additional plugins the authenticator or browser should use during authentication
17
- * @param rpID Valid domain name (after `https://`)
9
+ * **Options:**
10
+ *
11
+ * @param rpID - Valid domain name (after `https://`)
12
+ * @param allowCredentials **(Optional)** - Authenticators previously registered by the user, if any. If undefined the client will ask the user which credential they want to use
13
+ * @param challenge **(Optional)** - Random value the authenticator needs to sign and pass back user for authentication. Defaults to generating a random value
14
+ * @param timeout **(Optional)** - How long (in ms) the user can take to complete authentication. Defaults to `60000`
15
+ * @param userVerification **(Optional)** - Set to `'discouraged'` when asserting as part of a 2FA flow, otherwise set to `'preferred'` or `'required'` as desired. Defaults to `"preferred"`
16
+ * @param extensions **(Optional)** - Additional plugins the authenticator or browser should use during authentication
18
17
  */
19
- async function generateAuthenticationOptions(options = {}) {
18
+ async function generateAuthenticationOptions(options) {
20
19
  const { allowCredentials, challenge = await (0, generateChallenge_js_1.generateChallenge)(), timeout = 60000, userVerification = 'preferred', extensions, rpID, } = options;
21
20
  /**
22
21
  * Preserve ability to specify `string` values for challenges
@@ -26,15 +25,21 @@ async function generateAuthenticationOptions(options = {}) {
26
25
  _challenge = index_js_1.isoUint8Array.fromUTF8String(_challenge);
27
26
  }
28
27
  return {
28
+ rpId: rpID,
29
29
  challenge: index_js_1.isoBase64URL.fromBuffer(_challenge),
30
- allowCredentials: allowCredentials?.map((cred) => ({
31
- ...cred,
32
- id: index_js_1.isoBase64URL.fromBuffer(cred.id),
33
- })),
30
+ allowCredentials: allowCredentials?.map((cred) => {
31
+ if (!index_js_1.isoBase64URL.isBase64URL(cred.id)) {
32
+ throw new Error(`excludeCredential id "${cred.id}" is not a valid base64url string`);
33
+ }
34
+ return {
35
+ ...cred,
36
+ id: index_js_1.isoBase64URL.trimPadding(cred.id),
37
+ type: 'public-key',
38
+ };
39
+ }),
34
40
  timeout,
35
41
  userVerification,
36
42
  extensions,
37
- rpId: rpID,
38
43
  };
39
44
  }
40
45
  exports.generateAuthenticationOptions = generateAuthenticationOptions;
@@ -1,36 +1,31 @@
1
- import type { AuthenticationResponseJSON, AuthenticatorDevice, CredentialDeviceType, UserVerificationRequirement } from '../deps.js';
1
+ import type { AuthenticationResponseJSON, AuthenticatorDevice, Base64URLString, CredentialDeviceType, UserVerificationRequirement } 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
- expectedType?: string | string[];
9
8
  authenticator: AuthenticatorDevice;
9
+ expectedType?: string | string[];
10
10
  requireUserVerification?: boolean;
11
11
  advancedFIDOConfig?: {
12
12
  userVerification?: UserVerificationRequirement;
13
13
  };
14
14
  };
15
15
  /**
16
- * Verify that the user has legitimately completed the login process
16
+ * Verify that the user has legitimately completed the authentication process
17
17
  *
18
18
  * **Options:**
19
19
  *
20
- * @param response Response returned by **@simplewebauthn/browser**'s `startAssertion()`
21
- * @param expectedChallenge The base64url-encoded `options.challenge` returned by
22
- * `generateAuthenticationOptions()`
23
- * @param expectedOrigin Website URL (or array of URLs) that the registration should have occurred on
24
- * @param expectedRPID RP ID (or array of IDs) that was specified in the registration options
25
- * @param expectedType (Optional) The response type expected ('webauthn.get')
26
- * @param authenticator An internal {@link AuthenticatorDevice} matching the credential's ID
27
- * @param requireUserVerification (Optional) Enforce user verification by the authenticator
28
- * (via PIN, fingerprint, etc...)
29
- * @param advancedFIDOConfig (Optional) Options for satisfying more stringent FIDO RP feature
30
- * requirements
31
- * @param advancedFIDOConfig.userVerification (Optional) Enable alternative rules for evaluating the
32
- * User Presence and User Verified flags in authenticator data: UV (and UP) flags are optional
33
- * unless this value is `"required"`
20
+ * @param response - Response returned by **@simplewebauthn/browser**'s `startAssertion()`
21
+ * @param expectedChallenge - The base64url-encoded `options.challenge` returned by `generateAuthenticationOptions()`
22
+ * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
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
25
+ * @param expectedType **(Optional)** - The response type expected ('webauthn.get')
26
+ * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
27
+ * @param advancedFIDOConfig **(Optional)** - Options for satisfying more stringent FIDO RP feature requirements
28
+ * @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"`
34
29
  */
35
30
  export declare function verifyAuthenticationResponse(options: VerifyAuthenticationResponseOpts): Promise<VerifiedAuthenticationResponse>;
36
31
  /**
@@ -56,7 +51,7 @@ export declare function verifyAuthenticationResponse(options: VerifyAuthenticati
56
51
  export type VerifiedAuthenticationResponse = {
57
52
  verified: boolean;
58
53
  authenticationInfo: {
59
- credentialID: Uint8Array;
54
+ credentialID: Base64URLString;
60
55
  newCounter: number;
61
56
  userVerified: boolean;
62
57
  credentialDeviceType: CredentialDeviceType;
@@ -9,24 +9,19 @@ const parseBackupFlags_js_1 = require("../helpers/parseBackupFlags.js");
9
9
  const matchExpectedRPID_js_1 = require("../helpers/matchExpectedRPID.js");
10
10
  const index_js_1 = require("../helpers/iso/index.js");
11
11
  /**
12
- * Verify that the user has legitimately completed the login process
12
+ * Verify that the user has legitimately completed the authentication process
13
13
  *
14
14
  * **Options:**
15
15
  *
16
- * @param response Response returned by **@simplewebauthn/browser**'s `startAssertion()`
17
- * @param expectedChallenge The base64url-encoded `options.challenge` returned by
18
- * `generateAuthenticationOptions()`
19
- * @param expectedOrigin Website URL (or array of URLs) that the registration should have occurred on
20
- * @param expectedRPID RP ID (or array of IDs) that was specified in the registration options
21
- * @param expectedType (Optional) The response type expected ('webauthn.get')
22
- * @param authenticator An internal {@link AuthenticatorDevice} matching the credential's ID
23
- * @param requireUserVerification (Optional) Enforce user verification by the authenticator
24
- * (via PIN, fingerprint, etc...)
25
- * @param advancedFIDOConfig (Optional) Options for satisfying more stringent FIDO RP feature
26
- * requirements
27
- * @param advancedFIDOConfig.userVerification (Optional) Enable alternative rules for evaluating the
28
- * User Presence and User Verified flags in authenticator data: UV (and UP) flags are optional
29
- * unless this value is `"required"`
16
+ * @param response - Response returned by **@simplewebauthn/browser**'s `startAssertion()`
17
+ * @param expectedChallenge - The base64url-encoded `options.challenge` returned by `generateAuthenticationOptions()`
18
+ * @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
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
21
+ * @param expectedType **(Optional)** - The response type expected ('webauthn.get')
22
+ * @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
23
+ * @param advancedFIDOConfig **(Optional)** - Options for satisfying more stringent FIDO RP feature requirements
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"`
30
25
  */
31
26
  async function verifyAuthenticationResponse(options) {
32
27
  const { response, expectedChallenge, expectedOrigin, expectedRPID, expectedType, authenticator, requireUserVerification = true, advancedFIDOConfig, } = options;
@@ -87,10 +82,10 @@ async function verifyAuthenticationResponse(options) {
87
82
  throw new Error(`Unexpected authentication response origin "${origin}", expected "${expectedOrigin}"`);
88
83
  }
89
84
  }
90
- if (!index_js_1.isoBase64URL.isBase64url(assertionResponse.authenticatorData)) {
85
+ if (!index_js_1.isoBase64URL.isBase64URL(assertionResponse.authenticatorData)) {
91
86
  throw new Error('Credential response authenticatorData was not a base64url string');
92
87
  }
93
- if (!index_js_1.isoBase64URL.isBase64url(assertionResponse.signature)) {
88
+ if (!index_js_1.isoBase64URL.isBase64URL(assertionResponse.signature)) {
94
89
  throw new Error('Credential response signature was not a base64url string');
95
90
  }
96
91
  if (assertionResponse.userHandle &&
package/script/deps.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export type { AttestationConveyancePreference, AuthenticationExtensionsClientInputs, AuthenticationResponseJSON, AuthenticatorDevice, AuthenticatorSelectionCriteria, Base64URLString, COSEAlgorithmIdentifier, CredentialDeviceType, Crypto, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialDescriptorFuture, PublicKeyCredentialParameters, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON, UserVerificationRequirement, } from '@simplewebauthn/types';
2
- export * as cborx from 'cbor-x/index-no-eval';
1
+ export type { AttestationConveyancePreference, AuthenticationExtensionsClientInputs, AuthenticationResponseJSON, AuthenticatorDevice, AuthenticatorSelectionCriteria, AuthenticatorTransportFuture, Base64URLString, COSEAlgorithmIdentifier, CredentialDeviceType, Crypto, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialParameters, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON, UserVerificationRequirement, } from '@simplewebauthn/types';
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
5
  export { AsnParser, AsnSerializer } from '@peculiar/asn1-schema';
package/script/deps.js CHANGED
@@ -26,9 +26,9 @@ 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.cborx = void 0;
30
- // cbor (a.k.a. cbor-x in Node land)
31
- exports.cborx = __importStar(require("cbor-x/index-no-eval"));
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;
30
+ // tiny_cbor (a.k.a. tiny-cbor in Node land)
31
+ exports.tinyCbor = __importStar(require("@levischuck/tiny-cbor"));
32
32
  // b64 (a.k.a. @hexagon/base64 in Node land)
33
33
  var base64_1 = require("@hexagon/base64");
34
34
  Object.defineProperty(exports, "base64", { enumerable: true, get: function () { return __importDefault(base64_1).default; } });
@@ -11,7 +11,7 @@ function convertCertBufferToPEM(certBuffer) {
11
11
  * Get certBuffer to a base64 representation
12
12
  */
13
13
  if (typeof certBuffer === 'string') {
14
- if (index_js_1.isoBase64URL.isBase64url(certBuffer)) {
14
+ if (index_js_1.isoBase64URL.isBase64URL(certBuffer)) {
15
15
  b64cert = index_js_1.isoBase64URL.toBase64(certBuffer);
16
16
  }
17
17
  else if (index_js_1.isoBase64URL.isBase64(certBuffer)) {
@@ -1,7 +1,8 @@
1
+ import type { Base64URLString } from '../deps.js';
1
2
  /**
2
3
  * Decode an authenticator's base64url-encoded clientDataJSON to JSON
3
4
  */
4
- export declare function decodeClientDataJSON(data: string): ClientDataJSON;
5
+ export declare function decodeClientDataJSON(data: Base64URLString): ClientDataJSON;
5
6
  export type ClientDataJSON = {
6
7
  type: string;
7
8
  challenge: string;
@@ -6,7 +6,7 @@ const index_js_1 = require("./iso/index.js");
6
6
  * Decode an authenticator's base64url-encoded clientDataJSON to JSON
7
7
  */
8
8
  function decodeClientDataJSON(data) {
9
- const toString = index_js_1.isoBase64URL.toString(data);
9
+ const toString = index_js_1.isoBase64URL.toUTF8String(data);
10
10
  const clientData = JSON.parse(toString);
11
11
  return exports._decodeClientDataJSONInternals.stubThis(clientData);
12
12
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Generate a suitably random value to be used as user ID
3
+ */
4
+ export declare function generateUserID(): Promise<Uint8Array>;
5
+ export declare const _generateUserIDInternals: {
6
+ stubThis: (value: Uint8Array) => Uint8Array;
7
+ };
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports._generateUserIDInternals = exports.generateUserID = void 0;
4
+ const index_js_1 = require("./iso/index.js");
5
+ /**
6
+ * Generate a suitably random value to be used as user ID
7
+ */
8
+ async function generateUserID() {
9
+ /**
10
+ * WebAuthn spec says user.id has a max length of 64 bytes. I prefer how 32 random bytes look
11
+ * after they're base64url-encoded so I'm choosing to go with that here.
12
+ */
13
+ const newUserID = new Uint8Array(32);
14
+ await index_js_1.isoCrypto.getRandomValues(newUserID);
15
+ return exports._generateUserIDInternals.stubThis(newUserID);
16
+ }
17
+ exports.generateUserID = generateUserID;
18
+ // Make it possible to stub the return value during testing
19
+ exports._generateUserIDInternals = {
20
+ stubThis: (value) => value,
21
+ };
@@ -5,6 +5,7 @@ import { decodeAttestationObject } from './decodeAttestationObject.js';
5
5
  import { decodeClientDataJSON } from './decodeClientDataJSON.js';
6
6
  import { decodeCredentialPublicKey } from './decodeCredentialPublicKey.js';
7
7
  import { generateChallenge } from './generateChallenge.js';
8
+ import { generateUserID } from './generateUserID.js';
8
9
  import { getCertificateInfo } from './getCertificateInfo.js';
9
10
  import { isCertRevoked } from './isCertRevoked.js';
10
11
  import { parseAuthenticatorData } from './parseAuthenticatorData.js';
@@ -13,7 +14,7 @@ import { validateCertificatePath } from './validateCertificatePath.js';
13
14
  import { verifySignature } from './verifySignature.js';
14
15
  import { isoBase64URL, isoCBOR, isoCrypto, isoUint8Array } from './iso/index.js';
15
16
  import * as cose from './cose.js';
16
- export { convertAAGUIDToString, convertCertBufferToPEM, convertCOSEtoPKCS, cose, decodeAttestationObject, decodeClientDataJSON, decodeCredentialPublicKey, generateChallenge, getCertificateInfo, isCertRevoked, isoBase64URL, isoCBOR, isoCrypto, isoUint8Array, parseAuthenticatorData, toHash, validateCertificatePath, verifySignature, };
17
+ export { convertAAGUIDToString, convertCertBufferToPEM, convertCOSEtoPKCS, cose, decodeAttestationObject, decodeClientDataJSON, decodeCredentialPublicKey, generateChallenge, generateUserID, getCertificateInfo, isCertRevoked, isoBase64URL, isoCBOR, isoCrypto, isoUint8Array, parseAuthenticatorData, toHash, validateCertificatePath, verifySignature, };
17
18
  import type { AttestationFormat, AttestationObject, AttestationStatement } from './decodeAttestationObject.js';
18
19
  import type { CertificateInfo } from './getCertificateInfo.js';
19
20
  import type { ClientDataJSON } from './decodeClientDataJSON.js';
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.verifySignature = exports.validateCertificatePath = exports.toHash = exports.parseAuthenticatorData = exports.isoUint8Array = exports.isoCrypto = exports.isoCBOR = exports.isoBase64URL = exports.isCertRevoked = exports.getCertificateInfo = exports.generateChallenge = exports.decodeCredentialPublicKey = exports.decodeClientDataJSON = exports.decodeAttestationObject = exports.cose = exports.convertCOSEtoPKCS = exports.convertCertBufferToPEM = exports.convertAAGUIDToString = void 0;
26
+ exports.verifySignature = exports.validateCertificatePath = exports.toHash = exports.parseAuthenticatorData = exports.isoUint8Array = exports.isoCrypto = exports.isoCBOR = exports.isoBase64URL = exports.isCertRevoked = exports.getCertificateInfo = exports.generateUserID = exports.generateChallenge = exports.decodeCredentialPublicKey = exports.decodeClientDataJSON = exports.decodeAttestationObject = exports.cose = exports.convertCOSEtoPKCS = exports.convertCertBufferToPEM = exports.convertAAGUIDToString = void 0;
27
27
  const convertAAGUIDToString_js_1 = require("./convertAAGUIDToString.js");
28
28
  Object.defineProperty(exports, "convertAAGUIDToString", { enumerable: true, get: function () { return convertAAGUIDToString_js_1.convertAAGUIDToString; } });
29
29
  const convertCertBufferToPEM_js_1 = require("./convertCertBufferToPEM.js");
@@ -38,6 +38,8 @@ const decodeCredentialPublicKey_js_1 = require("./decodeCredentialPublicKey.js")
38
38
  Object.defineProperty(exports, "decodeCredentialPublicKey", { enumerable: true, get: function () { return decodeCredentialPublicKey_js_1.decodeCredentialPublicKey; } });
39
39
  const generateChallenge_js_1 = require("./generateChallenge.js");
40
40
  Object.defineProperty(exports, "generateChallenge", { enumerable: true, get: function () { return generateChallenge_js_1.generateChallenge; } });
41
+ const generateUserID_js_1 = require("./generateUserID.js");
42
+ Object.defineProperty(exports, "generateUserID", { enumerable: true, get: function () { return generateUserID_js_1.generateUserID; } });
41
43
  const getCertificateInfo_js_1 = require("./getCertificateInfo.js");
42
44
  Object.defineProperty(exports, "getCertificateInfo", { enumerable: true, get: function () { return getCertificateInfo_js_1.getCertificateInfo; } });
43
45
  const isCertRevoked_js_1 = require("./isCertRevoked.js");
@@ -1,3 +1,4 @@
1
+ import type { Base64URLString } from '../../deps.js';
1
2
  /**
2
3
  * Decode from a Base64URL-encoded string to an ArrayBuffer. Best used when converting a
3
4
  * credential ID from a JSON string to an ArrayBuffer, like in allowCredentials or
@@ -20,13 +21,13 @@ export declare function fromBuffer(buffer: Uint8Array, to?: 'base64' | 'base64ur
20
21
  */
21
22
  export declare function toBase64(base64urlString: string): string;
22
23
  /**
23
- * Encode a string to base64url
24
+ * Encode a UTF-8 string to base64url
24
25
  */
25
- export declare function fromString(ascii: string): string;
26
+ export declare function fromUTF8String(utf8String: string): string;
26
27
  /**
27
- * Decode a base64url string into its original string
28
+ * Decode a base64url string into its original UTF-8 string
28
29
  */
29
- export declare function toString(base64urlString: string): string;
30
+ export declare function toUTF8String(base64urlString: string): string;
30
31
  /**
31
32
  * Confirm that the string is encoded into base64
32
33
  */
@@ -34,4 +35,8 @@ export declare function isBase64(input: string): boolean;
34
35
  /**
35
36
  * Confirm that the string is encoded into base64url, with support for optional padding
36
37
  */
37
- export declare function isBase64url(input: string): boolean;
38
+ export declare function isBase64URL(input: string): boolean;
39
+ /**
40
+ * Remove optional padding from a base64url-encoded string
41
+ */
42
+ export declare function trimPadding(input: Base64URLString): Base64URLString;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isBase64url = exports.isBase64 = exports.toString = exports.fromString = exports.toBase64 = exports.fromBuffer = exports.toBuffer = void 0;
3
+ exports.trimPadding = exports.isBase64URL = exports.isBase64 = exports.toUTF8String = exports.fromUTF8String = exports.toBase64 = exports.fromBuffer = exports.toBuffer = void 0;
4
4
  const deps_js_1 = require("../../deps.js");
5
5
  /**
6
6
  * Decode from a Base64URL-encoded string to an ArrayBuffer. Best used when converting a
@@ -36,19 +36,19 @@ function toBase64(base64urlString) {
36
36
  }
37
37
  exports.toBase64 = toBase64;
38
38
  /**
39
- * Encode a string to base64url
39
+ * Encode a UTF-8 string to base64url
40
40
  */
41
- function fromString(ascii) {
42
- return deps_js_1.base64.fromString(ascii, true);
41
+ function fromUTF8String(utf8String) {
42
+ return deps_js_1.base64.fromString(utf8String, true);
43
43
  }
44
- exports.fromString = fromString;
44
+ exports.fromUTF8String = fromUTF8String;
45
45
  /**
46
- * Decode a base64url string into its original string
46
+ * Decode a base64url string into its original UTF-8 string
47
47
  */
48
- function toString(base64urlString) {
48
+ function toUTF8String(base64urlString) {
49
49
  return deps_js_1.base64.toString(base64urlString, true);
50
50
  }
51
- exports.toString = toString;
51
+ exports.toUTF8String = toUTF8String;
52
52
  /**
53
53
  * Confirm that the string is encoded into base64
54
54
  */
@@ -59,9 +59,16 @@ exports.isBase64 = isBase64;
59
59
  /**
60
60
  * Confirm that the string is encoded into base64url, with support for optional padding
61
61
  */
62
- function isBase64url(input) {
62
+ function isBase64URL(input) {
63
63
  // Trim padding characters from the string if present
64
- input = input.replace(/=/g, '');
64
+ input = trimPadding(input);
65
65
  return deps_js_1.base64.validate(input, true);
66
66
  }
67
- exports.isBase64url = isBase64url;
67
+ exports.isBase64URL = isBase64URL;
68
+ /**
69
+ * Remove optional padding from a base64url-encoded string
70
+ */
71
+ function trimPadding(input) {
72
+ return input.replace(/=/g, '');
73
+ }
74
+ exports.trimPadding = trimPadding;
@@ -1,3 +1,14 @@
1
+ import { tinyCbor } from '../../deps.js';
2
+ /**
3
+ * Whatever CBOR encoder is used should keep CBOR data the same length when data is re-encoded
4
+ *
5
+ * MOST CRITICALLY, this means the following needs to be true of whatever CBOR library we use:
6
+ * - CBOR Map type values MUST decode to JavaScript Maps
7
+ * - CBOR tag 64 (uint8 Typed Array) MUST NOT be used when encoding Uint8Arrays back to CBOR
8
+ *
9
+ * So long as these requirements are maintained, then CBOR sequences can be encoded and decoded
10
+ * freely while maintaining their lengths for the most accurate pointer movement across them.
11
+ */
1
12
  /**
2
13
  * Decode and return the first item in a sequence of CBOR-encoded values
3
14
  *
@@ -9,4 +20,4 @@ export declare function decodeFirst<Type>(input: Uint8Array): Type;
9
20
  /**
10
21
  * Encode data to CBOR
11
22
  */
12
- export declare function encode(input: unknown): Uint8Array;
23
+ export declare function encode(input: tinyCbor.CBORType): Uint8Array;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.encode = exports.decodeFirst = void 0;
4
4
  const deps_js_1 = require("../../deps.js");
5
5
  /**
6
- * This encoder should keep CBOR data the same length when data is re-encoded
6
+ * Whatever CBOR encoder is used should keep CBOR data the same length when data is re-encoded
7
7
  *
8
8
  * MOST CRITICALLY, this means the following needs to be true of whatever CBOR library we use:
9
9
  * - CBOR Map type values MUST decode to JavaScript Maps
@@ -12,10 +12,6 @@ const deps_js_1 = require("../../deps.js");
12
12
  * So long as these requirements are maintained, then CBOR sequences can be encoded and decoded
13
13
  * freely while maintaining their lengths for the most accurate pointer movement across them.
14
14
  */
15
- const encoder = new deps_js_1.cborx.Encoder({
16
- mapsAsObjects: false,
17
- tagUint8Array: false,
18
- });
19
15
  /**
20
16
  * Decode and return the first item in a sequence of CBOR-encoded values
21
17
  *
@@ -26,16 +22,7 @@ const encoder = new deps_js_1.cborx.Encoder({
26
22
  function decodeFirst(input) {
27
23
  // Make a copy so we don't mutate the original
28
24
  const _input = new Uint8Array(input);
29
- const decoded = encoder.decodeMultiple(_input);
30
- if (decoded === undefined) {
31
- throw new Error('CBOR input data was empty');
32
- }
33
- /**
34
- * Typing on `decoded` is `void | []` which causes TypeScript to think that it's an empty array,
35
- * and thus you can't destructure it. I'm ignoring that because the code works fine in JS, and
36
- * so this should be a valid operation.
37
- */
38
- // @ts-ignore 2493
25
+ const decoded = deps_js_1.tinyCbor.decodePartialCBOR(_input, 0);
39
26
  const [first] = decoded;
40
27
  return first;
41
28
  }
@@ -44,6 +31,6 @@ exports.decodeFirst = decodeFirst;
44
31
  * Encode data to CBOR
45
32
  */
46
33
  function encode(input) {
47
- return encoder.encode(input);
34
+ return deps_js_1.tinyCbor.encodeCBOR(input);
48
35
  }
49
36
  exports.encode = encode;