diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
commit | 85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch) | |
tree | 9160f299afd8c80c038f08e1545be119f5e3f1e1 /spec/frontend/authentication/webauthn/authenticate_spec.js | |
parent | 15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff) | |
download | gitlab-ce-85dc423f7090da0a52c73eb66faf22ddb20efff9.tar.gz |
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'spec/frontend/authentication/webauthn/authenticate_spec.js')
-rw-r--r-- | spec/frontend/authentication/webauthn/authenticate_spec.js | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/spec/frontend/authentication/webauthn/authenticate_spec.js b/spec/frontend/authentication/webauthn/authenticate_spec.js new file mode 100644 index 00000000000..0a82adfd0ee --- /dev/null +++ b/spec/frontend/authentication/webauthn/authenticate_spec.js @@ -0,0 +1,132 @@ +import $ from 'jquery'; +import waitForPromises from 'helpers/wait_for_promises'; +import WebAuthnAuthenticate from '~/authentication/webauthn/authenticate'; +import MockWebAuthnDevice from './mock_webauthn_device'; +import { useMockNavigatorCredentials } from './util'; + +const mockResponse = { + type: 'public-key', + id: '', + rawId: '', + response: { clientDataJSON: '', authenticatorData: '', signature: '', userHandle: '' }, + getClientExtensionResults: () => {}, +}; + +describe('WebAuthnAuthenticate', () => { + preloadFixtures('webauthn/authenticate.html'); + useMockNavigatorCredentials(); + + let fallbackElement; + let webAuthnDevice; + let container; + let component; + let submitSpy; + + const findDeviceResponseInput = () => container[0].querySelector('#js-device-response'); + const findDeviceResponseInputValue = () => findDeviceResponseInput().value; + const findMessage = () => container[0].querySelector('p'); + const findRetryButton = () => container[0].querySelector('#js-token-2fa-try-again'); + const expectAuthenticated = () => { + expect(container.text()).toMatchInterpolatedText( + 'We heard back from your device. You have been authenticated.', + ); + expect(findDeviceResponseInputValue()).toBe(JSON.stringify(mockResponse)); + expect(submitSpy).toHaveBeenCalled(); + }; + + beforeEach(() => { + loadFixtures('webauthn/authenticate.html'); + fallbackElement = document.createElement('div'); + fallbackElement.classList.add('js-2fa-form'); + webAuthnDevice = new MockWebAuthnDevice(); + container = $('#js-authenticate-token-2fa'); + component = new WebAuthnAuthenticate( + container, + '#js-login-token-2fa-form', + { + options: + // we need some valid base64 for base64ToBuffer + // so we use "YQ==" = base64("a") + JSON.stringify({ + challenge: 'YQ==', + timeout: 120000, + allowCredentials: [ + { type: 'public-key', id: 'YQ==' }, + { type: 'public-key', id: 'YQ==' }, + ], + userVerification: 'discouraged', + }), + }, + document.querySelector('#js-login-2fa-device'), + fallbackElement, + ); + submitSpy = jest.spyOn(HTMLFormElement.prototype, 'submit'); + }); + + describe('with webauthn unavailable', () => { + let oldGetCredentials; + + beforeEach(() => { + oldGetCredentials = window.navigator.credentials.get; + window.navigator.credentials.get = null; + }); + + afterEach(() => { + window.navigator.credentials.get = oldGetCredentials; + }); + + it('falls back to normal 2fa', () => { + component.start(); + + expect(container.html()).toBe(''); + expect(container[0]).toHaveClass('hidden'); + expect(fallbackElement).not.toHaveClass('hidden'); + }); + }); + + describe('with webauthn available', () => { + beforeEach(() => { + component.start(); + }); + + it('shows in progress', () => { + const inProgressMessage = container.find('p'); + + expect(inProgressMessage.text()).toMatchInterpolatedText( + "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.", + ); + }); + + it('allows authenticating via a WebAuthn device', () => { + webAuthnDevice.respondToAuthenticateRequest(mockResponse); + + return waitForPromises().then(() => { + expectAuthenticated(); + }); + }); + + describe('errors', () => { + beforeEach(() => { + webAuthnDevice.rejectAuthenticateRequest(new DOMException()); + + return waitForPromises(); + }); + + it('displays an error message', () => { + expect(submitSpy).not.toHaveBeenCalled(); + expect(findMessage().textContent).toMatchInterpolatedText( + 'There was a problem communicating with your device. (Error)', + ); + }); + + it('allows retrying authentication after an error', () => { + findRetryButton().click(); + webAuthnDevice.respondToAuthenticateRequest(mockResponse); + + return waitForPromises().then(() => { + expectAuthenticated(); + }); + }); + }); + }); +}); |