Fix webauthn regression and improve code (#25113)
Follow: * #22697 There are some bugs in #22697: * https://github.com/go-gitea/gitea/pull/22697#issuecomment-1577957966 * the webauthn failure message is never shown and causes console error * The `document.getElementById('register-button')` and `document.getElementById('login-button')` is wrong * there is no such element in code * it causes JS error when a browser doesn't provide webauthn * the end user can't see the real error message These bugs are fixed in this PR. Other changes: * Use simple HTML/CSS layouts, no need to use too many `gt-` patches * Make the webauthn page have correct "page-content" layout * The "data-webauthn-error-msg" elements are only used to provide locale texts, so move them into a single "gt-hidden", then no need to repeat a lot of "gt-hidden" in code * The `{{.CsrfTokenHtml}}` is a no-op because there is no form * Many `hideElem('#webauthn-error')` in code is no-op because the `webauthn-error` already has "gt-hidden" by default * Make the tests for "URLEncodedBase64" really test with concrete cases. Screenshots: * Error message when webauthn fails (before, there is no error message): <details>  </details> * Error message when webauthn is unavailable <details>  </details>
This commit is contained in:
parent
58536093b3
commit
027014d7de
6 changed files with 57 additions and 66 deletions
|
@ -1,11 +1,9 @@
|
|||
import {encodeURLEncodedBase64, decodeURLEncodedBase64} from '../utils.js';
|
||||
import {showElem, hideElem} from '../utils/dom.js';
|
||||
import {showElem} from '../utils/dom.js';
|
||||
|
||||
const {appSubUrl, csrfToken} = window.config;
|
||||
|
||||
export async function initUserAuthWebAuthn() {
|
||||
hideElem('#webauthn-error');
|
||||
|
||||
const elPrompt = document.querySelector('.user.signin.webauthn-prompt');
|
||||
if (!elPrompt) {
|
||||
return;
|
||||
|
@ -25,10 +23,10 @@ export async function initUserAuthWebAuthn() {
|
|||
for (const cred of options.publicKey.allowCredentials) {
|
||||
cred.id = decodeURLEncodedBase64(cred.id);
|
||||
}
|
||||
const credential = await navigator.credentials.get({
|
||||
publicKey: options.publicKey
|
||||
});
|
||||
try {
|
||||
const credential = await navigator.credentials.get({
|
||||
publicKey: options.publicKey
|
||||
});
|
||||
await verifyAssertion(credential);
|
||||
} catch (err) {
|
||||
if (!options.publicKey.extensions?.appid) {
|
||||
|
@ -36,10 +34,10 @@ export async function initUserAuthWebAuthn() {
|
|||
return;
|
||||
}
|
||||
delete options.publicKey.extensions.appid;
|
||||
const credential = await navigator.credentials.get({
|
||||
publicKey: options.publicKey
|
||||
});
|
||||
try {
|
||||
const credential = await navigator.credentials.get({
|
||||
publicKey: options.publicKey
|
||||
});
|
||||
await verifyAssertion(credential);
|
||||
} catch (err) {
|
||||
webAuthnError('general', err.message);
|
||||
|
@ -48,7 +46,7 @@ export async function initUserAuthWebAuthn() {
|
|||
}
|
||||
|
||||
async function verifyAssertion(assertedCredential) {
|
||||
// Move data into Arrays incase it is super long
|
||||
// Move data into Arrays in case it is super long
|
||||
const authData = new Uint8Array(assertedCredential.response.authenticatorData);
|
||||
const clientDataJSON = new Uint8Array(assertedCredential.response.clientDataJSON);
|
||||
const rawId = new Uint8Array(assertedCredential.rawId);
|
||||
|
@ -137,15 +135,11 @@ function webAuthnError(errorType, message) {
|
|||
|
||||
function detectWebAuthnSupport() {
|
||||
if (!window.isSecureContext) {
|
||||
document.getElementById('register-button').disabled = true;
|
||||
document.getElementById('login-button').disabled = true;
|
||||
webAuthnError('insecure');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof window.PublicKeyCredential !== 'function') {
|
||||
document.getElementById('register-button').disabled = true;
|
||||
document.getElementById('login-button').disabled = true;
|
||||
webAuthnError('browser');
|
||||
return false;
|
||||
}
|
||||
|
@ -158,15 +152,13 @@ export function initUserAuthWebAuthnRegister() {
|
|||
if (!elRegister) {
|
||||
return;
|
||||
}
|
||||
|
||||
hideElem('#webauthn-error');
|
||||
|
||||
elRegister.addEventListener('click', (e) => {
|
||||
if (!detectWebAuthnSupport()) {
|
||||
elRegister.disabled = true;
|
||||
return;
|
||||
}
|
||||
elRegister.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
if (!detectWebAuthnSupport()) {
|
||||
return;
|
||||
}
|
||||
webAuthnRegisterRequest();
|
||||
await webAuthnRegisterRequest();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -203,15 +195,12 @@ async function webAuthnRegisterRequest() {
|
|||
}
|
||||
}
|
||||
|
||||
let credential;
|
||||
try {
|
||||
credential = await navigator.credentials.create({
|
||||
const credential = await navigator.credentials.create({
|
||||
publicKey: options.publicKey
|
||||
});
|
||||
await webauthnRegistered(credential);
|
||||
} catch (err) {
|
||||
webAuthnError('unknown', err);
|
||||
return;
|
||||
}
|
||||
|
||||
webauthnRegistered(credential);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue