proxmox-backup/www/window/AddWebauthn.js
Dominik Csapak b452e2df74 Revert "ui: window/Settings / WebAuthn: add browser setting for userVerificationo"
even if the options *could* be set in the frontend, the backend
actually has to do validation of those settings, thus we should not
make that a browser setting

additionally, having the value 'preferred' does not actually make sense,
since it does not add any security (the backend skips the
userverification check then)

This reverts commit aca4c2b5a9.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-03-03 14:05:05 +01:00

208 lines
4.8 KiB
JavaScript

Ext.define('PBS.window.AddWebauthn', {
extend: 'Ext.window.Window',
alias: 'widget.pbsAddWebauthn',
mixins: ['Proxmox.Mixin.CBind'],
onlineHelp: 'user_mgmt',
modal: true,
resizable: false,
title: gettext('Add a Webauthn login token'),
width: 512,
user: undefined,
fixedUser: false,
initComponent: function() {
let me = this;
me.callParent();
Ext.GlobalEvents.fireEvent('proxmoxShowHelp', me.onlineHelp);
},
viewModel: {
data: {
valid: false,
userid: null,
},
},
controller: {
xclass: 'Ext.app.ViewController',
control: {
'field': {
validitychange: function(field, valid) {
let me = this;
let viewmodel = me.getViewModel();
let form = me.lookup('webauthn_form');
viewmodel.set('valid', form.isValid());
},
},
'#': {
show: function() {
let me = this;
let view = me.getView();
if (Proxmox.UserName === 'root@pam') {
view.lookup('password').setVisible(false);
view.lookup('password').setDisabled(true);
}
},
},
},
registerWebauthn: async function() {
let me = this;
let values = me.lookup('webauthn_form').getValues();
values.type = "webauthn";
let userid = values.user;
delete values.user;
me.getView().mask(gettext('Please wait...'), 'x-mask-loading');
try {
let register_response = await PBS.Async.api2({
url: `/api2/extjs/access/tfa/${userid}`,
method: 'POST',
params: values,
});
let data = register_response.result.data;
if (!data.challenge) {
throw "server did not respond with a challenge";
}
let challenge_obj = JSON.parse(data.challenge);
// Fix this up before passing it to the browser, but keep a copy of the original
// string to pass in the response:
let challenge_str = challenge_obj.publicKey.challenge;
challenge_obj.publicKey.challenge = PBS.Utils.base64url_to_bytes(challenge_str);
challenge_obj.publicKey.user.id =
PBS.Utils.base64url_to_bytes(challenge_obj.publicKey.user.id);
let msg = Ext.Msg.show({
title: `Webauthn: ${gettext('Setup')}`,
message: gettext('Please press the button on your Webauthn Device'),
buttons: [],
});
let token_response = await navigator.credentials.create(challenge_obj);
// We cannot pass ArrayBuffers to the API, so extract & convert the data.
let response = {
id: token_response.id,
type: token_response.type,
rawId: PBS.Utils.bytes_to_base64url(token_response.rawId),
response: {
attestationObject: PBS.Utils.bytes_to_base64url(
token_response.response.attestationObject,
),
clientDataJSON: PBS.Utils.bytes_to_base64url(
token_response.response.clientDataJSON,
),
},
};
msg.close();
let params = {
type: "webauthn",
challenge: challenge_str,
value: JSON.stringify(response),
};
if (values.password) {
params.password = values.password;
}
await PBS.Async.api2({
url: `/api2/extjs/access/tfa/${userid}`,
method: 'POST',
params,
});
} catch (error) {
console.error(error); // for debugging if it's not displayable...
Ext.Msg.alert(gettext('Error'), error);
}
me.getView().close();
},
},
items: [
{
xtype: 'form',
reference: 'webauthn_form',
layout: 'anchor',
border: false,
bodyPadding: 10,
fieldDefaults: {
anchor: '100%',
},
items: [
{
xtype: 'pmxDisplayEditField',
name: 'user',
cbind: {
editable: (get) => !get('fixedUser'),
value: () => Proxmox.UserName,
},
fieldLabel: gettext('User'),
editConfig: {
xtype: 'pbsUserSelector',
allowBlank: false,
},
renderer: Ext.String.htmlEncode,
listeners: {
change: function(field, newValue, oldValue) {
let vm = this.up('window').getViewModel();
vm.set('userid', newValue);
},
},
},
{
xtype: 'textfield',
fieldLabel: gettext('Description'),
allowBlank: false,
name: 'description',
maxLength: 256,
emptyText: gettext('For example: TFA device ID, required to identify multiple factors.'),
},
{
xtype: 'textfield',
name: 'password',
reference: 'password',
fieldLabel: gettext('Verify Password'),
inputType: 'password',
minLength: 5,
allowBlank: false,
validateBlank: true,
cbind: {
hidden: () => Proxmox.UserName === 'root@pam',
disabled: () => Proxmox.UserName === 'root@pam',
emptyText: () =>
Ext.String.format(gettext("Confirm your ({0}) password"), Proxmox.UserName),
},
},
],
},
],
buttons: [
{
xtype: 'proxmoxHelpButton',
},
'->',
{
xtype: 'button',
text: gettext('Register Webauthn Device'),
handler: 'registerWebauthn',
bind: {
disabled: '{!valid}',
},
},
],
});