diff --git a/app/javascript/packages/webauthn/is-webauthn-platform-supported.spec.ts b/app/javascript/packages/webauthn/is-webauthn-platform-supported.spec.ts deleted file mode 100644 index 5e1957f6c5c..00000000000 --- a/app/javascript/packages/webauthn/is-webauthn-platform-supported.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { useDefineProperty } from '@18f/identity-test-helpers'; -import isWebauthnPlatformSupported from './is-webauthn-platform-supported'; - -describe('isWebauthnPlatformSupported', () => { - const defineProperty = useDefineProperty(); - - context('browser does not support webauthn', () => { - beforeEach(() => { - defineProperty(window, 'PublicKeyCredential', { - configurable: true, - value: undefined, - }); - }); - - it('resolves to false', async () => { - await expect(isWebauthnPlatformSupported()).to.eventually.equal(false); - }); - }); - - context('browser supports webauthn', () => { - context('device does not have platform authenticator available', () => { - beforeEach(() => { - defineProperty(window, 'PublicKeyCredential', { - configurable: true, - value: { isUserVerifyingPlatformAuthenticatorAvailable: () => Promise.resolve(false) }, - }); - }); - - it('resolves to false', async () => { - await expect(isWebauthnPlatformSupported()).to.eventually.equal(false); - }); - }); - - context('device has platform authenticator available', () => { - beforeEach(() => { - defineProperty(window, 'PublicKeyCredential', { - configurable: true, - value: { isUserVerifyingPlatformAuthenticatorAvailable: () => Promise.resolve(true) }, - }); - }); - - it('resolves to true', async () => { - await expect(isWebauthnPlatformSupported()).to.eventually.equal(true); - }); - }); - }); -}); diff --git a/app/javascript/packages/webauthn/is-webauthn-platform-supported.ts b/app/javascript/packages/webauthn/is-webauthn-platform-supported.ts deleted file mode 100644 index fa52218e416..00000000000 --- a/app/javascript/packages/webauthn/is-webauthn-platform-supported.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type IsWebauthnPlatformSupported = () => Promise; - -const isWebauthnPlatformSupported: IsWebauthnPlatformSupported = async () => - !!(await window.PublicKeyCredential?.isUserVerifyingPlatformAuthenticatorAvailable()); - -export default isWebauthnPlatformSupported; diff --git a/app/javascript/packages/webauthn/webauth-input-element.spec.ts b/app/javascript/packages/webauthn/webauthn-input-element.spec.ts similarity index 62% rename from app/javascript/packages/webauthn/webauth-input-element.spec.ts rename to app/javascript/packages/webauthn/webauthn-input-element.spec.ts index 89816331c9c..ef8df395b59 100644 --- a/app/javascript/packages/webauthn/webauth-input-element.spec.ts +++ b/app/javascript/packages/webauthn/webauthn-input-element.spec.ts @@ -1,9 +1,7 @@ import sinon from 'sinon'; import quibble from 'quibble'; -import { waitFor } from '@testing-library/dom'; import type { IsWebauthnSupported } from './is-webauthn-supported'; import type { IsWebauthnPasskeySupported } from './is-webauthn-passkey-supported'; -import type { IsWebauthnPlatformSupported } from './is-webauthn-platform-supported'; describe('WebauthnInputElement', () => { const isWebauthnSupported = sinon.stub< @@ -14,15 +12,10 @@ describe('WebauthnInputElement', () => { Parameters, ReturnType >(); - const isWebauthnPlatformSupported = sinon.stub< - Parameters, - ReturnType - >(); before(async () => { quibble('./is-webauthn-supported', isWebauthnSupported); quibble('./is-webauthn-passkey-supported', isWebauthnPasskeySupported); - quibble('./is-webauthn-platform-supported', isWebauthnPlatformSupported); await import('./webauthn-input-element'); }); @@ -30,9 +23,7 @@ describe('WebauthnInputElement', () => { isWebauthnSupported.reset(); isWebauthnSupported.returns(false); isWebauthnPasskeySupported.reset(); - isWebauthnPasskeySupported.resolves(false); - isWebauthnPlatformSupported.reset(); - isWebauthnPlatformSupported.resolves(false); + isWebauthnPasskeySupported.returns(false); }); after(() => { @@ -62,70 +53,50 @@ describe('WebauthnInputElement', () => { document.body.innerHTML = ``; }); - it('becomes visible', async () => { + it('becomes visible', () => { const element = document.querySelector('lg-webauthn-input')!; - await waitFor(() => expect(element.hidden).to.be.false()); + expect(element.hidden).to.be.false(); }); }); context('input for platform authenticator', () => { - context('device does not have available platform authenticator', () => { - beforeEach(() => { - isWebauthnPlatformSupported.resolves(false); - document.body.innerHTML = ``; - }); - - it('stays hidden', async () => { - const element = document.querySelector('lg-webauthn-input')!; - - await waitFor(() => expect(element.isInitialized).to.be.true()); - - expect(element.hidden).to.be.true(); - }); - }); - - context('device has available platform authenticator', () => { + context('no passkey only restriction', () => { beforeEach(() => { - isWebauthnPlatformSupported.resolves(true); document.body.innerHTML = ``; }); - it('becomes visible', async () => { + it('becomes visible', () => { const element = document.querySelector('lg-webauthn-input')!; - await waitFor(() => expect(element.hidden).to.be.false()); + expect(element.hidden).to.be.false(); }); }); context('passkey supported only', () => { context('device does not support passkey', () => { beforeEach(() => { - isWebauthnPlatformSupported.resolves(true); isWebauthnPasskeySupported.returns(false); document.body.innerHTML = ``; }); - it('stays hidden', async () => { + it('stays hidden', () => { const element = document.querySelector('lg-webauthn-input')!; - await waitFor(() => expect(element.isInitialized).to.be.true()); - expect(element.hidden).to.be.true(); }); }); context('device supports passkey', () => { beforeEach(() => { - isWebauthnPlatformSupported.resolves(true); isWebauthnPasskeySupported.returns(true); document.body.innerHTML = ``; }); - it('becomes visible', async () => { + it('becomes visible', () => { const element = document.querySelector('lg-webauthn-input')!; - await waitFor(() => expect(element.hidden).to.be.false()); + expect(element.hidden).to.be.false(); }); }); }); diff --git a/app/javascript/packages/webauthn/webauthn-input-element.ts b/app/javascript/packages/webauthn/webauthn-input-element.ts index 5d7e5327d09..640206bafd3 100644 --- a/app/javascript/packages/webauthn/webauthn-input-element.ts +++ b/app/javascript/packages/webauthn/webauthn-input-element.ts @@ -1,13 +1,9 @@ import isWebauthnPasskeySupported from './is-webauthn-passkey-supported'; -import isWebauthnPlatformSupported from './is-webauthn-platform-supported'; import isWebauthnSupported from './is-webauthn-supported'; export class WebauthnInputElement extends HTMLElement { - isInitialized = false; - - async connectedCallback() { - await this.toggleVisibleIfSupported(); - this.isInitialized = true; + connectedCallback() { + this.toggleVisibleIfSupported(); } get isPlatform(): boolean { @@ -18,24 +14,16 @@ export class WebauthnInputElement extends HTMLElement { return this.hasAttribute('passkey-supported-only'); } - async isSupported(): Promise { + isSupported(): boolean { if (!isWebauthnSupported()) { return false; } - if (!this.isPlatform) { - return true; - } - - if (!(await isWebauthnPlatformSupported())) { - return false; - } - - return !this.isOnlyPasskeySupported || isWebauthnPasskeySupported(); + return !this.isPlatform || !this.isOnlyPasskeySupported || isWebauthnPasskeySupported(); } - async toggleVisibleIfSupported() { - if (await this.isSupported()) { + toggleVisibleIfSupported() { + if (this.isSupported()) { this.removeAttribute('hidden'); } } diff --git a/app/javascript/packs/webauthn-authenticate.ts b/app/javascript/packs/webauthn-authenticate.ts index 5675e9bc3af..c16e114157a 100644 --- a/app/javascript/packs/webauthn-authenticate.ts +++ b/app/javascript/packs/webauthn-authenticate.ts @@ -8,10 +8,6 @@ function webauthn() { const webauthAlertContainer = document.querySelector('.usa-alert--error')!; const webauthnPlatformRequested = webauthnInProgressContainer.dataset.platformAuthenticatorRequested === 'true'; - const multipleFactorsEnabled = - webauthnInProgressContainer.dataset.multipleFactorsEnabled === 'true'; - const isPlatformAvailable = - (document.getElementById('webauthn_device') as HTMLInputElement).value === 'true'; const spinner = document.getElementById('spinner')!; spinner.classList.remove('display-none'); @@ -20,10 +16,7 @@ function webauthn() { (document.getElementById('credentials') as HTMLInputElement).value, ); - if ( - !isWebauthnSupported() || - (webauthnPlatformRequested && !isPlatformAvailable && !multipleFactorsEnabled) - ) { + if (!isWebauthnSupported()) { const href = webauthnInProgressContainer.getAttribute('data-webauthn-not-enabled-url')!; window.location.href = href; } else { @@ -60,13 +53,4 @@ function webauthnButton() { button.addEventListener('click', webauthn); } -function isPlatformAuthenticatorAvailable() { - return window.PublicKeyCredential?.isUserVerifyingPlatformAuthenticatorAvailable().then( - (result) => { - (document.getElementById('webauthn_device') as HTMLInputElement).value = String(result); - }, - ); -} - document.addEventListener('DOMContentLoaded', webauthnButton); -document.addEventListener('DOMContentLoaded', isPlatformAuthenticatorAvailable); diff --git a/app/views/two_factor_authentication/webauthn_verification/show.html.erb b/app/views/two_factor_authentication/webauthn_verification/show.html.erb index 3833d889ba2..8d1e4401730 100644 --- a/app/views/two_factor_authentication/webauthn_verification/show.html.erb +++ b/app/views/two_factor_authentication/webauthn_verification/show.html.erb @@ -22,16 +22,13 @@ <%= hidden_field_tag :signature, '', id: 'signature' %> <%= hidden_field_tag :client_data_json, '', id: 'client_data_json' %> <%= hidden_field_tag :webauthn_error, '', id: 'webauthn_error' %> - <%= hidden_field_tag :platform, '', id: 'platform' %> - <%= hidden_field_tag :webauthn_device, '', id: 'webauthn_device' %> + <%= hidden_field_tag :platform, @presenter.platform_authenticator?, id: 'platform' %> <%= content_tag( :div, id: 'webauthn-auth-in-progress', data: { webauthn_not_enabled_url: @presenter.webauthn_not_enabled_link, - platform_authenticator_requested: @presenter.platform_authenticator?, - multiple_factors_enabled: @presenter.multiple_factors_enabled?, }, ) do %>