78 lines
2.6 KiB
JavaScript
78 lines
2.6 KiB
JavaScript
import { __ } from '~/locale';
|
|
import WebAuthnError from './error';
|
|
import WebAuthnFlow from './flow';
|
|
import { supported, isHTTPS, convertCreateParams, convertCreateResponse } from './util';
|
|
|
|
// Register WebAuthn devices for users to authenticate with.
|
|
//
|
|
// State Flow #1: setup -> in_progress -> registered -> POST to server
|
|
// State Flow #2: setup -> in_progress -> error -> setup
|
|
export default class WebAuthnRegister {
|
|
constructor(container, webauthnParams) {
|
|
this.container = container;
|
|
this.renderInProgress = this.renderInProgress.bind(this);
|
|
this.webauthnOptions = convertCreateParams(webauthnParams.options);
|
|
|
|
this.flow = new WebAuthnFlow(container, {
|
|
message: '#js-register-2fa-message',
|
|
setup: '#js-register-token-2fa-setup',
|
|
error: '#js-register-token-2fa-error',
|
|
registered: '#js-register-token-2fa-registered',
|
|
});
|
|
|
|
this.container.on('click', '#js-token-2fa-try-again', this.renderInProgress);
|
|
}
|
|
|
|
start() {
|
|
if (!supported()) {
|
|
// we show a special error message when the user visits the site
|
|
// using a non-ssl connection as this makes WebAuthn unavailable in
|
|
// any case, regardless of the used browser
|
|
this.renderNotSupported(!isHTTPS());
|
|
} else {
|
|
this.renderSetup();
|
|
}
|
|
}
|
|
|
|
register() {
|
|
navigator.credentials
|
|
.create({
|
|
publicKey: this.webauthnOptions,
|
|
})
|
|
.then((cred) => this.renderRegistered(JSON.stringify(convertCreateResponse(cred))))
|
|
.catch((err) => this.flow.renderError(new WebAuthnError(err, 'register')));
|
|
}
|
|
|
|
renderSetup() {
|
|
this.flow.renderTemplate('setup');
|
|
this.container.find('#js-setup-token-2fa-device').on('click', this.renderInProgress);
|
|
}
|
|
|
|
renderInProgress() {
|
|
this.flow.renderTemplate('message', {
|
|
message: __(
|
|
'Trying to communicate with your device. Plug it in (if needed) and press the button on the device now.',
|
|
),
|
|
});
|
|
return this.register();
|
|
}
|
|
|
|
renderRegistered(deviceResponse) {
|
|
this.flow.renderTemplate('registered');
|
|
// Prefer to do this instead of interpolating using Underscore templates
|
|
// because of JSON escaping issues.
|
|
this.container.find('#js-device-response').val(deviceResponse);
|
|
}
|
|
|
|
renderNotSupported(noHttps) {
|
|
const message = noHttps
|
|
? __(
|
|
'WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details.',
|
|
)
|
|
: __(
|
|
"Your browser doesn't support WebAuthn. Please use a supported browser, e.g. Chrome (67+) or Firefox (60+).",
|
|
);
|
|
|
|
this.flow.renderTemplate('message', { message });
|
|
}
|
|
}
|