2020-11-24 15:15:51 +05:30
|
|
|
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 })
|
2021-03-08 18:12:59 +05:30
|
|
|
.then((resp) => {
|
2020-11-24 15:15:51 +05:30
|
|
|
const convertedResponse = convertGetResponse(resp);
|
|
|
|
this.renderAuthenticated(JSON.stringify(convertedResponse));
|
|
|
|
})
|
2021-03-08 18:12:59 +05:30
|
|
|
.catch((err) => {
|
2020-11-24 15:15:51 +05:30
|
|
|
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');
|
|
|
|
}
|
|
|
|
}
|