1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-08-11 17:51:01 +00:00

refactor(js): enable touch handlers only on touch devices and fix various issues in WebAuthnHandler

This commit is contained in:
Frédéric Guillot 2025-08-02 15:34:54 -07:00
parent 4910f1f0f4
commit 52c1386450
2 changed files with 43 additions and 16 deletions

View file

@ -47,7 +47,7 @@ class TouchHandler {
} }
onItemTouchMove(event) { onItemTouchMove(event) {
if (event.touches === undefined || event.touches.length !== 1 || this.element === null) { if (event.touches === undefined || event.touches.length !== 1 || this.touch.element === null) {
return; return;
} }
@ -151,7 +151,15 @@ class TouchHandler {
} }
} }
static isTouchSupported() {
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
}
listen() { listen() {
if (!TouchHandler.isTouchSupported()) {
return;
}
const eventListenerOptions = { passive: true }; const eventListenerOptions = { passive: true };
document.querySelectorAll(".entry-swipe").forEach((element) => { document.querySelectorAll(".entry-swipe").forEach((element) => {

View file

@ -1,16 +1,15 @@
class WebAuthnHandler { class WebAuthnHandler {
static isWebAuthnSupported() { static isWebAuthnSupported() {
return window.PublicKeyCredential; return typeof PublicKeyCredential !== "undefined";
} }
static showErrorMessage(errorMessage) { static showErrorMessage(errorMessage) {
console.log("webauthn error: " + errorMessage); console.error("WebAuthn error:", errorMessage);
const alertElement = document.getElementById("webauthn-error-alert"); const alertElement = document.getElementById("webauthn-error-alert");
if (!alertElement) { if (alertElement) {
return;
}
alertElement.remove(); alertElement.remove();
}
const alertTemplateElement = document.getElementById("webauthn-error"); const alertTemplateElement = document.getElementById("webauthn-error");
if (alertTemplateElement) { if (alertTemplateElement) {
@ -23,15 +22,15 @@ class WebAuthnHandler {
} }
} }
async isConditionalLoginSupported() { static async isConditionalLoginSupported() {
return WebAuthnHandler.isWebAuthnSupported() && return WebAuthnHandler.isWebAuthnSupported() &&
window.PublicKeyCredential.isConditionalMediationAvailable && window.PublicKeyCredential.isConditionalMediationAvailable &&
window.PublicKeyCredential.isConditionalMediationAvailable(); await window.PublicKeyCredential.isConditionalMediationAvailable();
} }
async conditionalLogin(abortController) { async conditionalLogin(abortController) {
if (await this.isConditionalLoginSupported()) { if (await WebAuthnHandler.isConditionalLoginSupported()) {
this.login("", abortController); return this.login("", abortController);
} }
} }
@ -49,7 +48,7 @@ class WebAuthnHandler {
async post(urlKey, username, data) { async post(urlKey, username, data) {
let url = document.body.dataset[urlKey]; let url = document.body.dataset[urlKey];
if (username) { if (username) {
url += "?username=" + username; url += "?username=" + encodeURIComponent(username);
} }
return sendPOSTRequest(url, data); return sendPOSTRequest(url, data);
@ -58,7 +57,7 @@ class WebAuthnHandler {
async get(urlKey, username) { async get(urlKey, username) {
let url = document.body.dataset[urlKey]; let url = document.body.dataset[urlKey];
if (username) { if (username) {
url += "?username=" + username; url += "?username=" + encodeURIComponent(username);
} }
return fetch(url); return fetch(url);
} }
@ -83,14 +82,27 @@ class WebAuthnHandler {
return; return;
} }
const credentialCreationOptions = await registerBeginResponse.json(); let credentialCreationOptions;
try {
credentialCreationOptions = await registerBeginResponse.json();
} catch (err) {
WebAuthnHandler.showErrorMessage("Failed to parse registration options");
return;
}
credentialCreationOptions.publicKey.challenge = this.decodeBuffer(credentialCreationOptions.publicKey.challenge); credentialCreationOptions.publicKey.challenge = this.decodeBuffer(credentialCreationOptions.publicKey.challenge);
credentialCreationOptions.publicKey.user.id = this.decodeBuffer(credentialCreationOptions.publicKey.user.id); credentialCreationOptions.publicKey.user.id = this.decodeBuffer(credentialCreationOptions.publicKey.user.id);
if (Object.hasOwn(credentialCreationOptions.publicKey, 'excludeCredentials')) { if (Object.hasOwn(credentialCreationOptions.publicKey, 'excludeCredentials')) {
credentialCreationOptions.publicKey.excludeCredentials.forEach((credential) => credential.id = this.decodeBuffer(credential.id)); credentialCreationOptions.publicKey.excludeCredentials.forEach((credential) => credential.id = this.decodeBuffer(credential.id));
} }
const attestation = await navigator.credentials.create(credentialCreationOptions); let attestation;
try {
attestation = await navigator.credentials.create(credentialCreationOptions);
} catch (err) {
WebAuthnHandler.showErrorMessage(err);
return;
}
let registrationFinishResponse; let registrationFinishResponse;
try { try {
@ -109,7 +121,7 @@ class WebAuthnHandler {
} }
if (!registrationFinishResponse.ok) { if (!registrationFinishResponse.ok) {
throw new Error("Login failed with HTTP status code " + response.status); throw new Error("Registration failed with HTTP status code " + registrationFinishResponse.status);
} }
const jsonData = await registrationFinishResponse.json(); const jsonData = await registrationFinishResponse.json();
@ -125,7 +137,14 @@ class WebAuthnHandler {
return; return;
} }
const credentialRequestOptions = await loginBeginResponse.json(); let credentialRequestOptions;
try {
credentialRequestOptions = await loginBeginResponse.json();
} catch (err) {
WebAuthnHandler.showErrorMessage("Failed to parse login options");
return;
}
credentialRequestOptions.publicKey.challenge = this.decodeBuffer(credentialRequestOptions.publicKey.challenge); credentialRequestOptions.publicKey.challenge = this.decodeBuffer(credentialRequestOptions.publicKey.challenge);
if (Object.hasOwn(credentialRequestOptions.publicKey, 'allowCredentials')) { if (Object.hasOwn(credentialRequestOptions.publicKey, 'allowCredentials')) {