diff options
-rw-r--r-- | packages/server/src/attestation/generateAttestationOptions.test.ts | 98 | ||||
-rw-r--r-- | packages/server/src/attestation/generateAttestationOptions.ts | 97 |
2 files changed, 104 insertions, 91 deletions
diff --git a/packages/server/src/attestation/generateAttestationOptions.test.ts b/packages/server/src/attestation/generateAttestationOptions.test.ts index 3885dfc..73218bf 100644 --- a/packages/server/src/attestation/generateAttestationOptions.test.ts +++ b/packages/server/src/attestation/generateAttestationOptions.test.ts @@ -5,58 +5,54 @@ test('should generate credential request options suitable for sending via JSON', const rpID = 'not.real'; const challenge = 'totallyrandomvalue'; const userID = '1234'; - const username = 'usernameHere'; + const userName = 'usernameHere'; const timeout = 1; const attestationType = 'indirect'; - const options = generateAttestationOptions( + const options = generateAttestationOptions({ serviceName, rpID, challenge, userID, - username, + userName, timeout, attestationType, - ); + }); expect(options).toEqual({ - publicKey: { - challenge, - rp: { - name: serviceName, - id: rpID, - }, - user: { - id: userID, - name: username, - displayName: username, - }, - pubKeyCredParams: [ - { - alg: -7, - type: 'public-key', - }, - ], - timeout, - attestation: attestationType, - excludeCredentials: [], + challenge, + rp: { + name: serviceName, + id: rpID, + }, + user: { + id: userID, + name: userName, + displayName: userName, }, + pubKeyCredParams: [ + { + alg: -7, + type: 'public-key', + }, + ], + timeout, + attestation: attestationType, + excludeCredentials: [], }); }); test('should map excluded credential IDs if specified', () => { - const options = generateAttestationOptions( - 'SimpleWebAuthn', - 'not.real', - 'totallyrandomvalue', - '1234', - 'usernameHere', - undefined, - undefined, - ['someIDhere'], - ); + const options = generateAttestationOptions({ + serviceName: 'SimpleWebAuthn', + rpID: 'not.real', + challenge: 'totallyrandomvalue', + userID: '1234', + userName: 'usernameHere', + excludedBase64CredentialIDs: ['someIDhere'], + }); - expect(options.publicKey.excludeCredentials).toEqual([{ + expect(options.excludeCredentials).toEqual([{ id: 'someIDhere', type: 'public-key', transports: ['usb', 'ble', 'nfc', 'internal'], @@ -64,25 +60,25 @@ test('should map excluded credential IDs if specified', () => { }); test('defaults to 60 seconds if no timeout is specified', () => { - const options = generateAttestationOptions( - 'SimpleWebAuthn', - 'not.real', - 'totallyrandomvalue', - '1234', - 'usernameHere', - ); + const options = generateAttestationOptions({ + serviceName: 'SimpleWebAuthn', + rpID: 'not.real', + challenge: 'totallyrandomvalue', + userID: '1234', + userName: 'usernameHere', + }); - expect(options.publicKey.timeout).toEqual(60000); + expect(options.timeout).toEqual(60000); }); test('defaults to direct attestation if no attestation type is specified', () => { - const options = generateAttestationOptions( - 'SimpleWebAuthn', - 'not.real', - 'totallyrandomvalue', - '1234', - 'usernameHere', - ); + const options = generateAttestationOptions({ + serviceName: 'SimpleWebAuthn', + rpID: 'not.real', + challenge: 'totallyrandomvalue', + userID: '1234', + userName: 'usernameHere', + }); - expect(options.publicKey.attestation).toEqual('direct'); + expect(options.attestation).toEqual('none'); }); diff --git a/packages/server/src/attestation/generateAttestationOptions.ts b/packages/server/src/attestation/generateAttestationOptions.ts index 27931fa..65fbdff 100644 --- a/packages/server/src/attestation/generateAttestationOptions.ts +++ b/packages/server/src/attestation/generateAttestationOptions.ts @@ -1,57 +1,74 @@ -import { PublicKeyCredentialCreationOptionsJSON } from '@simplewebauthn/typescript-types'; +import { + PublicKeyCredentialCreationOptionsJSON, +} from '@simplewebauthn/typescript-types'; + +type Options = { + serviceName: string, + rpID: string, + challenge: string, + userID: string, + userName: string, + userDisplayName?: string, + timeout?: number, + attestationType?: AttestationConveyancePreference, + excludedBase64CredentialIDs?: string[], + suggestedTransports?: AuthenticatorTransport[], +}; /** * Prepare a value to pass into navigator.credentials.create(...) for authenticator "registration" * + * **Options:** + * * @param serviceName Friendly user-visible website name * @param rpID Valid domain name (after `https://`) * @param challenge Random string the authenticator needs to sign and pass back * @param userID User's website-specific unique ID - * @param username User's website-specific username + * @param userName User's website-specific username (email, etc...) + * @param userDisplayName User's actual name * @param timeout How long (in ms) the user can take to complete attestation - * @param attestationType Request a full ("direct") or anonymized ("indirect") attestation statement + * @param attestationType Specific attestation statement * @param excludedBase64CredentialIDs Array of base64-encoded authenticator IDs registered by the * user so the user can't register the same credential multiple times * @param suggestedTransports Suggested types of authenticators for attestation */ -export default function generateAttestationOptions( - serviceName: string, - rpID: string, - challenge: string, - userID: string, - username: string, - timeout = 60000, - attestationType: 'direct' | 'indirect' = 'direct', - excludedBase64CredentialIDs: string[] = [], - suggestedTransports: AuthenticatorTransport[] = ['usb', 'ble', 'nfc', 'internal'], -): PublicKeyCredentialCreationOptionsJSON { +export default function generateAttestationOptions(opts: Options): PublicKeyCredentialCreationOptionsJSON { + const { + serviceName, + rpID, + challenge, + userID, + userName, + userDisplayName = userName, + timeout = 60000, + attestationType = 'none', + excludedBase64CredentialIDs = [], + suggestedTransports = ['usb', 'ble', 'nfc', 'internal'], + } = opts; + return { - publicKey: { - // Cryptographically random bytes to prevent replay attacks - challenge, - // The organization registering and authenticating the user - rp: { - name: serviceName, - id: rpID, - }, - user: { - id: userID, - name: username, - displayName: username, - }, - pubKeyCredParams: [ - { - alg: -7, - type: 'public-key', - }, - ], - timeout, - attestation: attestationType, - excludeCredentials: excludedBase64CredentialIDs.map((id) => ({ - id, - type: 'public-key', - transports: suggestedTransports, - })), + challenge, + rp: { + name: serviceName, + id: rpID, + }, + user: { + id: userID, + name: userName, + displayName: userDisplayName, }, + pubKeyCredParams: [ + { + alg: -7, + type: 'public-key', + }, + ], + timeout, + attestation: attestationType, + excludeCredentials: excludedBase64CredentialIDs.map((id) => ({ + id, + type: 'public-key', + transports: suggestedTransports, + })), }; } |