Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ describe('enrollWebauthnDevice', () => {
user,
challenge,
excludeCredentials,
authenticatorAttachment: 'cross-platform',
hints: ['security-key'],
});

expect(navigator.credentials.create).to.have.been.calledWith({
Expand Down Expand Up @@ -130,16 +128,15 @@ describe('enrollWebauthnDevice', () => {
context('platform authenticator', () => {
it('enrolls a device with correct authenticatorAttachment', async () => {
await enrollWebauthnDevice({
platformAuthenticator: true,
user,
challenge,
excludeCredentials,
authenticatorAttachment: 'platform',
hints: ['client-device'],
});

expect(navigator.credentials.create).to.have.been.calledWithMatch({
publicKey: {
hints: ['client-device'],
hints: undefined,
authenticatorSelection: {
authenticatorAttachment: 'platform',
},
Expand All @@ -162,7 +159,6 @@ describe('enrollWebauthnDevice', () => {
user,
challenge,
excludeCredentials,
authenticatorAttachment: 'cross-platform',
});

expect(result.transports).to.equal(undefined);
Expand All @@ -182,7 +178,6 @@ describe('enrollWebauthnDevice', () => {
user,
challenge,
excludeCredentials,
authenticatorAttachment: 'cross-platform',
});

expect(result.authenticatorDataFlagsValue).to.equal(undefined);
Expand Down
20 changes: 11 additions & 9 deletions app/javascript/packages/webauthn/enroll-webauthn-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ interface AuthenticatorAttestationResponseBrowserSupport
type PublicKeyCredentialHintType = 'client-device' | 'security-key' | 'hybrid';

interface EnrollOptions {
platformAuthenticator?: boolean;

user: PublicKeyCredentialUserEntity;

challenge: BufferSource;

excludeCredentials: PublicKeyCredentialDescriptor[];

authenticatorAttachment?: AuthenticatorAttachment;

hints?: Array<PublicKeyCredentialHintType>;
}

interface EnrollResult {
Expand Down Expand Up @@ -80,11 +78,10 @@ const SUPPORTED_ALGORITHMS: COSEAlgorithm[] = [
];

async function enrollWebauthnDevice({
platformAuthenticator = false,
user,
challenge,
excludeCredentials,
authenticatorAttachment,
hints,
}: EnrollOptions): Promise<EnrollResult> {
const credential = (await navigator.credentials.create({
publicKey: {
Expand All @@ -94,11 +91,16 @@ async function enrollWebauthnDevice({
pubKeyCredParams: SUPPORTED_ALGORITHMS.map((alg) => ({ alg, type: 'public-key' })),
timeout: 800000,
attestation: 'none',
hints,
hints: platformAuthenticator ? undefined : ['security-key'],
authenticatorSelection: {
// Prevents user from needing to use PIN with Security Key
// A user is assumed to be AAL2 recently authenticated before being permitted to add an
// authentication method to their account. Additionally, unless explicitly discouraged,
// Windows devices will prompt the user to add a PIN to their security key. When used as a
// single-factor authenticator in combination with a memorized secret (password), proving
// possession is sufficient, and a PIN ("something you know") is unnecessary friction that
// contributes to abandonment or loss of access.
userVerification: 'discouraged',
authenticatorAttachment,
authenticatorAttachment: platformAuthenticator ? 'platform' : 'cross-platform',
},
excludeCredentials,
} as PublicKeyCredentialCreationOptionsWithHints,
Expand Down
3 changes: 1 addition & 2 deletions app/javascript/packs/webauthn-setup.ts
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of the changes can be summed up as: Minimize what happens in the pack because it's untested, and move the logic into the JavaScript package (tested). Ultimately the only change is that we pass undefined instead of ['client-device', 'hybrid'] for hints when platformAuthenticator is true.

Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ function webauthn() {
(document.getElementById('platform_authenticator') as HTMLInputElement).value === 'true';

enrollWebauthnDevice({
platformAuthenticator,
user: {
id: longToByteArray(Number((document.getElementById('user_id') as HTMLInputElement).value)),
name: (document.getElementById('user_email') as HTMLInputElement).value,
Expand All @@ -55,8 +56,6 @@ function webauthn() {
.split(',')
.filter(Boolean),
),
authenticatorAttachment: platformAuthenticator ? 'platform' : 'cross-platform',
hints: platformAuthenticator ? ['client-device', 'hybrid'] : ['security-key'],
})
.then((result) => {
(document.getElementById('webauthn_id') as HTMLInputElement).value = result.webauthnId;
Expand Down