fix(encryptid): persist verified email and fix OIDC re-prompt bug
Normalize emails to lowercase at all setUserEmail() call sites so case mismatches no longer break the OIDC allowedEmails check. Split the authorize error into email_required (shows verification form) vs access_denied (shows error message) so users with a verified email are never re-prompted unnecessarily. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
154f1230dc
commit
79448a230a
|
|
@ -581,7 +581,7 @@ app.post('/api/register/complete', async (c) => {
|
||||||
|
|
||||||
// Set recovery email if provided during registration
|
// Set recovery email if provided during registration
|
||||||
if (email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
if (email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
||||||
await setUserEmail(userId, email);
|
await setUserEmail(userId, email.trim().toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the RP ID from the caller's origin
|
// Resolve the RP ID from the caller's origin
|
||||||
|
|
@ -1203,7 +1203,7 @@ app.post('/api/account/email/verify', async (c) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
await markRecoveryTokenUsed(tokenKey);
|
await markRecoveryTokenUsed(tokenKey);
|
||||||
await setUserEmail(claims.sub as string, email);
|
await setUserEmail(claims.sub as string, email.trim().toLowerCase());
|
||||||
|
|
||||||
return c.json({ success: true, email });
|
return c.json({ success: true, email });
|
||||||
});
|
});
|
||||||
|
|
@ -1433,8 +1433,9 @@ app.post('/api/recovery/email/set', async (c) => {
|
||||||
return c.json({ error: 'Valid email required' }, 400);
|
return c.json({ error: 'Valid email required' }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
await setUserEmail(payload.sub as string, email);
|
const normalizedEmail = email.trim().toLowerCase();
|
||||||
return c.json({ success: true, email });
|
await setUserEmail(payload.sub as string, normalizedEmail);
|
||||||
|
return c.json({ success: true, email: normalizedEmail });
|
||||||
} catch {
|
} catch {
|
||||||
return c.json({ error: 'Unauthorized' }, 401);
|
return c.json({ error: 'Unauthorized' }, 401);
|
||||||
}
|
}
|
||||||
|
|
@ -5441,10 +5442,14 @@ app.post('/oidc/authorize', async (c) => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return c.json({ error: 'User not found' }, 404);
|
return c.json({ error: 'User not found' }, 404);
|
||||||
}
|
}
|
||||||
const userEmail = user.email || user.profile_email;
|
const userEmail = (user.email || user.profile_email || '').toLowerCase();
|
||||||
if (client.allowedEmails.length > 0) {
|
if (client.allowedEmails.length > 0) {
|
||||||
if (!userEmail || !client.allowedEmails.includes(userEmail)) {
|
const allowedLower = client.allowedEmails.map((e: string) => e.toLowerCase());
|
||||||
return c.json({ error: 'access_denied', message: 'You do not have access to this application.' }, 403);
|
if (!userEmail) {
|
||||||
|
return c.json({ error: 'email_required', message: 'Email verification is required for this application.' }, 403);
|
||||||
|
}
|
||||||
|
if (!allowedLower.includes(userEmail)) {
|
||||||
|
return c.json({ error: 'access_denied', message: 'Your email is not authorized for this application.' }, 403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5906,8 +5911,8 @@ function oidcAuthorizePage(appName: string, clientId: string, redirectUri: strin
|
||||||
const authorizeResult = await authorizeRes.json();
|
const authorizeResult = await authorizeRes.json();
|
||||||
|
|
||||||
if (authorizeResult.error) {
|
if (authorizeResult.error) {
|
||||||
if (authorizeResult.error === 'access_denied') {
|
if (authorizeResult.error === 'email_required') {
|
||||||
// Show email verification flow instead of dead-end error
|
// User has no verified email — show email verification form
|
||||||
loginBtn.style.display = 'none';
|
loginBtn.style.display = 'none';
|
||||||
statusEl.style.display = 'none';
|
statusEl.style.display = 'none';
|
||||||
verifySection.style.display = 'block';
|
verifySection.style.display = 'block';
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue