summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMatthew Miller <matthew@millerti.me>2020-05-26 22:38:27 -0700
committerGitHub <noreply@github.com>2020-05-26 22:38:27 -0700
commit5a1acc9128be6dbce3d8d718defe93fb14a88e36 (patch)
tree7eea60332475794ba7fa02ea24374fc98b59baf4
parent2374c09f8444b8a80ce8429f2654ce1b3b92c346 (diff)
parent1c956b9c39f5c175d3841b1fafaeeb495d1eea6b (diff)
Merge pull request #16 from MasterKale/feature/support-more-credential-options
feature/support-more-credential-options
-rw-r--r--packages/browser/src/methods/startAssertion.test.ts22
-rw-r--r--packages/browser/src/methods/startAssertion.ts6
-rw-r--r--packages/browser/src/methods/startAttestation.test.ts48
-rw-r--r--packages/browser/src/methods/startAttestation.ts10
-rw-r--r--packages/server/src/assertion/generateAssertionOptions.test.ts51
-rw-r--r--packages/server/src/assertion/generateAssertionOptions.ts35
-rw-r--r--packages/server/src/attestation/generateAttestationOptions.test.ts98
-rw-r--r--packages/server/src/attestation/generateAttestationOptions.ts95
-rw-r--r--packages/typescript-types/src/index.ts66
9 files changed, 215 insertions, 216 deletions
diff --git a/packages/browser/src/methods/startAssertion.test.ts b/packages/browser/src/methods/startAssertion.test.ts
index 7449dbf..db401dc 100644
--- a/packages/browser/src/methods/startAssertion.test.ts
+++ b/packages/browser/src/methods/startAssertion.test.ts
@@ -22,17 +22,15 @@ const mockSignature = toBase64String(toUint8Array('mockSignature'));
const mockUserHandle = toBase64String(toUint8Array('mockUserHandle'));
const goodOpts1: PublicKeyCredentialRequestOptionsJSON = {
- publicKey: {
- challenge: 'fizz',
- allowCredentials: [
- {
- id: 'abcdefgfdnsdfunguisdfgs',
- type: 'public-key',
- transports: ['nfc'],
- },
- ],
- timeout: 1,
- },
+ challenge: 'fizz',
+ allowCredentials: [
+ {
+ id: 'abcdefgfdnsdfunguisdfgs',
+ type: 'public-key',
+ transports: ['nfc'],
+ },
+ ],
+ timeout: 1,
};
beforeEach(() => {
@@ -57,7 +55,7 @@ test('should convert options before passing to navigator.credentials.get(...)',
const argsPublicKey = mockNavigatorGet.mock.calls[0][0].publicKey;
const credId = base64js.fromByteArray(argsPublicKey.allowCredentials[0].id);
- expect(argsPublicKey.challenge).toEqual(toUint8Array(goodOpts1.publicKey.challenge));
+ expect(argsPublicKey.challenge).toEqual(toUint8Array(goodOpts1.challenge));
// Make sure the credential ID is a proper base64 with a length that's a multiple of 4
expect(credId.length % 4).toEqual(0);
diff --git a/packages/browser/src/methods/startAssertion.ts b/packages/browser/src/methods/startAssertion.ts
index 0648e56..093cf30 100644
--- a/packages/browser/src/methods/startAssertion.ts
+++ b/packages/browser/src/methods/startAssertion.ts
@@ -23,9 +23,9 @@ export default async function startAssertion(
// We need to convert some values to Uint8Arrays before passing the credentials to the navigator
const publicKey: PublicKeyCredentialRequestOptions = {
- ...requestOptionsJSON.publicKey,
- challenge: toUint8Array(requestOptionsJSON.publicKey.challenge),
- allowCredentials: requestOptionsJSON.publicKey.allowCredentials.map(
+ ...requestOptionsJSON,
+ challenge: toUint8Array(requestOptionsJSON.challenge),
+ allowCredentials: requestOptionsJSON.allowCredentials.map(
toPublicKeyCredentialDescriptor,
),
};
diff --git a/packages/browser/src/methods/startAttestation.test.ts b/packages/browser/src/methods/startAttestation.test.ts
index a972014..ae79235 100644
--- a/packages/browser/src/methods/startAttestation.test.ts
+++ b/packages/browser/src/methods/startAttestation.test.ts
@@ -19,31 +19,29 @@ const mockAttestationObject = 'mockAtte';
const mockClientDataJSON = 'mockClie';
const goodOpts1: PublicKeyCredentialCreationOptionsJSON = {
- publicKey: {
- challenge: 'fizz',
- attestation: 'direct',
- pubKeyCredParams: [
- {
- alg: -7,
- type: 'public-key',
- },
- ],
- rp: {
- id: '1234',
- name: 'simplewebauthn',
- },
- user: {
- id: '5678',
- displayName: 'username',
- name: 'username',
- },
- timeout: 1,
- excludeCredentials: [{
- id: 'authIdentifier',
+ challenge: 'fizz',
+ attestation: 'direct',
+ pubKeyCredParams: [
+ {
+ alg: -7,
type: 'public-key',
- transports: ['internal'],
- }],
+ },
+ ],
+ rp: {
+ id: '1234',
+ name: 'simplewebauthn',
},
+ user: {
+ id: '5678',
+ displayName: 'username',
+ name: 'username',
+ },
+ timeout: 1,
+ excludeCredentials: [{
+ id: 'authIdentifier',
+ type: 'public-key',
+ transports: ['internal'],
+ }],
};
beforeEach(() => {
@@ -67,8 +65,8 @@ test('should convert options before passing to navigator.credentials.create(...)
const argsPublicKey = mockNavigatorCreate.mock.calls[0][0].publicKey;
- expect(argsPublicKey.challenge).toEqual(toUint8Array(goodOpts1.publicKey.challenge));
- expect(argsPublicKey.user.id).toEqual(toUint8Array(goodOpts1.publicKey.user.id));
+ expect(argsPublicKey.challenge).toEqual(toUint8Array(goodOpts1.challenge));
+ expect(argsPublicKey.user.id).toEqual(toUint8Array(goodOpts1.user.id));
expect(argsPublicKey.excludeCredentials).toEqual([{
id: base64js.toByteArray('authIdentifier=='),
type: 'public-key',
diff --git a/packages/browser/src/methods/startAttestation.ts b/packages/browser/src/methods/startAttestation.ts
index f77c2de..ea05fa8 100644
--- a/packages/browser/src/methods/startAttestation.ts
+++ b/packages/browser/src/methods/startAttestation.ts
@@ -23,13 +23,13 @@ export default async function startAttestation(
// We need to convert some values to Uint8Arrays before passing the credentials to the navigator
const publicKey: PublicKeyCredentialCreationOptions = {
- ...creationOptionsJSON.publicKey,
- challenge: toUint8Array(creationOptionsJSON.publicKey.challenge),
+ ...creationOptionsJSON,
+ challenge: toUint8Array(creationOptionsJSON.challenge),
user: {
- ...creationOptionsJSON.publicKey.user,
- id: toUint8Array(creationOptionsJSON.publicKey.user.id),
+ ...creationOptionsJSON.user,
+ id: toUint8Array(creationOptionsJSON.user.id),
},
- excludeCredentials: creationOptionsJSON.publicKey.excludeCredentials.map(
+ excludeCredentials: creationOptionsJSON.excludeCredentials.map(
toPublicKeyCredentialDescriptor,
),
};
diff --git a/packages/server/src/assertion/generateAssertionOptions.test.ts b/packages/server/src/assertion/generateAssertionOptions.test.ts
index 257657b..aa345af 100644
--- a/packages/server/src/assertion/generateAssertionOptions.test.ts
+++ b/packages/server/src/assertion/generateAssertionOptions.test.ts
@@ -3,40 +3,41 @@ import generateAssertionOptions from './generateAssertionOptions';
test('should generate credential request options suitable for sending via JSON', () => {
const challenge = 'totallyrandomvalue';
- const options = generateAssertionOptions(
+ const options = generateAssertionOptions({
challenge,
- 1,
- [
+ timeout: 1,
+ allowedBase64CredentialIDs: [
Buffer.from('1234', 'ascii').toString('base64'),
Buffer.from('5678', 'ascii').toString('base64'),
],
- );
+ });
expect(options).toEqual({
- publicKey: {
- challenge,
- allowCredentials: [
- {
- id: 'MTIzNA==',
- type: 'public-key',
- transports: ['usb', 'ble', 'nfc', 'internal'],
- },
- {
- id: 'NTY3OA==',
- type: 'public-key',
- transports: ['usb', 'ble', 'nfc', 'internal'],
- },
- ],
- timeout: 1,
- },
+ challenge,
+ allowCredentials: [
+ {
+ id: 'MTIzNA==',
+ type: 'public-key',
+ transports: ['usb', 'ble', 'nfc', 'internal'],
+ },
+ {
+ id: 'NTY3OA==',
+ type: 'public-key',
+ transports: ['usb', 'ble', 'nfc', 'internal'],
+ },
+ ],
+ timeout: 1,
});
});
test('defaults to 60 seconds if no timeout is specified', () => {
- const options = generateAssertionOptions('totallyrandomvalue', undefined, [
- Buffer.from('1234', 'ascii').toString('base64'),
- Buffer.from('5678', 'ascii').toString('base64'),
- ]);
+ const options = generateAssertionOptions({
+ challenge: 'totallyrandomvalue',
+ allowedBase64CredentialIDs: [
+ Buffer.from('1234', 'ascii').toString('base64'),
+ Buffer.from('5678', 'ascii').toString('base64'),
+ ],
+ });
- expect(options.publicKey.timeout).toEqual(60000);
+ expect(options.timeout).toEqual(60000);
});
diff --git a/packages/server/src/assertion/generateAssertionOptions.ts b/packages/server/src/assertion/generateAssertionOptions.ts
index b30344d..b31a34f 100644
--- a/packages/server/src/assertion/generateAssertionOptions.ts
+++ b/packages/server/src/assertion/generateAssertionOptions.ts
@@ -2,6 +2,13 @@ import type {
PublicKeyCredentialRequestOptionsJSON,
} from '@simplewebauthn/typescript-types';
+type Options = {
+ challenge: string,
+ allowedBase64CredentialIDs: string[],
+ suggestedTransports?: AuthenticatorTransport[],
+ timeout?: number,
+};
+
/**
* Prepare a value to pass into navigator.credentials.get(...) for authenticator "login"
*
@@ -12,20 +19,22 @@ import type {
* @param suggestedTransports Suggested types of authenticators for assertion
*/
export default function generateAssertionOptions(
- challenge: string,
- timeout = 60000,
- allowedBase64CredentialIDs: string[],
- suggestedTransports: AuthenticatorTransport[] = ['usb', 'ble', 'nfc', 'internal'],
+ options: Options,
): PublicKeyCredentialRequestOptionsJSON {
+ const {
+ challenge,
+ allowedBase64CredentialIDs,
+ suggestedTransports = ['usb', 'ble', 'nfc', 'internal'],
+ timeout = 60000,
+ } = options;
+
return {
- publicKey: {
- challenge,
- allowCredentials: allowedBase64CredentialIDs.map(id => ({
- id,
- type: 'public-key',
- transports: suggestedTransports,
- })),
- timeout,
- },
+ challenge,
+ allowCredentials: allowedBase64CredentialIDs.map(id => ({
+ id,
+ type: 'public-key',
+ transports: suggestedTransports,
+ })),
+ timeout,
};
}
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..e2a9926 100644
--- a/packages/server/src/attestation/generateAttestationOptions.ts
+++ b/packages/server/src/attestation/generateAttestationOptions.ts
@@ -1,57 +1,76 @@
-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'],
+ options: Options,
): PublicKeyCredentialCreationOptionsJSON {
+ const {
+ serviceName,
+ rpID,
+ challenge,
+ userID,
+ userName,
+ userDisplayName = userName,
+ timeout = 60000,
+ attestationType = 'none',
+ excludedBase64CredentialIDs = [],
+ suggestedTransports = ['usb', 'ble', 'nfc', 'internal'],
+ } = options;
+
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,
+ })),
};
}
diff --git a/packages/typescript-types/src/index.ts b/packages/typescript-types/src/index.ts
index 353623e..920fd7c 100644
--- a/packages/typescript-types/src/index.ts
+++ b/packages/typescript-types/src/index.ts
@@ -1,56 +1,27 @@
/**
* A variant of PublicKeyCredentialCreationOptions suitable for JSON transmission to the browser to
* (eventually) get passed into navigator.credentials.create(...) in the browser.
- *
- * Noteworthy values:
- * @param challenge A random string of characters. Will be converted to a Uint8Array in the browser
- * @param user.id Your unique, internal ID for the user. Will be converted to a Uint8Array in the
- * browser
*/
-export type PublicKeyCredentialCreationOptionsJSON = {
- publicKey: {
- challenge: string;
- // The organization registering and authenticating the user
- rp: {
- name: string;
- id: string;
- };
- user: {
- id: string;
- name: string;
- displayName: string;
- };
- pubKeyCredParams: [
- {
- alg: -7;
- type: 'public-key';
- },
- ];
- timeout?: number;
- attestation: 'direct' | 'indirect';
- excludeCredentials: PublicKeyCredentialDescriptorJSON[];
- };
-};
+export interface PublicKeyCredentialCreationOptionsJSON extends Omit<
+PublicKeyCredentialCreationOptions, 'challenge' | 'user' | 'excludeCredentials'
+> {
+ // Will be converted to a Uint8Array in the browser
+ user: PublicKeyCredentialUserEntityJSON;
+ challenge: string;
+ excludeCredentials: PublicKeyCredentialDescriptorJSON[];
+}
/**
* A variant of PublicKeyCredentialRequestOptions suitable for JSON transmission to the browser to
* (eventually) get passed into navigator.credentials.get(...) in the browser.
- *
- * Noteworthy values:
- * @param challenge A random string of characters. Will be converted to a Uint8Array in the browser
- * @param allowCredentials.id Base64-encoded credentialId. Will be converted to a Uint8Array in the
- * browser
*/
-export type PublicKeyCredentialRequestOptionsJSON = {
- publicKey: {
- challenge: string;
- allowCredentials: PublicKeyCredentialDescriptorJSON[];
- // extensions?: AuthenticationExtensionsClientInputs,
- rpId?: string;
- timeout?: number;
- userVerification?: UserVerificationRequirement;
- };
-};
+export interface PublicKeyCredentialRequestOptionsJSON extends Omit<
+PublicKeyCredentialRequestOptions, 'challenge' |'allowCredentials'
+> {
+ // Will be converted to a Uint8Array in the browser
+ challenge: string;
+ allowCredentials: PublicKeyCredentialDescriptorJSON[];
+}
export interface PublicKeyCredentialDescriptorJSON extends Omit<
PublicKeyCredentialDescriptor, 'id'
@@ -59,6 +30,13 @@ PublicKeyCredentialDescriptor, 'id'
id: string;
}
+export interface PublicKeyCredentialUserEntityJSON extends Omit <
+PublicKeyCredentialUserEntity, 'id'
+> {
+ // Should be a Base64-encoded credential ID. Will be converted to a Uint8Array in the browser
+ id: string;
+}
+
/**
* The value returned from navigator.credentials.create()
*/