import { browserSupportsWebAuthn, platformAuthenticatorIsAvailable, startRegistration } from '@simplewebauthn/browser';
import $ from 'jquery';
import QRCode from 'qrcode';
import { ActionDialog } from 'vj/components/dialog';
import Notification from 'vj/components/notification';
import { NamedPage } from 'vj/misc/Page';
import {
delay, i18n, request, secureRandomString, tpl,
} from 'vj/utils';
async function changeMail() {
const changeMailDialog = new ActionDialog({
$body: tpl(
,
),
onDispatch(action) {
if (action === 'ok') {
const $password = $('#change-mail-dialog [name="password"]');
const $mail = $('#change-mail-dialog [name="mail"]');
if (!$password.val() || !$mail.val()) {
if (!$password.val()) $password.focus();
else $mail.focus();
return false;
}
}
return true;
},
}).open();
const action = await changeMailDialog;
if (action !== 'ok') return;
try {
await request.post('', {
operation: 'change_mail',
password: $('#change-mail-dialog [name="password"]').val(),
mail: $('#change-mail-dialog [name="mail"]').val(),
});
} catch (err) {
Notification.error(err.message);
console.error(err);
return;
}
Notification.success(i18n('Successfully changed.'));
await delay(2000);
window.location.reload();
}
async function enableTfa() {
const enableTFA = new ActionDialog({
$body: tpl`
${i18n('Please use your two factor authentication app to scan the qrcode below:')}
${i18n('Click to show secret')}
`,
onDispatch(action) {
if (action === 'ok' && $('[name="tfa_code"]').val() === null) {
$('[name="tfa_code"]').focus();
return false;
}
return true;
},
}).open();
const secret = secureRandomString(13, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567');
$('#secret').on('click', () => $('#secret').html(secret));
const uri = `otpauth://totp/Hydro:${UserContext.uname}?secret=${secret}&issuer=Hydro`;
const canvas = document.getElementById('qrcode');
await QRCode.toCanvas(canvas, uri);
const tfaAction = await enableTFA;
if (tfaAction !== 'ok') return;
try {
await request.post('', {
operation: 'enable_tfa',
code: $('[name="tfa_code"]').val(),
secret,
});
} catch (err) {
Notification.error(err.message);
console.error(err);
return;
}
Notification.success(i18n('Successfully enabled.'));
await delay(2000);
window.location.reload();
}
async function enableAuthn(type: string) {
const authnInfo = await request.post('', { operation: 'register', type });
if (!authnInfo.authOptions) {
Notification.error(i18n('Failed to fetch registration data.'));
return;
}
Notification.info(i18n('Please follow the instructions on your device to complete the verification.'));
let credential;
try {
console.log(authnInfo);
credential = await startRegistration({ optionsJSON: authnInfo.authOptions });
} catch (err) {
Notification.error(i18n('Failed to get credential: {0}', err));
return;
}
const op = await new ActionDialog({
$body: tpl`
`,
onDispatch(action) {
if (action === 'ok' && $('[name="webauthn_name"]').val() === null) {
$('[name="webauthn_name"]').focus();
return false;
}
return true;
},
}).open();
if (op !== 'ok') return;
try {
await request.post('', {
operation: 'enable_authn',
name: $('[name="webauthn_name"]').val(),
result: credential,
});
} catch (err) {
Notification.error(err.message);
console.error(err);
return;
}
Notification.success(i18n('Successfully enabled.'));
await delay(2000);
window.location.reload();
}
export default new NamedPage('home_security', () => {
const MenuLink = ({ children, action, icon }: { children: React.ReactNode, action?: string, icon?: string }) => (
{icon && }
{children}
);
$(document).on('click', '[name="auth_enable"]', async () => {
const platformAvailable = browserSupportsWebAuthn() && await platformAuthenticatorIsAvailable();
const $body = tpl(
{i18n('Choose Authenticator Type')}
{i18n('Two Factor Authentication')}
{(!window.isSecureContext || !browserSupportsWebAuthn()) ? (
{window.isSecureContext
? i18n("Your browser doesn't support WebAuthn.")
: i18n('Webauthn is not available in insecure context.')}
) : (<>
{platformAvailable ? (
{i18n('Your Device')}
) : (
{i18n("Your browser doesn't support platform authenticator.")}
)}
{i18n('Multi Platform Authenticator')}
>)}
,
);
const action = await new ActionDialog({ $body, $action: [] }).open();
if (!action || action === 'cancel') return;
if (action === 'tfa') enableTfa();
else enableAuthn(action);
});
$(document).on('click', '[data-operation="change_mail"]', async (ev) => {
ev.preventDefault();
await changeMail();
});
});