core&ui: add passkey login support

This commit is contained in:
undefined committed 2025-05-01 02:38:51 +08:00
1 parent d6865d86e5
commit 5b3bcd1134
8 files changed
+82 -23

No files matched your search

@@ -1,9 +1,12 @@
import { browserSupportsWebAuthnAutofill, startAuthentication } from '@simplewebauthn/browser';
import $ from 'jquery';
import responsiveCutoff from 'vj/breakpoints.json';
import DomDialog from 'vj/components/dialog/DomDialog';
import { InfoDialog } from 'vj/components/dialog/index';
import { AutoloadPage } from 'vj/misc/Page';
import { i18n, mediaQuery, tpl } from 'vj/utils';
import {
i18n, mediaQuery, request, tpl,
} from 'vj/utils';
const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => {
const signInDialog = DomDialog.getOrConstruct($('.dialog--signin'), {
@@ -11,13 +14,47 @@ const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => {
cancelByEsc: true,
});
let authnInitialized = false;
async function initPasskey() {
if (authnInitialized) return;
authnInitialized = true;
const support = await browserSupportsWebAuthnAutofill();
if (!support) return;
const authnInfo = await request.get('/user/webauthn', { login: true });
if (!authnInfo.authOptions) return;
const result = await startAuthentication({ optionsJSON: authnInfo.authOptions, useBrowserAutofill: true })
.catch((e) => {
Notification.error(i18n('Failed to get credential: {0}', e));
return null;
});
if (!result) return;
try {
const authn = await request.post('/user/webauthn', {
result,
});
if (authn.url) window.location.href = authn.url;
else if (authn.error) throw new Error(authn.error);
} catch (err) {
Notification.error(err.message);
console.error(err);
}
}
if (window.location.href.endsWith('/login')) initPasskey();
function show() {
signInDialog.show();
initPasskey();
}
// don't show quick login dialog if in mobile
if ($('[name="nav_login"]').length > 0) {
// nav
$('[name="nav_login"]').on('click', (ev) => {
if (mediaQuery.isBelow(responsiveCutoff.mobile)) return;
if (ev.shiftKey || ev.metaKey || ev.ctrlKey) return;
signInDialog.show();
show();
ev.preventDefault();
});
}
@@ -36,7 +73,7 @@ const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => {
return;
}
}
signInDialog.show();
show();
};
$('[data-lostpass]').on('click', (e) => {
+3 -3
View File
@@ -21,7 +21,7 @@
"@fontsource/source-code-pro": "^5.2.5",
"@fontsource/ubuntu-mono": "^5.2.5",
"@hydrooj/utils": "workspace:^",
"@sentry/browser": "^9.14.0",
"@sentry/browser": "^9.15.0",
"@sentry/webpack-plugin": "^3.3.1",
"@simplewebauthn/browser": "13.1.0",
"@svgr/webpack": "^8.1.0",
@@ -50,8 +50,8 @@
"clipboard": "^2.0.11",
"copy-webpack-plugin": "^13.0.0",
"cordis": "4.0.0-beta.2",
"core-js": "^3.41.0",
"core-js-compat": "^3.41.0",
"core-js": "^3.42.0",
"core-js-compat": "^3.42.0",
"css-loader": "^7.1.2",
"diff": "^7.0.0",
"diff-dom": "^5.1.4",
@@ -20,7 +20,7 @@
<div class="row"><div class="columns">
<label class="material textbox">
{{ _('Username') }}
<input name="uname" type="text" data-autofocus>
<input name="uname" type="text" data-autofocus autocomplete="username webauthn">
</label>
</div></div>
<div class="row"><div class="columns">
@@ -8,7 +8,7 @@
<div class="row"><div class="columns">
<label class="inverse material textbox">
{{ _('Username') }}
<input name="uname" type="text" autofocus>
<input name="uname" type="text" autofocus autocomplete="username webauthn">
</label>
</div></div>
<div class="row"><div class="columns">