diff options
Diffstat (limited to 'app/assets/javascripts/authentication/webauthn/authenticate.js')
-rw-r--r-- | app/assets/javascripts/authentication/webauthn/authenticate.js | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/app/assets/javascripts/authentication/webauthn/authenticate.js b/app/assets/javascripts/authentication/webauthn/authenticate.js new file mode 100644 index 00000000000..42c4c2b63bd --- /dev/null +++ b/app/assets/javascripts/authentication/webauthn/authenticate.js @@ -0,0 +1,69 @@ +import WebAuthnError from './error'; +import WebAuthnFlow from './flow'; +import { supported, convertGetParams, convertGetResponse } from './util'; + +// Authenticate WebAuthn devices for users to authenticate with. +// +// State Flow #1: setup -> in_progress -> authenticated -> POST to server +// State Flow #2: setup -> in_progress -> error -> setup +export default class WebAuthnAuthenticate { + constructor(container, form, webauthnParams, fallbackButton, fallbackUI) { + this.container = container; + this.webauthnParams = convertGetParams(JSON.parse(webauthnParams.options)); + this.renderInProgress = this.renderInProgress.bind(this); + + this.form = form; + this.fallbackButton = fallbackButton; + this.fallbackUI = fallbackUI; + if (this.fallbackButton) { + this.fallbackButton.addEventListener('click', this.switchToFallbackUI.bind(this)); + } + + this.flow = new WebAuthnFlow(container, { + inProgress: '#js-authenticate-token-2fa-in-progress', + error: '#js-authenticate-token-2fa-error', + authenticated: '#js-authenticate-token-2fa-authenticated', + }); + + this.container.on('click', '#js-token-2fa-try-again', this.renderInProgress); + } + + start() { + if (!supported()) { + this.switchToFallbackUI(); + } else { + this.renderInProgress(); + } + } + + authenticate() { + navigator.credentials + .get({ publicKey: this.webauthnParams }) + .then(resp => { + const convertedResponse = convertGetResponse(resp); + this.renderAuthenticated(JSON.stringify(convertedResponse)); + }) + .catch(err => { + this.flow.renderError(new WebAuthnError(err, 'authenticate')); + }); + } + + renderInProgress() { + this.flow.renderTemplate('inProgress'); + this.authenticate(); + } + + renderAuthenticated(deviceResponse) { + this.flow.renderTemplate('authenticated'); + const container = this.container[0]; + container.querySelector('#js-device-response').value = deviceResponse; + container.querySelector(this.form).submit(); + this.fallbackButton.classList.add('hidden'); + } + + switchToFallbackUI() { + this.fallbackButton.classList.add('hidden'); + this.container[0].classList.add('hidden'); + this.fallbackUI.classList.remove('hidden'); + } +} |