Fix local-auth sections not showing for normal users + backup-code modal signature

settings load2FAStatus():
  - Explicit credentials: 'same-origin' on the /me fetch (was relying
    on fetch defaults, which can behave oddly in some browsers/edges)
  - Fall back to window.CURRENT_USER (cached at login) if /me fails,
    so local-auth users still see their password/2FA sections after
    a transient error. Keeps cache in sync on each successful fetch.

enterApp():
  - Cache the logged-in user object on window.CURRENT_USER so modules
    that need the canLocalAuth flag don't have to re-fetch /me.

2FA regenerate modal:
  - Previous call passed a wrong-shape options object to showConfirm.
    Updated to the correct (message, callback, opts) signature with
    input:true, inputType:'password', placeholder, required.

OIDC email_verified check:
  - Accept boolean true or string 'true' for robustness. Some IdPs
    serialize ID-token booleans as strings.
This commit is contained in:
Daniel 2026-04-14 04:43:07 +02:00
parent c7a04626a3
commit 37e58be5ec
2 changed files with 24 additions and 12 deletions

View file

@ -147,6 +147,7 @@ document.addEventListener('DOMContentLoaded', function() {
function enterApp(user, token) {
window.AUTH_TOKEN = token;
window.CURRENT_USER = user; // cached so settings page doesn't re-fetch /me just to read canLocalAuth
if (isNativeApp()) {
// Mobile: persist token to Keychain/Keystore
window.SecureStorage.set(TOKEN_KEY, token);
@ -356,15 +357,7 @@ document.addEventListener('DOMContentLoaded', function() {
// Regenerate backup codes
if (target.id === 'btn-regen-backup-codes' || target.closest('#btn-regen-backup-codes')) {
e.preventDefault();
showConfirm({
title: 'Regenerate backup codes?',
message: 'This will invalidate your existing backup codes and generate 10 new ones. Enter your current password.',
inputType: 'password',
inputPlaceholder: 'Current password',
confirmText: 'Regenerate',
required: true,
requiredMsg: 'Password is required'
}, function(pw) {
showConfirm('Regenerate backup codes? This invalidates your existing ones. Enter your current password to confirm.', function(pw) {
if (!pw) return;
fetch('/api/auth/2fa/backup-codes', {
method: 'POST', headers: getAuthHeaders(),
@ -375,6 +368,13 @@ document.addEventListener('DOMContentLoaded', function() {
if (data.success && data.codes) showBackupCodesModal(data.codes);
else showToast(data.error || 'Failed to regenerate codes', 'error');
});
}, {
input: true,
inputType: 'password',
placeholder: 'Current password',
confirmText: 'Regenerate',
required: true,
requiredMsg: 'Password is required'
});
}
@ -729,10 +729,20 @@ document.addEventListener('DOMContentLoaded', function() {
// ---- 2FA STATUS ----
function load2FAStatus() {
fetch('/api/auth/me', { headers: getAuthHeaders() })
fetch('/api/auth/me', { credentials: 'same-origin', headers: getAuthHeaders() })
.then(function(r) { return r.json(); })
.then(function(data) {
if (!data || !data.user) return;
if (!data || !data.user) {
// /me failed — if we already have a cached user from login, fall back to that
// so reloaded Settings pages still render for local-auth users.
if (window.CURRENT_USER && window.CURRENT_USER.canLocalAuth === true) {
data = { user: window.CURRENT_USER };
} else {
return;
}
}
// Keep cache in sync
window.CURRENT_USER = data.user;
// Sections are display:none by default in the HTML. Only reveal them
// if the server confirms this user has a real password hash
// (canLocalAuth === true). SSO-auto-created users never see these UIs.

View file

@ -203,7 +203,9 @@ router.get('/oidc/callback', async function(req, res) {
return res.redirect(appUrl + '?error=sub_mismatch');
}
if (!user.oidc_sub) {
if (emailVerified !== true) {
// Some IdPs (rare) serialize booleans as strings — accept both.
var verified = emailVerified === true || emailVerified === 'true';
if (!verified) {
console.warn('[OIDC] refusing to auto-link existing user ' + user.id + ' — IdP did not assert email_verified=true');
return res.redirect(appUrl + '?error=email_unverified');
}