diff --git a/HANDBOOK.md b/HANDBOOK.md index bc4dec346..86673d12a 100644 --- a/HANDBOOK.md +++ b/HANDBOOK.md @@ -28,7 +28,6 @@ Update `"version"` in the following **deno.json** files for each package that ne - **@simplewebauthn/browser**: [packages/browser/deno.json](./packages/browser/deno.json) - **@simplewebauthn/server**: [packages/server/deno.json](./packages/server/deno.json) -- **@simplewebauthn/types**: [packages/types/deno.json](./packages/types/deno.json) Continue using your best judgement on what an appropriate new version number should be. @@ -46,12 +45,6 @@ The following commands can be run from the root of the monorepo to build the res then **publish it to both [NPM](https://www.npmjs.com/search?q=%40simplewebauthn) and [JSR](https://jsr.io/@simplewebauthn)**. -#### Need to publish @simplewebauthn/types? - -``` -deno task publish:types -``` - #### Need to publish @simplewebauthn/browser? ``` @@ -60,12 +53,6 @@ deno task publish:browser #### Need to publish @simplewebauthn/server? -1. - - [ ] Make sure the correct version of **@simplewebauthn/types** is on NPM - - The `npm install` step that dnt performs while building **@simplewebauthn/server** pulls from - NPM. The build will fail if the version of **@simplewebauthn/types** specified in - [packages/types/deno.json](./packages/types/deno.json) is unavailable on NPM. - ``` deno task publish:server ``` diff --git a/README.md b/README.md index a70f696c8..6845834f3 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,6 @@ incorporate WebAuthn into a website. The following packages are maintained here: - [@simplewebauthn/server](https://github.com/MasterKale/SimpleWebAuthn/tree/master/packages/server) - [@simplewebauthn/browser](https://github.com/MasterKale/SimpleWebAuthn/tree/master/packages/browser) -An additional package is also included that contains shared TypeScript definitions: - -- [@simplewebauthn/types](https://github.com/MasterKale/SimpleWebAuthn/tree/master/packages/types/) - ## Installation SimpleWebAuthn can be installed from **[NPM](https://www.npmjs.com/search?q=%40simplewebauthn)** and diff --git a/deno.json b/deno.json index 09d5194b7..8fcc01c22 100644 --- a/deno.json +++ b/deno.json @@ -22,10 +22,19 @@ ] }, "test:coverage:collect": "deno test -A --coverage=cov_profile", - "build:types": "(cd packages/types; deno task build)", - "build:browser": "(cd packages/browser; deno task build)", - "build:server": "(cd packages/server; deno task build)", - "publish:types": "(cd packages/types; deno task publish)", + "codegen:types": "(cd packages/types; deno task codegen)", + "build:browser": { + "command": "(cd packages/browser; deno task build)", + "dependencies": [ + "codegen:types" + ] + }, + "build:server": { + "command": "(cd packages/server; deno task build)", + "dependencies": [ + "codegen:types" + ] + }, "publish:browser": "(cd packages/browser; deno task publish)", "publish:server": "(cd packages/server; deno task publish)" }, diff --git a/packages/browser/README.md b/packages/browser/README.md index c130edc95..81b92013a 100644 --- a/packages/browser/README.md +++ b/packages/browser/README.md @@ -22,13 +22,13 @@ and **[JSR](https://jsr.io/@simplewebauthn/browser)**: ### Node LTS 20.x and higher ```sh -npm install @simplewebauthn/browser @simplewebauthn/types +npm install @simplewebauthn/browser ``` ### Deno v1.43 and higher ```sh -deno add jsr:@simplewebauthn/browser jsr:@simplewebauthn/types +deno add jsr:@simplewebauthn/browser ``` ### UMD diff --git a/packages/browser/build_npm.ts b/packages/browser/build_npm.ts index 6aa5186ff..38fa32b47 100644 --- a/packages/browser/build_npm.ts +++ b/packages/browser/build_npm.ts @@ -57,10 +57,7 @@ async function buildESMAndCJS() { 'umd', ], unpkg: 'dist/bundle/index.umd.min.js', - dependencies: { - // Deno workspaces maps this identifier locally, make sure it's defined in the NPM package - '@simplewebauthn/types': `^${typesDenoJSON.version}`, - }, + dependencies: {}, }, // Map from Deno package to NPM package for Node build mappings: {}, diff --git a/packages/browser/deno.json b/packages/browser/deno.json index e5074712f..887023d57 100644 --- a/packages/browser/deno.json +++ b/packages/browser/deno.json @@ -11,6 +11,14 @@ }, "test": "deno test -A src/", "test:watch": "deno test -A --watch src/", + "docs:serve": { + "command": "deno run -A jsr:@std/http/file-server --host 127.0.0.1 docs/", + "dependencies": [ + "docs:html" + ] + }, + "docs:html": "deno doc --html src/index.ts", + "docs:lint": "deno doc --lint src/index.ts", "publish": { "command": "deno task publish:jsr && deno task publish:npm", "dependencies": [ diff --git a/packages/browser/src/helpers/browserSupportsWebAuthn.ts b/packages/browser/src/helpers/browserSupportsWebAuthn.ts index dc0b4c130..03673270e 100644 --- a/packages/browser/src/helpers/browserSupportsWebAuthn.ts +++ b/packages/browser/src/helpers/browserSupportsWebAuthn.ts @@ -8,7 +8,10 @@ export function browserSupportsWebAuthn(): boolean { ); } -// Make it possible to stub the return value during testing +/** + * Make it possible to stub the return value during testing + * @ignore Don't include this in docs output + */ export const _browserSupportsWebAuthnInternals = { stubThis: (value: boolean) => value, }; diff --git a/packages/browser/src/helpers/browserSupportsWebAuthnAutofill.ts b/packages/browser/src/helpers/browserSupportsWebAuthnAutofill.ts index d7409b43b..0e0ea990d 100644 --- a/packages/browser/src/helpers/browserSupportsWebAuthnAutofill.ts +++ b/packages/browser/src/helpers/browserSupportsWebAuthnAutofill.ts @@ -1,5 +1,4 @@ -import { PublicKeyCredentialFuture } from '@simplewebauthn/types'; - +import type { PublicKeyCredentialFuture } from '../types/index.ts'; import { browserSupportsWebAuthn } from './browserSupportsWebAuthn.ts'; /** diff --git a/packages/browser/src/helpers/toAuthenticatorAttachment.ts b/packages/browser/src/helpers/toAuthenticatorAttachment.ts index 93fc386f2..22d88bab8 100644 --- a/packages/browser/src/helpers/toAuthenticatorAttachment.ts +++ b/packages/browser/src/helpers/toAuthenticatorAttachment.ts @@ -1,4 +1,4 @@ -import { AuthenticatorAttachment } from '@simplewebauthn/types'; +import type { AuthenticatorAttachment } from '../types/index.ts'; const attachments: AuthenticatorAttachment[] = ['cross-platform', 'platform']; diff --git a/packages/browser/src/helpers/toPublicKeyCredentialDescriptor.ts b/packages/browser/src/helpers/toPublicKeyCredentialDescriptor.ts index d84ad5128..e19bbd9da 100644 --- a/packages/browser/src/helpers/toPublicKeyCredentialDescriptor.ts +++ b/packages/browser/src/helpers/toPublicKeyCredentialDescriptor.ts @@ -2,8 +2,7 @@ import type { AuthenticatorTransport, PublicKeyCredentialDescriptor, PublicKeyCredentialDescriptorJSON, -} from '@simplewebauthn/types'; - +} from '../types/index.ts'; import { base64URLStringToBuffer } from './base64URLStringToBuffer.ts'; export function toPublicKeyCredentialDescriptor( diff --git a/packages/browser/src/helpers/webAuthnAbortService.ts b/packages/browser/src/helpers/webAuthnAbortService.ts index 8ed915fc3..9cc68576f 100644 --- a/packages/browser/src/helpers/webAuthnAbortService.ts +++ b/packages/browser/src/helpers/webAuthnAbortService.ts @@ -1,16 +1,19 @@ interface WebAuthnAbortService { + /** + * Prepare an abort signal that will help support multiple auth attempts without needing to + * reload the page. This is automatically called whenever `startRegistration()` and + * `startAuthentication()` are called. + */ createNewAbortSignal(): AbortSignal; + /** + * Manually cancel any active WebAuthn registration or authentication attempt. + */ cancelCeremony(): void; } class BaseWebAuthnAbortService implements WebAuthnAbortService { private controller: AbortController | undefined; - /** - * Prepare an abort signal that will help support multiple auth attempts without needing to - * reload the page. This is automatically called whenever `startRegistration()` and - * `startAuthentication()` are called. - */ createNewAbortSignal(): AbortSignal { // Abort any existing calls to navigator.credentials.create() or navigator.credentials.get() if (this.controller) { @@ -27,9 +30,6 @@ class BaseWebAuthnAbortService implements WebAuthnAbortService { return newController.signal; } - /** - * Manually cancel any active WebAuthn registration or authentication attempt. - */ cancelCeremony(): void { if (this.controller) { const abortError = new Error( diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index ff305c311..44ad37f5e 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -1,27 +1,10 @@ -/** - * @packageDocumentation - * @module @simplewebauthn/browser - */ -import { startRegistration } from './methods/startRegistration.ts'; -import { startAuthentication } from './methods/startAuthentication.ts'; -import { browserSupportsWebAuthn } from './helpers/browserSupportsWebAuthn.ts'; -import { platformAuthenticatorIsAvailable } from './helpers/platformAuthenticatorIsAvailable.ts'; -import { browserSupportsWebAuthnAutofill } from './helpers/browserSupportsWebAuthnAutofill.ts'; -import { base64URLStringToBuffer } from './helpers/base64URLStringToBuffer.ts'; -import { bufferToBase64URLString } from './helpers/bufferToBase64URLString.ts'; -import { WebAuthnAbortService } from './helpers/webAuthnAbortService.ts'; -import { WebAuthnError } from './helpers/webAuthnError.ts'; - -export { - base64URLStringToBuffer, - browserSupportsWebAuthn, - browserSupportsWebAuthnAutofill, - bufferToBase64URLString, - platformAuthenticatorIsAvailable, - startAuthentication, - startRegistration, - WebAuthnAbortService, - WebAuthnError, -}; - -export type { WebAuthnErrorCode } from './helpers/webAuthnError.ts'; +export * from './methods/startRegistration.ts'; +export * from './methods/startAuthentication.ts'; +export * from './helpers/browserSupportsWebAuthn.ts'; +export * from './helpers/platformAuthenticatorIsAvailable.ts'; +export * from './helpers/browserSupportsWebAuthnAutofill.ts'; +export * from './helpers/base64URLStringToBuffer.ts'; +export * from './helpers/bufferToBase64URLString.ts'; +export * from './helpers/webAuthnAbortService.ts'; +export * from './helpers/webAuthnError.ts'; +export * from './types/index.ts'; diff --git a/packages/browser/src/methods/startAuthentication.test.ts b/packages/browser/src/methods/startAuthentication.test.ts index 0e2a27113..8fa49156f 100644 --- a/packages/browser/src/methods/startAuthentication.test.ts +++ b/packages/browser/src/methods/startAuthentication.test.ts @@ -7,19 +7,17 @@ import { assertRejects, assertStringIncludes, } from '@std/assert'; -import { assertSpyCalls, type Spy, spy, stub } from '@std/testing/mock'; +import { assertSpyCalls, type Spy, spy } from '@std/testing/mock'; import { afterEach, beforeEach, describe, it } from '@std/testing/bdd'; import { JSDOM } from 'jsdom'; -import { - AuthenticationCredential, +import type { AuthenticationExtensionsClientInputs, AuthenticationExtensionsClientOutputs, PublicKeyCredentialRequestOptionsJSON, -} from '@simplewebauthn/types'; +} from '../types/index.ts'; import { _browserSupportsWebAuthnInternals } from '../helpers/browserSupportsWebAuthn.ts'; import { _browserSupportsWebAuthnAutofillInternals } from '../helpers/browserSupportsWebAuthnAutofill.ts'; -import { base64URLStringToBuffer } from '../helpers/base64URLStringToBuffer.ts'; import { bufferToBase64URLString } from '../helpers/bufferToBase64URLString.ts'; import { WebAuthnError } from '../helpers/webAuthnError.ts'; import { generateCustomError } from '../helpers/__jest__/generateCustomError.ts'; diff --git a/packages/browser/src/methods/startAuthentication.ts b/packages/browser/src/methods/startAuthentication.ts index 4f6010d69..021b6b3bf 100644 --- a/packages/browser/src/methods/startAuthentication.ts +++ b/packages/browser/src/methods/startAuthentication.ts @@ -1,9 +1,8 @@ -import { +import type { AuthenticationCredential, AuthenticationResponseJSON, PublicKeyCredentialRequestOptionsJSON, -} from '@simplewebauthn/types'; - +} from '../types/index.ts'; import { bufferToBase64URLString } from '../helpers/bufferToBase64URLString.ts'; import { base64URLStringToBuffer } from '../helpers/base64URLStringToBuffer.ts'; import { browserSupportsWebAuthn } from '../helpers/browserSupportsWebAuthn.ts'; @@ -13,11 +12,7 @@ import { identifyAuthenticationError } from '../helpers/identifyAuthenticationEr import { WebAuthnAbortService } from '../helpers/webAuthnAbortService.ts'; import { toAuthenticatorAttachment } from '../helpers/toAuthenticatorAttachment.ts'; -export type StartAuthenticationOpts = { - optionsJSON: PublicKeyCredentialRequestOptionsJSON; - useBrowserAutofill?: boolean; - verifyBrowserAutofillInput?: boolean; -}; +export type StartAuthenticationOpts = Parameters[0]; /** * Begin authenticator "login" via WebAuthn assertion @@ -27,7 +22,11 @@ export type StartAuthenticationOpts = { * @param verifyBrowserAutofillInput (Optional) Ensure a suitable `` element is present when `useBrowserAutofill` is `true`. Defaults to `true`. */ export async function startAuthentication( - options: StartAuthenticationOpts, + options: { + optionsJSON: PublicKeyCredentialRequestOptionsJSON; + useBrowserAutofill?: boolean; + verifyBrowserAutofillInput?: boolean; + }, ): Promise { const { optionsJSON, diff --git a/packages/browser/src/methods/startRegistration.test.ts b/packages/browser/src/methods/startRegistration.test.ts index 6026ba939..da9761883 100644 --- a/packages/browser/src/methods/startRegistration.test.ts +++ b/packages/browser/src/methods/startRegistration.test.ts @@ -2,11 +2,11 @@ import { assertEquals, assertInstanceOf, assertRejects, assertStringIncludes } from '@std/assert'; import { assertSpyCalls, type Spy, spy, stub } from '@std/testing/mock'; import { afterEach, beforeEach, describe, it } from '@std/testing/bdd'; -import { +import type { AuthenticationExtensionsClientInputs, AuthenticationExtensionsClientOutputs, PublicKeyCredentialCreationOptionsJSON, -} from '@simplewebauthn/types'; +} from '../types/index.ts'; import { generateCustomError } from '../helpers/__jest__/generateCustomError.ts'; import { _browserSupportsWebAuthnInternals } from '../helpers/browserSupportsWebAuthn.ts'; diff --git a/packages/browser/src/methods/startRegistration.ts b/packages/browser/src/methods/startRegistration.ts index 0e82e7bb4..aa756f21b 100644 --- a/packages/browser/src/methods/startRegistration.ts +++ b/packages/browser/src/methods/startRegistration.ts @@ -1,10 +1,9 @@ -import { +import type { AuthenticatorTransportFuture, PublicKeyCredentialCreationOptionsJSON, RegistrationCredential, RegistrationResponseJSON, -} from '@simplewebauthn/types'; - +} from '../types/index.ts'; import { bufferToBase64URLString } from '../helpers/bufferToBase64URLString.ts'; import { base64URLStringToBuffer } from '../helpers/base64URLStringToBuffer.ts'; import { browserSupportsWebAuthn } from '../helpers/browserSupportsWebAuthn.ts'; @@ -13,10 +12,7 @@ import { identifyRegistrationError } from '../helpers/identifyRegistrationError. import { WebAuthnAbortService } from '../helpers/webAuthnAbortService.ts'; import { toAuthenticatorAttachment } from '../helpers/toAuthenticatorAttachment.ts'; -export type StartRegistrationOpts = { - optionsJSON: PublicKeyCredentialCreationOptionsJSON; - useAutoRegister?: boolean; -}; +export type StartRegistrationOpts = Parameters[0]; /** * Begin authenticator "registration" via WebAuthn attestation @@ -25,7 +21,10 @@ export type StartRegistrationOpts = { * @param useAutoRegister (Optional) Try to silently create a passkey with the password manager that the user just signed in with. Defaults to `false`. */ export async function startRegistration( - options: StartRegistrationOpts, + options: { + optionsJSON: PublicKeyCredentialCreationOptionsJSON; + useAutoRegister?: boolean; + }, ): Promise { const { optionsJSON, useAutoRegister = false } = options; diff --git a/packages/browser/src/types/dom.ts b/packages/browser/src/types/dom.ts new file mode 100644 index 000000000..1caddc0dc --- /dev/null +++ b/packages/browser/src/types/dom.ts @@ -0,0 +1,373 @@ +// deno-fmt-ignore-file +/** + * DO NOT MODIFY THESE FILES! + * + * These files were copied from the **types** package. To update this file, make changes to those + * files instead and then run the following command from the monorepo root folder: + * + * deno task codegen:types + */ +// BEGIN CODEGEN +/** + * Generated from typescript@5.6.3 + * To regenerate, run the following command from the package root: + * deno task extract-dom-types + */ +/** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAssertionResponse) + */ +export interface AuthenticatorAssertionResponse extends AuthenticatorResponse { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAssertionResponse/authenticatorData) */ + readonly authenticatorData: ArrayBuffer; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAssertionResponse/signature) */ + readonly signature: ArrayBuffer; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAssertionResponse/userHandle) */ + readonly userHandle: ArrayBuffer | null; +} + +/** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse) + */ +export interface AuthenticatorAttestationResponse extends AuthenticatorResponse { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse/attestationObject) */ + readonly attestationObject: ArrayBuffer; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse/getAuthenticatorData) */ + getAuthenticatorData(): ArrayBuffer; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse/getPublicKey) */ + getPublicKey(): ArrayBuffer | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse/getPublicKeyAlgorithm) */ + getPublicKeyAlgorithm(): COSEAlgorithmIdentifier; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse/getTransports) */ + getTransports(): string[]; +} + +export interface AuthenticationExtensionsClientInputs { + appid?: string; + credProps?: boolean; + hmacCreateSecret?: boolean; + minPinLength?: boolean; +} + +export interface AuthenticationExtensionsClientOutputs { + appid?: boolean; + credProps?: CredentialPropertiesOutput; + hmacCreateSecret?: boolean; +} + +export interface AuthenticatorSelectionCriteria { + authenticatorAttachment?: AuthenticatorAttachment; + requireResidentKey?: boolean; + residentKey?: ResidentKeyRequirement; + userVerification?: UserVerificationRequirement; +} + +/** + * Basic cryptography features available in the current context. It allows access to a cryptographically strong random number generator and to cryptographic primitives. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto) + */ +export interface Crypto { + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/subtle) + */ + readonly subtle: SubtleCrypto; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues) */ + getRandomValues(array: T): T; + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/randomUUID) + */ + randomUUID(): `${string}-${string}-${string}-${string}-${string}`; +} + +/** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential) + */ +export interface PublicKeyCredential extends Credential { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential/authenticatorAttachment) */ + readonly authenticatorAttachment: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential/rawId) */ + readonly rawId: ArrayBuffer; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential/response) */ + readonly response: AuthenticatorResponse; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential/getClientExtensionResults) */ + getClientExtensionResults(): AuthenticationExtensionsClientOutputs; +} + +export interface PublicKeyCredentialCreationOptions { + attestation?: AttestationConveyancePreference; + authenticatorSelection?: AuthenticatorSelectionCriteria; + challenge: BufferSource; + excludeCredentials?: PublicKeyCredentialDescriptor[]; + extensions?: AuthenticationExtensionsClientInputs; + pubKeyCredParams: PublicKeyCredentialParameters[]; + rp: PublicKeyCredentialRpEntity; + timeout?: number; + user: PublicKeyCredentialUserEntity; +} + +export interface PublicKeyCredentialDescriptor { + id: BufferSource; + transports?: AuthenticatorTransport[]; + type: PublicKeyCredentialType; +} + +export interface PublicKeyCredentialParameters { + alg: COSEAlgorithmIdentifier; + type: PublicKeyCredentialType; +} + +export interface PublicKeyCredentialRequestOptions { + allowCredentials?: PublicKeyCredentialDescriptor[]; + challenge: BufferSource; + extensions?: AuthenticationExtensionsClientInputs; + rpId?: string; + timeout?: number; + userVerification?: UserVerificationRequirement; +} + +export interface PublicKeyCredentialUserEntity extends PublicKeyCredentialEntity { + displayName: string; + id: BufferSource; +} + +/** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorResponse) + */ +export interface AuthenticatorResponse { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorResponse/clientDataJSON) */ + readonly clientDataJSON: ArrayBuffer; +} + +export interface CredentialPropertiesOutput { + rk?: boolean; +} + +/** + * This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via Window.crypto). + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto) + */ +export interface SubtleCrypto { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/decrypt) */ + decrypt(algorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams, key: CryptoKey, data: BufferSource): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveBits) */ + deriveBits(algorithm: AlgorithmIdentifier | EcdhKeyDeriveParams | HkdfParams | Pbkdf2Params, baseKey: CryptoKey, length: number): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveKey) */ + deriveKey(algorithm: AlgorithmIdentifier | EcdhKeyDeriveParams | HkdfParams | Pbkdf2Params, baseKey: CryptoKey, derivedKeyType: AlgorithmIdentifier | AesDerivedKeyParams | HmacImportParams | HkdfParams | Pbkdf2Params, extractable: boolean, keyUsages: KeyUsage[]): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/digest) */ + digest(algorithm: AlgorithmIdentifier, data: BufferSource): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/encrypt) */ + encrypt(algorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams, key: CryptoKey, data: BufferSource): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/exportKey) */ + exportKey(format: "jwk", key: CryptoKey): Promise; + exportKey(format: Exclude, key: CryptoKey): Promise; + exportKey(format: KeyFormat, key: CryptoKey): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/generateKey) */ + generateKey(algorithm: "Ed25519", extractable: boolean, keyUsages: ReadonlyArray<"sign" | "verify">): Promise; + generateKey(algorithm: RsaHashedKeyGenParams | EcKeyGenParams, extractable: boolean, keyUsages: ReadonlyArray): Promise; + generateKey(algorithm: AesKeyGenParams | HmacKeyGenParams | Pbkdf2Params, extractable: boolean, keyUsages: ReadonlyArray): Promise; + generateKey(algorithm: AlgorithmIdentifier, extractable: boolean, keyUsages: KeyUsage[]): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/importKey) */ + importKey(format: "jwk", keyData: JsonWebKey, algorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm, extractable: boolean, keyUsages: ReadonlyArray): Promise; + importKey(format: Exclude, keyData: BufferSource, algorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/sign) */ + sign(algorithm: AlgorithmIdentifier | RsaPssParams | EcdsaParams, key: CryptoKey, data: BufferSource): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/unwrapKey) */ + unwrapKey(format: KeyFormat, wrappedKey: BufferSource, unwrappingKey: CryptoKey, unwrapAlgorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams, unwrappedKeyAlgorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/verify) */ + verify(algorithm: AlgorithmIdentifier | RsaPssParams | EcdsaParams, key: CryptoKey, signature: BufferSource, data: BufferSource): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/wrapKey) */ + wrapKey(format: KeyFormat, key: CryptoKey, wrappingKey: CryptoKey, wrapAlgorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams): Promise; +} + +/** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Credential) + */ +export interface Credential { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Credential/id) */ + readonly id: string; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Credential/type) */ + readonly type: string; +} + +export interface PublicKeyCredentialRpEntity extends PublicKeyCredentialEntity { + id?: string; +} + +export interface PublicKeyCredentialEntity { + name: string; +} + +export interface RsaOaepParams extends Algorithm { + label?: BufferSource; +} + +export interface AesCtrParams extends Algorithm { + counter: BufferSource; + length: number; +} + +export interface AesCbcParams extends Algorithm { + iv: BufferSource; +} + +export interface AesGcmParams extends Algorithm { + additionalData?: BufferSource; + iv: BufferSource; + tagLength?: number; +} + +/** + * The CryptoKey dictionary of the Web Crypto API represents a cryptographic key. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey) + */ +export interface CryptoKey { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/algorithm) */ + readonly algorithm: KeyAlgorithm; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/extractable) */ + readonly extractable: boolean; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/type) */ + readonly type: KeyType; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/usages) */ + readonly usages: KeyUsage[]; +} + +export interface EcdhKeyDeriveParams extends Algorithm { + public: CryptoKey; +} + +export interface HkdfParams extends Algorithm { + hash: HashAlgorithmIdentifier; + info: BufferSource; + salt: BufferSource; +} + +export interface Pbkdf2Params extends Algorithm { + hash: HashAlgorithmIdentifier; + iterations: number; + salt: BufferSource; +} + +export interface AesDerivedKeyParams extends Algorithm { + length: number; +} + +export interface HmacImportParams extends Algorithm { + hash: HashAlgorithmIdentifier; + length?: number; +} + +export interface JsonWebKey { + alg?: string; + crv?: string; + d?: string; + dp?: string; + dq?: string; + e?: string; + ext?: boolean; + k?: string; + key_ops?: string[]; + kty?: string; + n?: string; + oth?: RsaOtherPrimesInfo[]; + p?: string; + q?: string; + qi?: string; + use?: string; + x?: string; + y?: string; +} + +export interface CryptoKeyPair { + privateKey: CryptoKey; + publicKey: CryptoKey; +} + +export interface RsaHashedKeyGenParams extends RsaKeyGenParams { + hash: HashAlgorithmIdentifier; +} + +export interface EcKeyGenParams extends Algorithm { + namedCurve: NamedCurve; +} + +export interface AesKeyGenParams extends Algorithm { + length: number; +} + +export interface HmacKeyGenParams extends Algorithm { + hash: HashAlgorithmIdentifier; + length?: number; +} + +export interface RsaHashedImportParams extends Algorithm { + hash: HashAlgorithmIdentifier; +} + +export interface EcKeyImportParams extends Algorithm { + namedCurve: NamedCurve; +} + +export interface AesKeyAlgorithm extends KeyAlgorithm { + length: number; +} + +export interface RsaPssParams extends Algorithm { + saltLength: number; +} + +export interface EcdsaParams extends Algorithm { + hash: HashAlgorithmIdentifier; +} + +export interface Algorithm { + name: string; +} + +export interface KeyAlgorithm { + name: string; +} + +export interface RsaOtherPrimesInfo { + d?: string; + r?: string; + t?: string; +} + +export interface RsaKeyGenParams extends Algorithm { + modulusLength: number; + publicExponent: BigInteger; +} + +export type AttestationConveyancePreference = "direct" | "enterprise" | "indirect" | "none"; +export type AuthenticatorTransport = "ble" | "hybrid" | "internal" | "nfc" | "usb"; +export type COSEAlgorithmIdentifier = number; +export type UserVerificationRequirement = "discouraged" | "preferred" | "required"; +export type AuthenticatorAttachment = "cross-platform" | "platform"; +export type ResidentKeyRequirement = "discouraged" | "preferred" | "required"; +export type BufferSource = ArrayBufferView | ArrayBuffer; +export type PublicKeyCredentialType = "public-key"; +export type AlgorithmIdentifier = Algorithm | string; +export type KeyUsage = "decrypt" | "deriveBits" | "deriveKey" | "encrypt" | "sign" | "unwrapKey" | "verify" | "wrapKey"; +export type KeyFormat = "jwk" | "pkcs8" | "raw" | "spki"; +export type KeyType = "private" | "public" | "secret"; +export type HashAlgorithmIdentifier = AlgorithmIdentifier; +export type NamedCurve = string; +export type BigInteger = Uint8Array; diff --git a/packages/browser/src/types/index.ts b/packages/browser/src/types/index.ts new file mode 100644 index 000000000..7ce146edc --- /dev/null +++ b/packages/browser/src/types/index.ts @@ -0,0 +1,294 @@ +// deno-fmt-ignore-file +/** + * DO NOT MODIFY THESE FILES! + * + * These files were copied from the **types** package. To update this file, make changes to those + * files instead and then run the following command from the monorepo root folder: + * + * deno task codegen:types + */ +// BEGIN CODEGEN +import type { + AttestationConveyancePreference, + AuthenticationExtensionsClientInputs, + AuthenticationExtensionsClientOutputs, + AuthenticatorAssertionResponse, + AuthenticatorAttachment, + AuthenticatorAttestationResponse, + AuthenticatorSelectionCriteria, + COSEAlgorithmIdentifier, + PublicKeyCredential, + PublicKeyCredentialCreationOptions, + PublicKeyCredentialDescriptor, + PublicKeyCredentialParameters, + PublicKeyCredentialRequestOptions, + PublicKeyCredentialRpEntity, + PublicKeyCredentialType, + UserVerificationRequirement, +} from './dom.ts'; + +export type { + AttestationConveyancePreference, + AuthenticationExtensionsClientInputs, + AuthenticationExtensionsClientOutputs, + AuthenticatorAssertionResponse, + AuthenticatorAttachment, + AuthenticatorAttestationResponse, + AuthenticatorSelectionCriteria, + AuthenticatorTransport, + COSEAlgorithmIdentifier, + Crypto, + PublicKeyCredential, + PublicKeyCredentialCreationOptions, + PublicKeyCredentialDescriptor, + PublicKeyCredentialParameters, + PublicKeyCredentialRequestOptions, + PublicKeyCredentialRpEntity, + PublicKeyCredentialType, + PublicKeyCredentialUserEntity, + UserVerificationRequirement, +} from './dom.ts'; + +/** + * A variant of PublicKeyCredentialCreationOptions suitable for JSON transmission to the browser to + * (eventually) get passed into navigator.credentials.create(...) in the browser. + * + * This should eventually get replaced with official TypeScript DOM types when WebAuthn L3 types + * eventually make it into the language: + * + * https://w3c.github.io/webauthn/#dictdef-publickeycredentialcreationoptionsjson + */ +export interface PublicKeyCredentialCreationOptionsJSON { + rp: PublicKeyCredentialRpEntity; + user: PublicKeyCredentialUserEntityJSON; + challenge: Base64URLString; + pubKeyCredParams: PublicKeyCredentialParameters[]; + timeout?: number; + excludeCredentials?: PublicKeyCredentialDescriptorJSON[]; + authenticatorSelection?: AuthenticatorSelectionCriteria; + hints?: PublicKeyCredentialHint[]; + attestation?: AttestationConveyancePreference; + attestationFormats?: AttestationFormat[]; + extensions?: AuthenticationExtensionsClientInputs; +} + +/** + * A variant of PublicKeyCredentialRequestOptions suitable for JSON transmission to the browser to + * (eventually) get passed into navigator.credentials.get(...) in the browser. + */ +export interface PublicKeyCredentialRequestOptionsJSON { + challenge: Base64URLString; + timeout?: number; + rpId?: string; + allowCredentials?: PublicKeyCredentialDescriptorJSON[]; + userVerification?: UserVerificationRequirement; + hints?: PublicKeyCredentialHint[]; + extensions?: AuthenticationExtensionsClientInputs; +} + +/** + * https://w3c.github.io/webauthn/#dictdef-publickeycredentialdescriptorjson + */ +export interface PublicKeyCredentialDescriptorJSON { + id: Base64URLString; + type: PublicKeyCredentialType; + transports?: AuthenticatorTransportFuture[]; +} + +/** + * https://w3c.github.io/webauthn/#dictdef-publickeycredentialuserentityjson + */ +export interface PublicKeyCredentialUserEntityJSON { + id: string; + name: string; + displayName: string; +} + +/** + * The value returned from navigator.credentials.create() + */ +export interface RegistrationCredential extends PublicKeyCredentialFuture { + response: AuthenticatorAttestationResponseFuture; +} + +/** + * A slightly-modified RegistrationCredential to simplify working with ArrayBuffers that + * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. + * + * https://w3c.github.io/webauthn/#dictdef-registrationresponsejson + */ +export interface RegistrationResponseJSON { + id: Base64URLString; + rawId: Base64URLString; + response: AuthenticatorAttestationResponseJSON; + authenticatorAttachment?: AuthenticatorAttachment; + clientExtensionResults: AuthenticationExtensionsClientOutputs; + type: PublicKeyCredentialType; +} + +/** + * The value returned from navigator.credentials.get() + */ +export interface AuthenticationCredential extends PublicKeyCredentialFuture { + response: AuthenticatorAssertionResponse; +} + +/** + * A slightly-modified AuthenticationCredential to simplify working with ArrayBuffers that + * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. + * + * https://w3c.github.io/webauthn/#dictdef-authenticationresponsejson + */ +export interface AuthenticationResponseJSON { + id: Base64URLString; + rawId: Base64URLString; + response: AuthenticatorAssertionResponseJSON; + authenticatorAttachment?: AuthenticatorAttachment; + clientExtensionResults: AuthenticationExtensionsClientOutputs; + type: PublicKeyCredentialType; +} + +/** + * A slightly-modified AuthenticatorAttestationResponse to simplify working with ArrayBuffers that + * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. + * + * https://w3c.github.io/webauthn/#dictdef-authenticatorattestationresponsejson + */ +export interface AuthenticatorAttestationResponseJSON { + clientDataJSON: Base64URLString; + attestationObject: Base64URLString; + // Optional in L2, but becomes required in L3. Play it safe until L3 becomes Recommendation + authenticatorData?: Base64URLString; + // Optional in L2, but becomes required in L3. Play it safe until L3 becomes Recommendation + transports?: AuthenticatorTransportFuture[]; + // Optional in L2, but becomes required in L3. Play it safe until L3 becomes Recommendation + publicKeyAlgorithm?: COSEAlgorithmIdentifier; + publicKey?: Base64URLString; +} + +/** + * A slightly-modified AuthenticatorAssertionResponse to simplify working with ArrayBuffers that + * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. + * + * https://w3c.github.io/webauthn/#dictdef-authenticatorassertionresponsejson + */ +export interface AuthenticatorAssertionResponseJSON { + clientDataJSON: Base64URLString; + authenticatorData: Base64URLString; + signature: Base64URLString; + userHandle?: Base64URLString; +} + +/** + * Public key credential information needed to verify authentication responses + */ +export type WebAuthnCredential = { + id: Base64URLString; + publicKey: Uint8Array; + // Number of times this authenticator is expected to have been used + counter: number; + // From browser's `startRegistration()` -> RegistrationCredentialJSON.transports (API L2 and up) + transports?: AuthenticatorTransportFuture[]; +}; + +/** + * An attempt to communicate that this isn't just any string, but a Base64URL-encoded string + */ +export type Base64URLString = string; + +/** + * AuthenticatorAttestationResponse in TypeScript's DOM lib is outdated (up through v3.9.7). + * Maintain an augmented version here so we can implement additional properties as the WebAuthn + * spec evolves. + * + * See https://www.w3.org/TR/webauthn-2/#iface-authenticatorattestationresponse + * + * Properties marked optional are not supported in all browsers. + */ +export interface AuthenticatorAttestationResponseFuture extends AuthenticatorAttestationResponse { + getTransports(): AuthenticatorTransportFuture[]; +} + +/** + * A super class of TypeScript's `AuthenticatorTransport` that includes support for the latest + * transports. Should eventually be replaced by TypeScript's when TypeScript gets updated to + * know about it (sometime after 4.6.3) + */ +export type AuthenticatorTransportFuture = + | 'ble' + | 'cable' + | 'hybrid' + | 'internal' + | 'nfc' + | 'smart-card' + | 'usb'; + +/** + * A super class of TypeScript's `PublicKeyCredentialDescriptor` that knows about the latest + * transports. Should eventually be replaced by TypeScript's when TypeScript gets updated to + * know about it (sometime after 4.6.3) + */ +export interface PublicKeyCredentialDescriptorFuture + extends Omit { + transports?: AuthenticatorTransportFuture[]; +} + +/** */ +export type PublicKeyCredentialJSON = + | RegistrationResponseJSON + | AuthenticationResponseJSON; + +/** + * A super class of TypeScript's `PublicKeyCredential` that knows about upcoming WebAuthn features + */ +export interface PublicKeyCredentialFuture extends PublicKeyCredential { + type: PublicKeyCredentialType; + // See https://github.com/w3c/webauthn/issues/1745 + isConditionalMediationAvailable?(): Promise; + // See https://w3c.github.io/webauthn/#sctn-parseCreationOptionsFromJSON + parseCreationOptionsFromJSON?( + options: PublicKeyCredentialCreationOptionsJSON, + ): PublicKeyCredentialCreationOptions; + // See https://w3c.github.io/webauthn/#sctn-parseRequestOptionsFromJSON + parseRequestOptionsFromJSON?( + options: PublicKeyCredentialRequestOptionsJSON, + ): PublicKeyCredentialRequestOptions; + // See https://w3c.github.io/webauthn/#dom-publickeycredential-tojson + toJSON?(): PublicKeyCredentialJSON; +} + +/** + * The two types of credentials as defined by bit 3 ("Backup Eligibility") in authenticator data: + * - `"singleDevice"` credentials will never be backed up + * - `"multiDevice"` credentials can be backed up + */ +export type CredentialDeviceType = 'singleDevice' | 'multiDevice'; + +/** + * Categories of authenticators that Relying Parties can pass along to browsers during + * registration. Browsers that understand these values can optimize their modal experience to + * start the user off in a particular registration flow: + * + * - `hybrid`: A platform authenticator on a mobile device + * - `security-key`: A portable FIDO2 authenticator capable of being used on multiple devices via a USB or NFC connection + * - `client-device`: The device that WebAuthn is being called on. Typically synonymous with platform authenticators + * + * See https://w3c.github.io/webauthn/#enumdef-publickeycredentialhint + * + * These values are less strict than `authenticatorAttachment` + */ +export type PublicKeyCredentialHint = 'hybrid' | 'security-key' | 'client-device'; + +/** + * Values for an attestation object's `fmt` + * + * See https://www.iana.org/assignments/webauthn/webauthn.xhtml#webauthn-attestation-statement-format-ids + */ +export type AttestationFormat = + | 'fido-u2f' + | 'packed' + | 'android-safetynet' + | 'android-key' + | 'tpm' + | 'apple' + | 'none'; diff --git a/packages/server/README.md b/packages/server/README.md index 356e2c093..b6189b02c 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -18,13 +18,13 @@ and **[JSR](https://jsr.io/@simplewebauthn/server)**: ### Node LTS 20.x and higher ```sh -npm install @simplewebauthn/server @simplewebauthn/types +npm install @simplewebauthn/server ``` ### Deno v1.43 and higher ```sh -deno add jsr:@simplewebauthn/server jsr:@simplewebauthn/types +deno add jsr:@simplewebauthn/server ``` ## Documentation diff --git a/packages/server/build_npm.ts b/packages/server/build_npm.ts index 54731cdd1..1de6e40ac 100644 --- a/packages/server/build_npm.ts +++ b/packages/server/build_npm.ts @@ -62,10 +62,7 @@ await build({ ], }, }, - dependencies: { - // Deno workspaces maps this identifier locally, make sure it's defined in the NPM package - '@simplewebauthn/types': `^${typesDenoJSON.version}`, - }, + dependencies: {}, }, // Map from Deno package to NPM package for Node build mappings: {}, diff --git a/packages/server/deno.json b/packages/server/deno.json index affecbb25..05571847a 100644 --- a/packages/server/deno.json +++ b/packages/server/deno.json @@ -14,6 +14,14 @@ }, "test": "deno test -A src/", "test:watch": "deno test -A --watch src/", + "docs:serve": { + "command": "deno run -A jsr:@std/http/file-server --host 127.0.0.1 docs/", + "dependencies": [ + "docs:html" + ] + }, + "docs:html": "deno doc --html src/index.ts", + "docs:lint": "deno doc --lint src/index.ts", "publish": { "command": "deno task publish:jsr && deno task publish:npm", "dependencies": [ diff --git a/packages/server/src/authentication/generateAuthenticationOptions.ts b/packages/server/src/authentication/generateAuthenticationOptions.ts index 5b042f961..03bc376c3 100644 --- a/packages/server/src/authentication/generateAuthenticationOptions.ts +++ b/packages/server/src/authentication/generateAuthenticationOptions.ts @@ -3,8 +3,7 @@ import type { AuthenticatorTransportFuture, Base64URLString, PublicKeyCredentialRequestOptionsJSON, -} from '@simplewebauthn/types'; - +} from '../types/index.ts'; import { isoBase64URL, isoUint8Array } from '../helpers/iso/index.ts'; import { generateChallenge } from '../helpers/generateChallenge.ts'; diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.test.ts b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts index b7c45e2dd..f74fe5794 100644 --- a/packages/server/src/authentication/verifyAuthenticationResponse.test.ts +++ b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts @@ -7,10 +7,10 @@ import { assertRejects, } from '@std/assert'; import { returnsNext, stub } from '@std/testing/mock'; -import type { AuthenticationResponseJSON, WebAuthnCredential } from '@simplewebauthn/types'; import { verifyAuthenticationResponse } from './verifyAuthenticationResponse.ts'; +import type { AuthenticationResponseJSON, WebAuthnCredential } from '../types/index.ts'; import { _decodeClientDataJSONInternals } from '../helpers/decodeClientDataJSON.ts'; import { _parseAuthenticatorDataInternals, diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.ts b/packages/server/src/authentication/verifyAuthenticationResponse.ts index 703c02e58..550d04dcc 100644 --- a/packages/server/src/authentication/verifyAuthenticationResponse.ts +++ b/packages/server/src/authentication/verifyAuthenticationResponse.ts @@ -4,29 +4,20 @@ import type { CredentialDeviceType, UserVerificationRequirement, WebAuthnCredential, -} from '@simplewebauthn/types'; - +} from '../types/index.ts'; import { decodeClientDataJSON } from '../helpers/decodeClientDataJSON.ts'; import { toHash } from '../helpers/toHash.ts'; import { verifySignature } from '../helpers/verifySignature.ts'; import { parseAuthenticatorData } from '../helpers/parseAuthenticatorData.ts'; import { parseBackupFlags } from '../helpers/parseBackupFlags.ts'; -import { AuthenticationExtensionsAuthenticatorOutputs } from '../helpers/decodeAuthenticatorExtensions.ts'; +import type { AuthenticationExtensionsAuthenticatorOutputs } from '../helpers/decodeAuthenticatorExtensions.ts'; import { matchExpectedRPID } from '../helpers/matchExpectedRPID.ts'; import { isoBase64URL, isoUint8Array } from '../helpers/iso/index.ts'; -export type VerifyAuthenticationResponseOpts = { - response: AuthenticationResponseJSON; - expectedChallenge: string | ((challenge: string) => boolean | Promise); - expectedOrigin: string | string[]; - expectedRPID: string | string[]; - credential: WebAuthnCredential; - expectedType?: string | string[]; - requireUserVerification?: boolean; - advancedFIDOConfig?: { - userVerification?: UserVerificationRequirement; - }; -}; +/** + * Configurable options when calling `verifyAuthenticationResponse()` + */ +export type VerifyAuthenticationResponseOpts = Parameters[0]; /** * Verify that the user has legitimately completed the authentication process @@ -44,7 +35,18 @@ export type VerifyAuthenticationResponseOpts = { * @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"` */ export async function verifyAuthenticationResponse( - options: VerifyAuthenticationResponseOpts, + options: { + response: AuthenticationResponseJSON; + expectedChallenge: string | ((challenge: string) => boolean | Promise); + expectedOrigin: string | string[]; + expectedRPID: string | string[]; + credential: WebAuthnCredential; + expectedType?: string | string[]; + requireUserVerification?: boolean; + advancedFIDOConfig?: { + userVerification?: UserVerificationRequirement; + }; + }, ): Promise { const { response, diff --git a/packages/server/src/helpers/convertCertBufferToPEM.ts b/packages/server/src/helpers/convertCertBufferToPEM.ts index 126621e88..3179d72cd 100644 --- a/packages/server/src/helpers/convertCertBufferToPEM.ts +++ b/packages/server/src/helpers/convertCertBufferToPEM.ts @@ -1,5 +1,4 @@ -import type { Base64URLString } from '@simplewebauthn/types'; - +import type { Base64URLString } from '../types/index.ts'; import { isoBase64URL } from './iso/index.ts'; /** diff --git a/packages/server/src/helpers/cose.ts b/packages/server/src/helpers/cose.ts index 4e0224059..225d287f4 100644 --- a/packages/server/src/helpers/cose.ts +++ b/packages/server/src/helpers/cose.ts @@ -7,6 +7,11 @@ * * These types are an unorthodox way of saying "these Maps should involve these discrete lists of * keys", but it works. + * @module + */ + +/** + * COSE public key common values */ export type COSEPublicKey = { // Getters @@ -17,6 +22,9 @@ export type COSEPublicKey = { set(key: COSEKEYS.alg, value: COSEALG): void; }; +/** + * Values specific to Octet Key Pair public keys + */ export type COSEPublicKeyOKP = COSEPublicKey & { // Getters get(key: COSEKEYS.crv): number | undefined; @@ -26,6 +34,9 @@ export type COSEPublicKeyOKP = COSEPublicKey & { set(key: COSEKEYS.x, value: Uint8Array): void; }; +/** + * Values specific to Elliptic Curve Cryptography public keys + */ export type COSEPublicKeyEC2 = COSEPublicKey & { // Getters get(key: COSEKEYS.crv): number | undefined; @@ -37,6 +48,9 @@ export type COSEPublicKeyEC2 = COSEPublicKey & { set(key: COSEKEYS.y, value: Uint8Array): void; }; +/** + * Values specific to RSA public keys + */ export type COSEPublicKeyRSA = COSEPublicKey & { // Getters get(key: COSEKEYS.n): Uint8Array | undefined; @@ -46,6 +60,9 @@ export type COSEPublicKeyRSA = COSEPublicKey & { set(key: COSEKEYS.e, value: Uint8Array): void; }; +/** + * A type guard for determining if a COSE public key is an OKP key pair + */ export function isCOSEPublicKeyOKP( cosePublicKey: COSEPublicKey, ): cosePublicKey is COSEPublicKeyOKP { @@ -53,6 +70,9 @@ export function isCOSEPublicKeyOKP( return isCOSEKty(kty) && kty === COSEKTY.OKP; } +/** + * A type guard for determining if a COSE public key is an EC2 key pair + */ export function isCOSEPublicKeyEC2( cosePublicKey: COSEPublicKey, ): cosePublicKey is COSEPublicKeyEC2 { @@ -60,6 +80,9 @@ export function isCOSEPublicKeyEC2( return isCOSEKty(kty) && kty === COSEKTY.EC2; } +/** + * A type guard for determining if a COSE public key is an RSA key pair + */ export function isCOSEPublicKeyRSA( cosePublicKey: COSEPublicKey, ): cosePublicKey is COSEPublicKeyRSA { diff --git a/packages/server/src/helpers/decodeAttestationObject.ts b/packages/server/src/helpers/decodeAttestationObject.ts index 3ccc47b78..9413fe45a 100644 --- a/packages/server/src/helpers/decodeAttestationObject.ts +++ b/packages/server/src/helpers/decodeAttestationObject.ts @@ -44,7 +44,10 @@ export type AttestationStatement = { readonly size: number; }; -// Make it possible to stub the return value during testing +/** + * Make it possible to stub the return value during testing + * @ignore Don't include this in docs output + */ export const _decodeAttestationObjectInternals = { stubThis: (value: AttestationObject) => value, }; diff --git a/packages/server/src/helpers/decodeClientDataJSON.ts b/packages/server/src/helpers/decodeClientDataJSON.ts index a3a334101..22fef212f 100644 --- a/packages/server/src/helpers/decodeClientDataJSON.ts +++ b/packages/server/src/helpers/decodeClientDataJSON.ts @@ -1,5 +1,4 @@ -import type { Base64URLString } from '@simplewebauthn/types'; - +import type { Base64URLString } from '../types/index.ts'; import { isoBase64URL } from './iso/index.ts'; /** @@ -23,7 +22,10 @@ export type ClientDataJSON = { }; }; -// Make it possible to stub the return value during testing +/** + * Make it possible to stub the return value during testing + * @ignore Don't include this in docs output + */ export const _decodeClientDataJSONInternals = { stubThis: (value: ClientDataJSON) => value, }; diff --git a/packages/server/src/helpers/decodeCredentialPublicKey.ts b/packages/server/src/helpers/decodeCredentialPublicKey.ts index 12ff2987c..38535ddc4 100644 --- a/packages/server/src/helpers/decodeCredentialPublicKey.ts +++ b/packages/server/src/helpers/decodeCredentialPublicKey.ts @@ -9,7 +9,10 @@ export function decodeCredentialPublicKey( ); } -// Make it possible to stub the return value during testing +/** + * Make it possible to stub the return value during testing + * @ignore Don't include this in docs output + */ export const _decodeCredentialPublicKeyInternals = { stubThis: (value: COSEPublicKey) => value, }; diff --git a/packages/server/src/helpers/fetch.ts b/packages/server/src/helpers/fetch.ts index 8df5f245e..848c1c9e6 100644 --- a/packages/server/src/helpers/fetch.ts +++ b/packages/server/src/helpers/fetch.ts @@ -8,7 +8,10 @@ export function fetch(url: string): Promise { return _fetchInternals.stubThis(url); } -// Make it possible to stub the return value during testing +/** + * Make it possible to stub the return value during testing + * @ignore Don't include this in docs output + */ export const _fetchInternals = { stubThis: (url: string) => crossFetch(url), }; diff --git a/packages/server/src/helpers/generateChallenge.ts b/packages/server/src/helpers/generateChallenge.ts index 40b12a4e6..4fc1a46f7 100644 --- a/packages/server/src/helpers/generateChallenge.ts +++ b/packages/server/src/helpers/generateChallenge.ts @@ -19,7 +19,10 @@ export async function generateChallenge(): Promise { return _generateChallengeInternals.stubThis(challenge); } -// Make it possible to stub the return value during testing +/** + * Make it possible to stub the return value during testing + * @ignore Don't include this in docs output + */ export const _generateChallengeInternals = { stubThis: (value: Uint8Array) => value, }; diff --git a/packages/server/src/helpers/generateUserID.ts b/packages/server/src/helpers/generateUserID.ts index eaf9bb053..9e475ec09 100644 --- a/packages/server/src/helpers/generateUserID.ts +++ b/packages/server/src/helpers/generateUserID.ts @@ -15,7 +15,10 @@ export async function generateUserID(): Promise { return _generateUserIDInternals.stubThis(newUserID); } -// Make it possible to stub the return value during testing +/** + * Make it possible to stub the return value during testing + * @ignore Don't include this in docs output + */ export const _generateUserIDInternals = { stubThis: (value: Uint8Array) => value, }; diff --git a/packages/server/src/helpers/index.ts b/packages/server/src/helpers/index.ts index 09b2f33bb..ffc2c4d44 100644 --- a/packages/server/src/helpers/index.ts +++ b/packages/server/src/helpers/index.ts @@ -1,66 +1,16 @@ -import { convertAAGUIDToString } from './convertAAGUIDToString.ts'; -import { convertCertBufferToPEM } from './convertCertBufferToPEM.ts'; -import { convertCOSEtoPKCS } from './convertCOSEtoPKCS.ts'; -import { decodeAttestationObject } from './decodeAttestationObject.ts'; -import { decodeClientDataJSON } from './decodeClientDataJSON.ts'; -import { decodeCredentialPublicKey } from './decodeCredentialPublicKey.ts'; -import { generateChallenge } from './generateChallenge.ts'; -import { generateUserID } from './generateUserID.ts'; -import { getCertificateInfo } from './getCertificateInfo.ts'; -import { isCertRevoked } from './isCertRevoked.ts'; -import { parseAuthenticatorData } from './parseAuthenticatorData.ts'; -import { toHash } from './toHash.ts'; -import { validateCertificatePath } from './validateCertificatePath.ts'; -import { verifySignature } from './verifySignature.ts'; -import { isoBase64URL, isoCBOR, isoCrypto, isoUint8Array } from './iso/index.ts'; -import * as cose from './cose.ts'; - -export { - convertAAGUIDToString, - convertCertBufferToPEM, - convertCOSEtoPKCS, - cose, - decodeAttestationObject, - decodeClientDataJSON, - decodeCredentialPublicKey, - generateChallenge, - generateUserID, - getCertificateInfo, - isCertRevoked, - isoBase64URL, - isoCBOR, - isoCrypto, - isoUint8Array, - parseAuthenticatorData, - toHash, - validateCertificatePath, - verifySignature, -}; - -import type { - AttestationFormat, - AttestationObject, - AttestationStatement, -} from './decodeAttestationObject.ts'; -import type { CertificateInfo } from './getCertificateInfo.ts'; -import type { ClientDataJSON } from './decodeClientDataJSON.ts'; -import type { - COSEPublicKey, - COSEPublicKeyEC2, - COSEPublicKeyOKP, - COSEPublicKeyRSA, -} from './cose.ts'; -import type { ParsedAuthenticatorData } from './parseAuthenticatorData.ts'; - -export type { - AttestationFormat, - AttestationObject, - AttestationStatement, - CertificateInfo, - ClientDataJSON, - COSEPublicKey, - COSEPublicKeyEC2, - COSEPublicKeyOKP, - COSEPublicKeyRSA, - ParsedAuthenticatorData, -}; +export * from './convertAAGUIDToString.ts'; +export * from './convertCertBufferToPEM.ts'; +export * from './convertCOSEtoPKCS.ts'; +export * from './decodeAttestationObject.ts'; +export * from './decodeClientDataJSON.ts'; +export * from './decodeCredentialPublicKey.ts'; +export * from './generateChallenge.ts'; +export * from './generateUserID.ts'; +export * from './getCertificateInfo.ts'; +export * from './isCertRevoked.ts'; +export * from './parseAuthenticatorData.ts'; +export * from './toHash.ts'; +export * from './validateCertificatePath.ts'; +export * from './verifySignature.ts'; +export * from './iso/index.ts'; +export * as cose from './cose.ts'; diff --git a/packages/server/src/helpers/iso/isoBase64URL.ts b/packages/server/src/helpers/iso/isoBase64URL.ts index bc22ff4b4..6ad536c6e 100644 --- a/packages/server/src/helpers/iso/isoBase64URL.ts +++ b/packages/server/src/helpers/iso/isoBase64URL.ts @@ -1,5 +1,10 @@ +/** + * A runtime-agnostic collection of methods for working with Base64URL encoding + * @module + */ import base64 from '@hexagon/base64'; -import type { Base64URLString } from '@simplewebauthn/types'; + +import type { Base64URLString } from '../../types/index.ts'; /** * Decode from a Base64URL-encoded string to an ArrayBuffer. Best used when converting a diff --git a/packages/server/src/helpers/iso/isoCBOR.ts b/packages/server/src/helpers/iso/isoCBOR.ts index d48ca49c4..70f0e7ea4 100644 --- a/packages/server/src/helpers/iso/isoCBOR.ts +++ b/packages/server/src/helpers/iso/isoCBOR.ts @@ -1,3 +1,7 @@ +/** + * A runtime-agnostic collection of methods for working with CBOR encoding + * @module + */ import * as tinyCbor from 'tiny-cbor'; /** diff --git a/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.ts b/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.ts index fffdc2acc..4518c65b8 100644 --- a/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.ts +++ b/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.ts @@ -1,4 +1,4 @@ -import type { Crypto } from '@simplewebauthn/types'; +import type { Crypto } from '../../../types/index.ts'; let webCrypto: Crypto | undefined = undefined; diff --git a/packages/server/src/helpers/iso/isoCrypto/index.ts b/packages/server/src/helpers/iso/isoCrypto/index.ts index 6d10ad1a6..ea9050950 100644 --- a/packages/server/src/helpers/iso/isoCrypto/index.ts +++ b/packages/server/src/helpers/iso/isoCrypto/index.ts @@ -1,3 +1,7 @@ +/** + * A runtime-agnostic collection of methods for working with the WebCrypto API + * @module + */ export { digest } from './digest.ts'; export { getRandomValues } from './getRandomValues.ts'; export { verify } from './verify.ts'; diff --git a/packages/server/src/helpers/iso/isoUint8Array.ts b/packages/server/src/helpers/iso/isoUint8Array.ts index 0df676379..f681c0d78 100644 --- a/packages/server/src/helpers/iso/isoUint8Array.ts +++ b/packages/server/src/helpers/iso/isoUint8Array.ts @@ -1,3 +1,8 @@ +/** + * A runtime-agnostic collection of methods for working with Uint8Arrays + * @module + */ + /** * Make sure two Uint8Arrays are deeply equivalent */ diff --git a/packages/server/src/helpers/parseAuthenticatorData.ts b/packages/server/src/helpers/parseAuthenticatorData.ts index 3166bebbc..f66884176 100644 --- a/packages/server/src/helpers/parseAuthenticatorData.ts +++ b/packages/server/src/helpers/parseAuthenticatorData.ts @@ -153,7 +153,10 @@ export type ParsedAuthenticatorData = { extensionsDataBuffer?: Uint8Array; }; -// Make it possible to stub the return value during testing +/** + * Make it possible to stub the return value during testing + * @ignore Don't include this in docs output + */ export const _parseAuthenticatorDataInternals = { stubThis: (value: ParsedAuthenticatorData) => value, }; diff --git a/packages/server/src/helpers/parseBackupFlags.ts b/packages/server/src/helpers/parseBackupFlags.ts index 029a09fdd..368068f95 100644 --- a/packages/server/src/helpers/parseBackupFlags.ts +++ b/packages/server/src/helpers/parseBackupFlags.ts @@ -1,4 +1,4 @@ -import type { CredentialDeviceType } from '@simplewebauthn/types'; +import type { CredentialDeviceType } from '../types/index.ts'; /** * Make sense of Bits 3 and 4 in authenticator indicating: diff --git a/packages/server/src/helpers/verifySignature.ts b/packages/server/src/helpers/verifySignature.ts index 40d7c9d5e..973dfe8e2 100644 --- a/packages/server/src/helpers/verifySignature.ts +++ b/packages/server/src/helpers/verifySignature.ts @@ -49,7 +49,10 @@ export function verifySignature(opts: { ); } -// Make it possible to stub the return value during testing +/** + * Make it possible to stub the return value during testing + * @ignore Don't include this in docs output + */ export const _verifySignatureInternals = { stubThis: (value: Promise) => value, }; diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 2e2a25bff..f6da2251c 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -1,41 +1,8 @@ -/** - * @packageDocumentation - * @module @simplewebauthn/server - */ -import { generateRegistrationOptions } from './registration/generateRegistrationOptions.ts'; -import { verifyRegistrationResponse } from './registration/verifyRegistrationResponse.ts'; -import { generateAuthenticationOptions } from './authentication/generateAuthenticationOptions.ts'; -import { verifyAuthenticationResponse } from './authentication/verifyAuthenticationResponse.ts'; -import { MetadataService } from './services/metadataService.ts'; -import { SettingsService } from './services/settingsService.ts'; - -export { - generateAuthenticationOptions, - generateRegistrationOptions, - MetadataService, - SettingsService, - verifyAuthenticationResponse, - verifyRegistrationResponse, -}; - -import type { GenerateRegistrationOptionsOpts } from './registration/generateRegistrationOptions.ts'; -import type { GenerateAuthenticationOptionsOpts } from './authentication/generateAuthenticationOptions.ts'; -import type { MetadataStatement } from './metadata/mdsTypes.ts'; -import type { - VerifiedRegistrationResponse, - VerifyRegistrationResponseOpts, -} from './registration/verifyRegistrationResponse.ts'; -import type { - VerifiedAuthenticationResponse, - VerifyAuthenticationResponseOpts, -} from './authentication/verifyAuthenticationResponse.ts'; - -export type { - GenerateAuthenticationOptionsOpts, - GenerateRegistrationOptionsOpts, - MetadataStatement, - VerifiedAuthenticationResponse, - VerifiedRegistrationResponse, - VerifyAuthenticationResponseOpts, - VerifyRegistrationResponseOpts, -}; +export * from './registration/generateRegistrationOptions.ts'; +export * from './registration/verifyRegistrationResponse.ts'; +export * from './authentication/generateAuthenticationOptions.ts'; +export * from './authentication/verifyAuthenticationResponse.ts'; +export * from './services/metadataService.ts'; +export * from './services/settingsService.ts'; +export * from './metadata/mdsTypes.ts'; +export * from './types/index.ts'; diff --git a/packages/server/src/metadata/mdsTypes.ts b/packages/server/src/metadata/mdsTypes.ts index 185fd58bb..51728da5e 100644 --- a/packages/server/src/metadata/mdsTypes.ts +++ b/packages/server/src/metadata/mdsTypes.ts @@ -1,9 +1,11 @@ -import type { Base64URLString } from '@simplewebauthn/types'; +import type { Base64URLString } from '../types/index.ts'; /** * Metadata Service structures * https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html */ + +/** */ export type MDSJWTHeader = { alg: string; typ: string; @@ -136,7 +138,9 @@ export type ExtensionDescriptor = { fail_if_unknown: boolean; }; -// langCode -> "en-US", "ja-JP", etc... +/** + * langCode -> "en-US", "ja-JP", etc... + */ export type AlternativeDescriptions = { [langCode: string]: string }; export type MetadataStatement = { diff --git a/packages/server/src/metadata/verifyAttestationWithMetadata.ts b/packages/server/src/metadata/verifyAttestationWithMetadata.ts index e9b73e025..757221bb0 100644 --- a/packages/server/src/metadata/verifyAttestationWithMetadata.ts +++ b/packages/server/src/metadata/verifyAttestationWithMetadata.ts @@ -1,10 +1,15 @@ -import type { Base64URLString } from '@simplewebauthn/types'; - +import type { Base64URLString } from '../types/index.ts'; import type { AlgSign, MetadataStatement } from '../metadata/mdsTypes.ts'; import { convertCertBufferToPEM } from '../helpers/convertCertBufferToPEM.ts'; import { validateCertificatePath } from '../helpers/validateCertificatePath.ts'; import { decodeCredentialPublicKey } from '../helpers/decodeCredentialPublicKey.ts'; -import { COSEALG, COSECRV, COSEKEYS, COSEKTY, isCOSEPublicKeyEC2 } from '../helpers/cose.ts'; +import { + type COSEALG, + type COSECRV, + COSEKEYS, + COSEKTY, + isCOSEPublicKeyEC2, +} from '../helpers/cose.ts'; /** * Match properties of the authenticator's attestation statement against expected values as diff --git a/packages/server/src/registration/generateRegistrationOptions.ts b/packages/server/src/registration/generateRegistrationOptions.ts index 1c8b7d3ca..a7f7bd821 100644 --- a/packages/server/src/registration/generateRegistrationOptions.ts +++ b/packages/server/src/registration/generateRegistrationOptions.ts @@ -7,8 +7,7 @@ import type { PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialHint, PublicKeyCredentialParameters, -} from '@simplewebauthn/types'; - +} from '../types/index.ts'; import { generateChallenge } from '../helpers/generateChallenge.ts'; import { generateUserID } from '../helpers/generateUserID.ts'; import { isoBase64URL, isoUint8Array } from '../helpers/iso/index.ts'; diff --git a/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.test.ts b/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.test.ts index 98756bfb2..53047e9ff 100644 --- a/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.test.ts +++ b/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.test.ts @@ -1,9 +1,9 @@ import { assert, assertRejects } from '@std/assert'; import { FakeTime } from '@std/testing/time'; -import type { RegistrationResponseJSON } from '@simplewebauthn/types'; import { verifyAttestationAndroidSafetyNet } from './verifyAttestationAndroidSafetyNet.ts'; +import type { RegistrationResponseJSON } from '../../types/index.ts'; import { decodeAttestationObject } from '../../helpers/decodeAttestationObject.ts'; import { parseAuthenticatorData } from '../../helpers/parseAuthenticatorData.ts'; import { toHash } from '../../helpers/toHash.ts'; diff --git a/packages/server/src/registration/verifyRegistrationResponse.test.ts b/packages/server/src/registration/verifyRegistrationResponse.test.ts index bcc176389..06aa0dd7c 100644 --- a/packages/server/src/registration/verifyRegistrationResponse.test.ts +++ b/packages/server/src/registration/verifyRegistrationResponse.test.ts @@ -1,8 +1,8 @@ import { assert, assertEquals, assertFalse, assertObjectMatch, assertRejects } from '@std/assert'; import { returnsNext, stub } from '@std/testing/mock'; -import type { RegistrationResponseJSON } from '@simplewebauthn/types'; import { verifyRegistrationResponse } from './verifyRegistrationResponse.ts'; +import type { RegistrationResponseJSON } from '../types/index.ts'; import { _decodeAttestationObjectInternals, decodeAttestationObject, diff --git a/packages/server/src/registration/verifyRegistrationResponse.ts b/packages/server/src/registration/verifyRegistrationResponse.ts index a1aa9f55b..3649f9bdd 100644 --- a/packages/server/src/registration/verifyRegistrationResponse.ts +++ b/packages/server/src/registration/verifyRegistrationResponse.ts @@ -3,14 +3,13 @@ import type { CredentialDeviceType, RegistrationResponseJSON, WebAuthnCredential, -} from '@simplewebauthn/types'; - +} from '../types/index.ts'; import { - AttestationFormat, - AttestationStatement, + type AttestationFormat, + type AttestationStatement, decodeAttestationObject, } from '../helpers/decodeAttestationObject.ts'; -import { AuthenticationExtensionsAuthenticatorOutputs } from '../helpers/decodeAuthenticatorExtensions.ts'; +import type { AuthenticationExtensionsAuthenticatorOutputs } from '../helpers/decodeAuthenticatorExtensions.ts'; import { decodeClientDataJSON } from '../helpers/decodeClientDataJSON.ts'; import { parseAuthenticatorData } from '../helpers/parseAuthenticatorData.ts'; import { toHash } from '../helpers/toHash.ts'; @@ -30,16 +29,10 @@ import { verifyAttestationTPM } from './verifications/tpm/verifyAttestationTPM.t import { verifyAttestationAndroidKey } from './verifications/verifyAttestationAndroidKey.ts'; import { verifyAttestationApple } from './verifications/verifyAttestationApple.ts'; -export type VerifyRegistrationResponseOpts = { - response: RegistrationResponseJSON; - expectedChallenge: string | ((challenge: string) => boolean | Promise); - expectedOrigin: string | string[]; - expectedRPID?: string | string[]; - expectedType?: string | string[]; - requireUserPresence?: boolean; - requireUserVerification?: boolean; - supportedAlgorithmIDs?: COSEAlgorithmIdentifier[]; -}; +/** + * Configurable options when calling `verifyRegistrationResponse()` + */ +export type VerifyRegistrationResponseOpts = Parameters[0]; /** * Verify that the user has legitimately completed the registration process @@ -56,7 +49,16 @@ export type VerifyRegistrationResponseOpts = { * @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 */ export async function verifyRegistrationResponse( - options: VerifyRegistrationResponseOpts, + options: { + response: RegistrationResponseJSON; + expectedChallenge: string | ((challenge: string) => boolean | Promise); + expectedOrigin: string | string[]; + expectedRPID?: string | string[]; + expectedType?: string | string[]; + requireUserPresence?: boolean; + requireUserVerification?: boolean; + supportedAlgorithmIDs?: COSEAlgorithmIdentifier[]; + }, ): Promise { const { response, diff --git a/packages/server/src/services/metadataService.ts b/packages/server/src/services/metadataService.ts index 0bca4a6d5..e310fc1f0 100644 --- a/packages/server/src/services/metadataService.ts +++ b/packages/server/src/services/metadataService.ts @@ -35,18 +35,39 @@ enum SERVICE_STATE { READY, } -// Allow MetadataService to accommodate unregistered AAGUIDs ("permissive"), or only allow -// registered AAGUIDs ("strict"). Currently primarily impacts how `getStatement()` operates -type VerificationMode = 'permissive' | 'strict'; +/** + * Allow MetadataService to accommodate unregistered AAGUIDs (`"permissive"`), or only allow + * registered AAGUIDs (`"strict"`). Currently primarily impacts how `getStatement()` operates + */ +export type VerificationMode = 'permissive' | 'strict'; const log = getLogger('MetadataService'); interface MetadataService { + /** + * Prepare the service to handle remote MDS servers and/or cache local metadata statements. + * + * **Options:** + * + * @param opts.mdsServers An array of URLs to FIDO Alliance Metadata Service + * (version 3.0)-compatible servers. Defaults to the official FIDO MDS server + * @param opts.statements An array of local metadata statements + * @param opts.verificationMode How MetadataService will handle unregistered AAGUIDs. Defaults to + * `"strict"` which throws errors during registration response verification when an + * unregistered AAGUID is encountered. Set to `"permissive"` to allow registration by + * authenticators with unregistered AAGUIDs + */ initialize(opts?: { mdsServers?: string[]; statements?: MetadataStatement[]; verificationMode?: VerificationMode; }): Promise; + /** + * Get a metadata statement for a given AAGUID. + * + * This method will coordinate updating the cache as per the `nextUpdate` property in the initial + * BLOB download. + */ getStatement(aaguid: string | Uint8Array): Promise; } @@ -62,19 +83,6 @@ export class BaseMetadataService implements MetadataService { private state: SERVICE_STATE = SERVICE_STATE.DISABLED; private verificationMode: VerificationMode = 'strict'; - /** - * Prepare the service to handle remote MDS servers and/or cache local metadata statements. - * - * **Options:** - * - * @param opts.mdsServers An array of URLs to FIDO Alliance Metadata Service - * (version 3.0)-compatible servers. Defaults to the official FIDO MDS server - * @param opts.statements An array of local metadata statements - * @param opts.verificationMode How MetadataService will handle unregistered AAGUIDs. Defaults to - * `"strict"` which throws errors during registration response verification when an - * unregistered AAGUID is encountered. Set to `"permissive"` to allow registration by - * authenticators with unregistered AAGUIDs - */ async initialize( opts: { mdsServers?: string[]; @@ -144,12 +152,6 @@ export class BaseMetadataService implements MetadataService { this.setState(SERVICE_STATE.READY); } - /** - * Get a metadata statement for a given AAGUID. - * - * This method will coordinate updating the cache as per the `nextUpdate` property in the initial - * BLOB download. - */ async getStatement( aaguid: string | Uint8Array, ): Promise { diff --git a/packages/server/src/services/settingsService.ts b/packages/server/src/services/settingsService.ts index 5cd567524..6f10ea056 100644 --- a/packages/server/src/services/settingsService.ts +++ b/packages/server/src/services/settingsService.ts @@ -9,13 +9,24 @@ import { import { Apple_WebAuthn_Root_CA } from './defaultRootCerts/apple.ts'; import { GlobalSign_Root_CA_R3 } from './defaultRootCerts/mds.ts'; -type RootCertIdentifier = AttestationFormat | 'mds'; +export type RootCertIdentifier = AttestationFormat | 'mds'; interface SettingsService { + /** + * Set potential root certificates for attestation formats that use them. Root certs will be tried + * one-by-one when validating a certificate path. + * + * Certificates can be specified as a raw `Buffer`, or as a PEM-formatted string. If a + * `Buffer` is passed in it will be converted to PEM format. + */ setRootCertificates(opts: { identifier: RootCertIdentifier; certificates: (Uint8Array | string)[]; }): void; + + /** + * Get any registered root certificates for the specified attestation format + */ getRootCertificates(opts: { identifier: RootCertIdentifier }): string[]; } @@ -27,13 +38,6 @@ class BaseSettingsService implements SettingsService { this.pemCertificates = new Map(); } - /** - * Set potential root certificates for attestation formats that use them. Root certs will be tried - * one-by-one when validating a certificate path. - * - * Certificates can be specified as a raw `Buffer`, or as a PEM-formatted string. If a - * `Buffer` is passed in it will be converted to PEM format. - */ setRootCertificates(opts: { identifier: RootCertIdentifier; certificates: (Uint8Array | string)[]; @@ -52,9 +56,6 @@ class BaseSettingsService implements SettingsService { this.pemCertificates.set(identifier, newCertificates); } - /** - * Get any registered root certificates for the specified attestation format - */ getRootCertificates(opts: { identifier: RootCertIdentifier }): string[] { const { identifier } = opts; return this.pemCertificates.get(identifier) ?? []; diff --git a/packages/server/src/types/dom.ts b/packages/server/src/types/dom.ts new file mode 100644 index 000000000..1caddc0dc --- /dev/null +++ b/packages/server/src/types/dom.ts @@ -0,0 +1,373 @@ +// deno-fmt-ignore-file +/** + * DO NOT MODIFY THESE FILES! + * + * These files were copied from the **types** package. To update this file, make changes to those + * files instead and then run the following command from the monorepo root folder: + * + * deno task codegen:types + */ +// BEGIN CODEGEN +/** + * Generated from typescript@5.6.3 + * To regenerate, run the following command from the package root: + * deno task extract-dom-types + */ +/** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAssertionResponse) + */ +export interface AuthenticatorAssertionResponse extends AuthenticatorResponse { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAssertionResponse/authenticatorData) */ + readonly authenticatorData: ArrayBuffer; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAssertionResponse/signature) */ + readonly signature: ArrayBuffer; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAssertionResponse/userHandle) */ + readonly userHandle: ArrayBuffer | null; +} + +/** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse) + */ +export interface AuthenticatorAttestationResponse extends AuthenticatorResponse { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse/attestationObject) */ + readonly attestationObject: ArrayBuffer; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse/getAuthenticatorData) */ + getAuthenticatorData(): ArrayBuffer; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse/getPublicKey) */ + getPublicKey(): ArrayBuffer | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse/getPublicKeyAlgorithm) */ + getPublicKeyAlgorithm(): COSEAlgorithmIdentifier; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse/getTransports) */ + getTransports(): string[]; +} + +export interface AuthenticationExtensionsClientInputs { + appid?: string; + credProps?: boolean; + hmacCreateSecret?: boolean; + minPinLength?: boolean; +} + +export interface AuthenticationExtensionsClientOutputs { + appid?: boolean; + credProps?: CredentialPropertiesOutput; + hmacCreateSecret?: boolean; +} + +export interface AuthenticatorSelectionCriteria { + authenticatorAttachment?: AuthenticatorAttachment; + requireResidentKey?: boolean; + residentKey?: ResidentKeyRequirement; + userVerification?: UserVerificationRequirement; +} + +/** + * Basic cryptography features available in the current context. It allows access to a cryptographically strong random number generator and to cryptographic primitives. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto) + */ +export interface Crypto { + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/subtle) + */ + readonly subtle: SubtleCrypto; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues) */ + getRandomValues(array: T): T; + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/randomUUID) + */ + randomUUID(): `${string}-${string}-${string}-${string}-${string}`; +} + +/** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential) + */ +export interface PublicKeyCredential extends Credential { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential/authenticatorAttachment) */ + readonly authenticatorAttachment: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential/rawId) */ + readonly rawId: ArrayBuffer; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential/response) */ + readonly response: AuthenticatorResponse; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential/getClientExtensionResults) */ + getClientExtensionResults(): AuthenticationExtensionsClientOutputs; +} + +export interface PublicKeyCredentialCreationOptions { + attestation?: AttestationConveyancePreference; + authenticatorSelection?: AuthenticatorSelectionCriteria; + challenge: BufferSource; + excludeCredentials?: PublicKeyCredentialDescriptor[]; + extensions?: AuthenticationExtensionsClientInputs; + pubKeyCredParams: PublicKeyCredentialParameters[]; + rp: PublicKeyCredentialRpEntity; + timeout?: number; + user: PublicKeyCredentialUserEntity; +} + +export interface PublicKeyCredentialDescriptor { + id: BufferSource; + transports?: AuthenticatorTransport[]; + type: PublicKeyCredentialType; +} + +export interface PublicKeyCredentialParameters { + alg: COSEAlgorithmIdentifier; + type: PublicKeyCredentialType; +} + +export interface PublicKeyCredentialRequestOptions { + allowCredentials?: PublicKeyCredentialDescriptor[]; + challenge: BufferSource; + extensions?: AuthenticationExtensionsClientInputs; + rpId?: string; + timeout?: number; + userVerification?: UserVerificationRequirement; +} + +export interface PublicKeyCredentialUserEntity extends PublicKeyCredentialEntity { + displayName: string; + id: BufferSource; +} + +/** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorResponse) + */ +export interface AuthenticatorResponse { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AuthenticatorResponse/clientDataJSON) */ + readonly clientDataJSON: ArrayBuffer; +} + +export interface CredentialPropertiesOutput { + rk?: boolean; +} + +/** + * This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via Window.crypto). + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto) + */ +export interface SubtleCrypto { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/decrypt) */ + decrypt(algorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams, key: CryptoKey, data: BufferSource): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveBits) */ + deriveBits(algorithm: AlgorithmIdentifier | EcdhKeyDeriveParams | HkdfParams | Pbkdf2Params, baseKey: CryptoKey, length: number): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveKey) */ + deriveKey(algorithm: AlgorithmIdentifier | EcdhKeyDeriveParams | HkdfParams | Pbkdf2Params, baseKey: CryptoKey, derivedKeyType: AlgorithmIdentifier | AesDerivedKeyParams | HmacImportParams | HkdfParams | Pbkdf2Params, extractable: boolean, keyUsages: KeyUsage[]): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/digest) */ + digest(algorithm: AlgorithmIdentifier, data: BufferSource): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/encrypt) */ + encrypt(algorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams, key: CryptoKey, data: BufferSource): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/exportKey) */ + exportKey(format: "jwk", key: CryptoKey): Promise; + exportKey(format: Exclude, key: CryptoKey): Promise; + exportKey(format: KeyFormat, key: CryptoKey): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/generateKey) */ + generateKey(algorithm: "Ed25519", extractable: boolean, keyUsages: ReadonlyArray<"sign" | "verify">): Promise; + generateKey(algorithm: RsaHashedKeyGenParams | EcKeyGenParams, extractable: boolean, keyUsages: ReadonlyArray): Promise; + generateKey(algorithm: AesKeyGenParams | HmacKeyGenParams | Pbkdf2Params, extractable: boolean, keyUsages: ReadonlyArray): Promise; + generateKey(algorithm: AlgorithmIdentifier, extractable: boolean, keyUsages: KeyUsage[]): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/importKey) */ + importKey(format: "jwk", keyData: JsonWebKey, algorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm, extractable: boolean, keyUsages: ReadonlyArray): Promise; + importKey(format: Exclude, keyData: BufferSource, algorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/sign) */ + sign(algorithm: AlgorithmIdentifier | RsaPssParams | EcdsaParams, key: CryptoKey, data: BufferSource): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/unwrapKey) */ + unwrapKey(format: KeyFormat, wrappedKey: BufferSource, unwrappingKey: CryptoKey, unwrapAlgorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams, unwrappedKeyAlgorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/verify) */ + verify(algorithm: AlgorithmIdentifier | RsaPssParams | EcdsaParams, key: CryptoKey, signature: BufferSource, data: BufferSource): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/wrapKey) */ + wrapKey(format: KeyFormat, key: CryptoKey, wrappingKey: CryptoKey, wrapAlgorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams): Promise; +} + +/** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Credential) + */ +export interface Credential { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Credential/id) */ + readonly id: string; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Credential/type) */ + readonly type: string; +} + +export interface PublicKeyCredentialRpEntity extends PublicKeyCredentialEntity { + id?: string; +} + +export interface PublicKeyCredentialEntity { + name: string; +} + +export interface RsaOaepParams extends Algorithm { + label?: BufferSource; +} + +export interface AesCtrParams extends Algorithm { + counter: BufferSource; + length: number; +} + +export interface AesCbcParams extends Algorithm { + iv: BufferSource; +} + +export interface AesGcmParams extends Algorithm { + additionalData?: BufferSource; + iv: BufferSource; + tagLength?: number; +} + +/** + * The CryptoKey dictionary of the Web Crypto API represents a cryptographic key. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey) + */ +export interface CryptoKey { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/algorithm) */ + readonly algorithm: KeyAlgorithm; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/extractable) */ + readonly extractable: boolean; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/type) */ + readonly type: KeyType; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/usages) */ + readonly usages: KeyUsage[]; +} + +export interface EcdhKeyDeriveParams extends Algorithm { + public: CryptoKey; +} + +export interface HkdfParams extends Algorithm { + hash: HashAlgorithmIdentifier; + info: BufferSource; + salt: BufferSource; +} + +export interface Pbkdf2Params extends Algorithm { + hash: HashAlgorithmIdentifier; + iterations: number; + salt: BufferSource; +} + +export interface AesDerivedKeyParams extends Algorithm { + length: number; +} + +export interface HmacImportParams extends Algorithm { + hash: HashAlgorithmIdentifier; + length?: number; +} + +export interface JsonWebKey { + alg?: string; + crv?: string; + d?: string; + dp?: string; + dq?: string; + e?: string; + ext?: boolean; + k?: string; + key_ops?: string[]; + kty?: string; + n?: string; + oth?: RsaOtherPrimesInfo[]; + p?: string; + q?: string; + qi?: string; + use?: string; + x?: string; + y?: string; +} + +export interface CryptoKeyPair { + privateKey: CryptoKey; + publicKey: CryptoKey; +} + +export interface RsaHashedKeyGenParams extends RsaKeyGenParams { + hash: HashAlgorithmIdentifier; +} + +export interface EcKeyGenParams extends Algorithm { + namedCurve: NamedCurve; +} + +export interface AesKeyGenParams extends Algorithm { + length: number; +} + +export interface HmacKeyGenParams extends Algorithm { + hash: HashAlgorithmIdentifier; + length?: number; +} + +export interface RsaHashedImportParams extends Algorithm { + hash: HashAlgorithmIdentifier; +} + +export interface EcKeyImportParams extends Algorithm { + namedCurve: NamedCurve; +} + +export interface AesKeyAlgorithm extends KeyAlgorithm { + length: number; +} + +export interface RsaPssParams extends Algorithm { + saltLength: number; +} + +export interface EcdsaParams extends Algorithm { + hash: HashAlgorithmIdentifier; +} + +export interface Algorithm { + name: string; +} + +export interface KeyAlgorithm { + name: string; +} + +export interface RsaOtherPrimesInfo { + d?: string; + r?: string; + t?: string; +} + +export interface RsaKeyGenParams extends Algorithm { + modulusLength: number; + publicExponent: BigInteger; +} + +export type AttestationConveyancePreference = "direct" | "enterprise" | "indirect" | "none"; +export type AuthenticatorTransport = "ble" | "hybrid" | "internal" | "nfc" | "usb"; +export type COSEAlgorithmIdentifier = number; +export type UserVerificationRequirement = "discouraged" | "preferred" | "required"; +export type AuthenticatorAttachment = "cross-platform" | "platform"; +export type ResidentKeyRequirement = "discouraged" | "preferred" | "required"; +export type BufferSource = ArrayBufferView | ArrayBuffer; +export type PublicKeyCredentialType = "public-key"; +export type AlgorithmIdentifier = Algorithm | string; +export type KeyUsage = "decrypt" | "deriveBits" | "deriveKey" | "encrypt" | "sign" | "unwrapKey" | "verify" | "wrapKey"; +export type KeyFormat = "jwk" | "pkcs8" | "raw" | "spki"; +export type KeyType = "private" | "public" | "secret"; +export type HashAlgorithmIdentifier = AlgorithmIdentifier; +export type NamedCurve = string; +export type BigInteger = Uint8Array; diff --git a/packages/server/src/types/index.ts b/packages/server/src/types/index.ts new file mode 100644 index 000000000..7ce146edc --- /dev/null +++ b/packages/server/src/types/index.ts @@ -0,0 +1,294 @@ +// deno-fmt-ignore-file +/** + * DO NOT MODIFY THESE FILES! + * + * These files were copied from the **types** package. To update this file, make changes to those + * files instead and then run the following command from the monorepo root folder: + * + * deno task codegen:types + */ +// BEGIN CODEGEN +import type { + AttestationConveyancePreference, + AuthenticationExtensionsClientInputs, + AuthenticationExtensionsClientOutputs, + AuthenticatorAssertionResponse, + AuthenticatorAttachment, + AuthenticatorAttestationResponse, + AuthenticatorSelectionCriteria, + COSEAlgorithmIdentifier, + PublicKeyCredential, + PublicKeyCredentialCreationOptions, + PublicKeyCredentialDescriptor, + PublicKeyCredentialParameters, + PublicKeyCredentialRequestOptions, + PublicKeyCredentialRpEntity, + PublicKeyCredentialType, + UserVerificationRequirement, +} from './dom.ts'; + +export type { + AttestationConveyancePreference, + AuthenticationExtensionsClientInputs, + AuthenticationExtensionsClientOutputs, + AuthenticatorAssertionResponse, + AuthenticatorAttachment, + AuthenticatorAttestationResponse, + AuthenticatorSelectionCriteria, + AuthenticatorTransport, + COSEAlgorithmIdentifier, + Crypto, + PublicKeyCredential, + PublicKeyCredentialCreationOptions, + PublicKeyCredentialDescriptor, + PublicKeyCredentialParameters, + PublicKeyCredentialRequestOptions, + PublicKeyCredentialRpEntity, + PublicKeyCredentialType, + PublicKeyCredentialUserEntity, + UserVerificationRequirement, +} from './dom.ts'; + +/** + * A variant of PublicKeyCredentialCreationOptions suitable for JSON transmission to the browser to + * (eventually) get passed into navigator.credentials.create(...) in the browser. + * + * This should eventually get replaced with official TypeScript DOM types when WebAuthn L3 types + * eventually make it into the language: + * + * https://w3c.github.io/webauthn/#dictdef-publickeycredentialcreationoptionsjson + */ +export interface PublicKeyCredentialCreationOptionsJSON { + rp: PublicKeyCredentialRpEntity; + user: PublicKeyCredentialUserEntityJSON; + challenge: Base64URLString; + pubKeyCredParams: PublicKeyCredentialParameters[]; + timeout?: number; + excludeCredentials?: PublicKeyCredentialDescriptorJSON[]; + authenticatorSelection?: AuthenticatorSelectionCriteria; + hints?: PublicKeyCredentialHint[]; + attestation?: AttestationConveyancePreference; + attestationFormats?: AttestationFormat[]; + extensions?: AuthenticationExtensionsClientInputs; +} + +/** + * A variant of PublicKeyCredentialRequestOptions suitable for JSON transmission to the browser to + * (eventually) get passed into navigator.credentials.get(...) in the browser. + */ +export interface PublicKeyCredentialRequestOptionsJSON { + challenge: Base64URLString; + timeout?: number; + rpId?: string; + allowCredentials?: PublicKeyCredentialDescriptorJSON[]; + userVerification?: UserVerificationRequirement; + hints?: PublicKeyCredentialHint[]; + extensions?: AuthenticationExtensionsClientInputs; +} + +/** + * https://w3c.github.io/webauthn/#dictdef-publickeycredentialdescriptorjson + */ +export interface PublicKeyCredentialDescriptorJSON { + id: Base64URLString; + type: PublicKeyCredentialType; + transports?: AuthenticatorTransportFuture[]; +} + +/** + * https://w3c.github.io/webauthn/#dictdef-publickeycredentialuserentityjson + */ +export interface PublicKeyCredentialUserEntityJSON { + id: string; + name: string; + displayName: string; +} + +/** + * The value returned from navigator.credentials.create() + */ +export interface RegistrationCredential extends PublicKeyCredentialFuture { + response: AuthenticatorAttestationResponseFuture; +} + +/** + * A slightly-modified RegistrationCredential to simplify working with ArrayBuffers that + * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. + * + * https://w3c.github.io/webauthn/#dictdef-registrationresponsejson + */ +export interface RegistrationResponseJSON { + id: Base64URLString; + rawId: Base64URLString; + response: AuthenticatorAttestationResponseJSON; + authenticatorAttachment?: AuthenticatorAttachment; + clientExtensionResults: AuthenticationExtensionsClientOutputs; + type: PublicKeyCredentialType; +} + +/** + * The value returned from navigator.credentials.get() + */ +export interface AuthenticationCredential extends PublicKeyCredentialFuture { + response: AuthenticatorAssertionResponse; +} + +/** + * A slightly-modified AuthenticationCredential to simplify working with ArrayBuffers that + * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. + * + * https://w3c.github.io/webauthn/#dictdef-authenticationresponsejson + */ +export interface AuthenticationResponseJSON { + id: Base64URLString; + rawId: Base64URLString; + response: AuthenticatorAssertionResponseJSON; + authenticatorAttachment?: AuthenticatorAttachment; + clientExtensionResults: AuthenticationExtensionsClientOutputs; + type: PublicKeyCredentialType; +} + +/** + * A slightly-modified AuthenticatorAttestationResponse to simplify working with ArrayBuffers that + * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. + * + * https://w3c.github.io/webauthn/#dictdef-authenticatorattestationresponsejson + */ +export interface AuthenticatorAttestationResponseJSON { + clientDataJSON: Base64URLString; + attestationObject: Base64URLString; + // Optional in L2, but becomes required in L3. Play it safe until L3 becomes Recommendation + authenticatorData?: Base64URLString; + // Optional in L2, but becomes required in L3. Play it safe until L3 becomes Recommendation + transports?: AuthenticatorTransportFuture[]; + // Optional in L2, but becomes required in L3. Play it safe until L3 becomes Recommendation + publicKeyAlgorithm?: COSEAlgorithmIdentifier; + publicKey?: Base64URLString; +} + +/** + * A slightly-modified AuthenticatorAssertionResponse to simplify working with ArrayBuffers that + * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. + * + * https://w3c.github.io/webauthn/#dictdef-authenticatorassertionresponsejson + */ +export interface AuthenticatorAssertionResponseJSON { + clientDataJSON: Base64URLString; + authenticatorData: Base64URLString; + signature: Base64URLString; + userHandle?: Base64URLString; +} + +/** + * Public key credential information needed to verify authentication responses + */ +export type WebAuthnCredential = { + id: Base64URLString; + publicKey: Uint8Array; + // Number of times this authenticator is expected to have been used + counter: number; + // From browser's `startRegistration()` -> RegistrationCredentialJSON.transports (API L2 and up) + transports?: AuthenticatorTransportFuture[]; +}; + +/** + * An attempt to communicate that this isn't just any string, but a Base64URL-encoded string + */ +export type Base64URLString = string; + +/** + * AuthenticatorAttestationResponse in TypeScript's DOM lib is outdated (up through v3.9.7). + * Maintain an augmented version here so we can implement additional properties as the WebAuthn + * spec evolves. + * + * See https://www.w3.org/TR/webauthn-2/#iface-authenticatorattestationresponse + * + * Properties marked optional are not supported in all browsers. + */ +export interface AuthenticatorAttestationResponseFuture extends AuthenticatorAttestationResponse { + getTransports(): AuthenticatorTransportFuture[]; +} + +/** + * A super class of TypeScript's `AuthenticatorTransport` that includes support for the latest + * transports. Should eventually be replaced by TypeScript's when TypeScript gets updated to + * know about it (sometime after 4.6.3) + */ +export type AuthenticatorTransportFuture = + | 'ble' + | 'cable' + | 'hybrid' + | 'internal' + | 'nfc' + | 'smart-card' + | 'usb'; + +/** + * A super class of TypeScript's `PublicKeyCredentialDescriptor` that knows about the latest + * transports. Should eventually be replaced by TypeScript's when TypeScript gets updated to + * know about it (sometime after 4.6.3) + */ +export interface PublicKeyCredentialDescriptorFuture + extends Omit { + transports?: AuthenticatorTransportFuture[]; +} + +/** */ +export type PublicKeyCredentialJSON = + | RegistrationResponseJSON + | AuthenticationResponseJSON; + +/** + * A super class of TypeScript's `PublicKeyCredential` that knows about upcoming WebAuthn features + */ +export interface PublicKeyCredentialFuture extends PublicKeyCredential { + type: PublicKeyCredentialType; + // See https://github.com/w3c/webauthn/issues/1745 + isConditionalMediationAvailable?(): Promise; + // See https://w3c.github.io/webauthn/#sctn-parseCreationOptionsFromJSON + parseCreationOptionsFromJSON?( + options: PublicKeyCredentialCreationOptionsJSON, + ): PublicKeyCredentialCreationOptions; + // See https://w3c.github.io/webauthn/#sctn-parseRequestOptionsFromJSON + parseRequestOptionsFromJSON?( + options: PublicKeyCredentialRequestOptionsJSON, + ): PublicKeyCredentialRequestOptions; + // See https://w3c.github.io/webauthn/#dom-publickeycredential-tojson + toJSON?(): PublicKeyCredentialJSON; +} + +/** + * The two types of credentials as defined by bit 3 ("Backup Eligibility") in authenticator data: + * - `"singleDevice"` credentials will never be backed up + * - `"multiDevice"` credentials can be backed up + */ +export type CredentialDeviceType = 'singleDevice' | 'multiDevice'; + +/** + * Categories of authenticators that Relying Parties can pass along to browsers during + * registration. Browsers that understand these values can optimize their modal experience to + * start the user off in a particular registration flow: + * + * - `hybrid`: A platform authenticator on a mobile device + * - `security-key`: A portable FIDO2 authenticator capable of being used on multiple devices via a USB or NFC connection + * - `client-device`: The device that WebAuthn is being called on. Typically synonymous with platform authenticators + * + * See https://w3c.github.io/webauthn/#enumdef-publickeycredentialhint + * + * These values are less strict than `authenticatorAttachment` + */ +export type PublicKeyCredentialHint = 'hybrid' | 'security-key' | 'client-device'; + +/** + * Values for an attestation object's `fmt` + * + * See https://www.iana.org/assignments/webauthn/webauthn.xhtml#webauthn-attestation-statement-format-ids + */ +export type AttestationFormat = + | 'fido-u2f' + | 'packed' + | 'android-safetynet' + | 'android-key' + | 'tpm' + | 'apple' + | 'none'; diff --git a/packages/types/LICENSE.md b/packages/types/LICENSE.md deleted file mode 100644 index adb1965c5..000000000 --- a/packages/types/LICENSE.md +++ /dev/null @@ -1,18 +0,0 @@ -MIT License - -Copyright (c) 2020 Matthew Miller - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, -sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES -OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/types/README.md b/packages/types/README.md index 71d07d410..c1eeb4ec3 100644 --- a/packages/types/README.md +++ b/packages/types/README.md @@ -1,28 +1,18 @@ # @simplewebauthn/types -![WebAuthn](https://img.shields.io/badge/WebAuthn-Simplified-blueviolet?style=for-the-badge&logo=WebAuthn) -[![npm (scoped)](https://img.shields.io/npm/v/@simplewebauthn/types?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@simplewebauthn/types) -[![JSR](https://jsr.io/badges/@simplewebauthn/types?style=for-the-badge)](https://jsr.io/@simplewebauthn/types) +TypeScript typings for **@simplewebauthn/server** and **@simplewebauthn/browser**. -TypeScript typings for **@simplewebauthn/server** and **@simplewebauthn/browser** +> NOTE: This package was formerly published as **@simplewebauthn/types** -- [Installation](#installation) - - [Node LTS 20.x and higher](#node-lts-20x-and-higher) - - [Deno v1.43 and higher](#deno-v143-and-higher) +## Including these types in other packages -## Installation - -This package can be installed from **[NPM](https://www.npmjs.com/package/@simplewebauthn/types)** -and **[JSR](https://jsr.io/@simplewebauthn/types)**: - -### Node LTS 20.x and higher +The types in this package are codegen'd into **@simplewebauthn/browser** and +**@simplewebauthn/server** so that the types are within those packages. When changes are made to the +typings here, run the following command to copy them into the other packages: ```sh -npm install @simplewebauthn/types +deno task codegen ``` -### Deno v1.43 and higher - -```sh -deno add jsr:@simplewebauthn/types -``` +Commit the copied-over code as well so that changes to them are tracked just like any other change +to their codebases. diff --git a/packages/types/build_npm.ts b/packages/types/build_npm.ts deleted file mode 100644 index 8bc60f0f2..000000000 --- a/packages/types/build_npm.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { build, emptyDir } from '@deno/dnt'; - -import denoJSON from './deno.json' with { type: 'json' }; - -const outDir = './npm'; - -await emptyDir(outDir); - -await build({ - entryPoints: ['./src/index.ts'], - outDir, - shims: {}, - // Keeping declarations in a single types/ directory to mimic the original file structure - declaration: 'separate', - test: false, - // package.json values - package: { - name: '@simplewebauthn/types', - version: denoJSON.version, - description: 'TypeScript types used by the @simplewebauthn series of libraries', - license: 'MIT', - author: 'Matthew Miller ', - repository: { - type: 'git', - url: 'git+https://github.com/MasterKale/SimpleWebAuthn.git', - directory: 'packages/types', - }, - homepage: 'https://github.com/MasterKale/SimpleWebAuthn/tree/master/packages/types#readme', - publishConfig: { - access: 'public', - }, - bugs: { - url: 'https://github.com/MasterKale/SimpleWebAuthn/issues', - }, - keywords: [ - 'typescript', - 'webauthn', - 'passkeys', - 'fido', - 'types', - ], - }, -}); - -Deno.copyFileSync('LICENSE.md', `${outDir}/LICENSE.md`); -Deno.copyFileSync('README.md', `${outDir}/README.md`); diff --git a/packages/types/codegen.ts b/packages/types/codegen.ts new file mode 100644 index 000000000..3994c9b82 --- /dev/null +++ b/packages/types/codegen.ts @@ -0,0 +1,59 @@ +const sourcePath = './src'; +const outputPaths = [ + '../browser/src/types', + '../server/src/types', +]; +// Spread to an array so we consume the `Iterable` from `.readDirSync()`. This lets us read these +// file properties multiple times while only reading the directory once. +const sourceFiles = [...Deno.readDirSync(sourcePath)]; + +const codegenNotice = `// deno-fmt-ignore-file +/** + * DO NOT MODIFY THESE FILES! + * + * These files were copied from the **types** package. To update this file, make changes to those + * files instead and then run the following command from the monorepo root folder: + * + * deno task codegen:types + */ +// BEGIN CODEGEN +`; + +/** + * Copy files to each output target + */ +for (const outputPath of outputPaths) { + console.log(`DESTINATION: ${outputPath}`); + + try { + // Make sure the folder exists in the target package + console.log(`Making sure output folder exists...`); + await Deno.mkdir(outputPath); + } catch (_err) { + // The folder already exists, keep going + } + + for (const file of sourceFiles) { + if (file.isFile) { + const fileInputPath = `${sourcePath}/${file.name}`; + const fileOutputPath = `${outputPath}/${file.name}`; + + // Read in original file + let fileContents = await Deno.readTextFile(fileInputPath); + + // Make sure the output file exists + await Deno.create(fileOutputPath); + + // Trim some content from the files being copied over + fileContents = fileContents.replace('// deno-fmt-ignore-file\n', ''); + fileContents = fileContents.replace('// BEGIN CODEGEN\n', ''); + + // Prepend the codegen notice to the file contents + const fileContentsWithNotice = `${codegenNotice}${fileContents}`; + + // Write the file + console.log(`Writing ${fileOutputPath}...`); + await Deno.writeTextFile(fileOutputPath, fileContentsWithNotice); + } + } +} diff --git a/packages/types/deno.json b/packages/types/deno.json index 87ea1bd47..9da8ae5e7 100644 --- a/packages/types/deno.json +++ b/packages/types/deno.json @@ -1,18 +1,16 @@ { - "name": "@simplewebauthn/types", - "version": "12.0.0", + "name": "@simplewebauthn/types-unpublished", + "version": "do-not-publish-this-package", "exports": "./src/index.ts", "tasks": { - "build": "deno run -A build_npm.ts", "extract-dom-types": "deno run -A extract-dom-types.ts", - "publish": { - "command": "deno task publish:jsr && deno task publish:npm", + "codegen": { + "description": "Copy the types in this project into the browser and server packages", + "command": "deno run -A codegen.ts", "dependencies": [ - "build" + "extract-dom-types" ] - }, - "publish:jsr": "deno publish", - "publish:npm": "(cd npm; npm publish)" + } }, "fmt": { "singleQuote": true, diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 2183274e3..9cf7d7a01 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module @simplewebauthn/types - */ - import type { AttestationConveyancePreference, AuthenticationExtensionsClientInputs,