fix(encryptid): show success page instead of auto-OIDC redirect
The auto-redirect through OIDC authorize fails because the client app (Postiz) didn't initiate the OAuth flow and has no matching state. Instead, show a branded success page with a link to the app. The user signs in with their passkey when they visit the app normally. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d861c0ad99
commit
8723aae3f6
|
|
@ -4165,11 +4165,16 @@ app.get('/api/invites/identity/:token/info', async (c) => {
|
||||||
if (Date.now() > invite.expiresAt) {
|
if (Date.now() > invite.expiresAt) {
|
||||||
return c.json({ error: 'Invite expired' }, 410);
|
return c.json({ error: 'Invite expired' }, 410);
|
||||||
}
|
}
|
||||||
// Look up OIDC client name if this is a client invite
|
// Look up OIDC client info if this is a client invite
|
||||||
let clientName: string | null = null;
|
let clientName: string | null = null;
|
||||||
|
let clientAppUrl: string | null = null;
|
||||||
if (invite.clientId) {
|
if (invite.clientId) {
|
||||||
const client = await getOidcClient(invite.clientId);
|
const client = await getOidcClient(invite.clientId);
|
||||||
clientName = client?.name || null;
|
clientName = client?.name || null;
|
||||||
|
// Derive app URL from first redirect URI (strip the callback path)
|
||||||
|
if (client?.redirectUris?.[0]) {
|
||||||
|
try { clientAppUrl = new URL(client.redirectUris[0]).origin; } catch {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return c.json({
|
return c.json({
|
||||||
invitedBy: invite.invitedByUsername,
|
invitedBy: invite.invitedByUsername,
|
||||||
|
|
@ -4178,6 +4183,7 @@ app.get('/api/invites/identity/:token/info', async (c) => {
|
||||||
spaceSlug: invite.spaceSlug,
|
spaceSlug: invite.spaceSlug,
|
||||||
clientId: invite.clientId,
|
clientId: invite.clientId,
|
||||||
clientName,
|
clientName,
|
||||||
|
clientAppUrl,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -4774,24 +4780,23 @@ function oidcAcceptPage(token: string): string {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is an OIDC client invite, redirect through the OIDC authorize flow
|
// Show success with link to the app
|
||||||
if (inviteData.clientId) {
|
|
||||||
showStatus('Redirecting to ' + (inviteData.clientName || 'the app') + '...');
|
|
||||||
// Store the session token so the OIDC authorize page can use it
|
|
||||||
localStorage.setItem('eid_token', sessionToken);
|
|
||||||
// Start the OIDC authorize flow — the authorize page will auto-login
|
|
||||||
window.location.href = '/oidc/authorize?client_id=' + encodeURIComponent(inviteData.clientId) +
|
|
||||||
'&response_type=code&scope=openid+profile+email&redirect_uri=' + encodeURIComponent('auto') +
|
|
||||||
'&state=invite_accept';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Non-OIDC invite — show success
|
|
||||||
document.getElementById('authSection').style.display = 'none';
|
document.getElementById('authSection').style.display = 'none';
|
||||||
statusEl.style.display = 'none';
|
statusEl.style.display = 'none';
|
||||||
successEl.innerHTML = '<strong>Welcome!</strong><br>Your invitation has been accepted.' +
|
|
||||||
(claimData.spaceSlug ? '<br>You\\'ve been added to <strong>' + esc(claimData.spaceSlug) + '</strong>.' : '') +
|
const appName = inviteData.clientName || 'rSpace';
|
||||||
'<br><br><a href="https://rspace.online" style="color: #7c3aed;">Go to rSpace \\u2192</a>';
|
const appUrl = inviteData.clientAppUrl || 'https://rspace.online';
|
||||||
|
|
||||||
|
if (inviteData.clientId) {
|
||||||
|
successEl.innerHTML = '<strong>You\\u2019re in!</strong><br>' +
|
||||||
|
'Your account is set up and you\\u2019ve been granted access to <strong>' + esc(appName) + '</strong>.' +
|
||||||
|
'<br><br><a href="' + esc(appUrl) + '" style="display:inline-block;padding:0.7rem 1.5rem;background:linear-gradient(90deg,#00d4ff,#7c3aed);color:#fff;text-decoration:none;border-radius:0.5rem;font-weight:600;">Go to ' + esc(appName) + ' \\u2192</a>' +
|
||||||
|
'<p style="color:#94a3b8;font-size:0.8rem;margin-top:1rem;">You\\u2019ll sign in with your passkey when you get there.</p>';
|
||||||
|
} else {
|
||||||
|
successEl.innerHTML = '<strong>Welcome!</strong><br>Your invitation has been accepted.' +
|
||||||
|
(claimData.spaceSlug ? '<br>You\\u2019ve been added to <strong>' + esc(claimData.spaceSlug) + '</strong>.' : '') +
|
||||||
|
'<br><br><a href="https://rspace.online" style="color: #7c3aed;">Go to rSpace \\u2192</a>';
|
||||||
|
}
|
||||||
successEl.style.display = 'block';
|
successEl.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4946,7 +4951,7 @@ app.get('/.well-known/openid-configuration', (c) => {
|
||||||
// Authorization endpoint
|
// Authorization endpoint
|
||||||
app.get('/oidc/authorize', async (c) => {
|
app.get('/oidc/authorize', async (c) => {
|
||||||
const clientId = c.req.query('client_id');
|
const clientId = c.req.query('client_id');
|
||||||
let redirectUri = c.req.query('redirect_uri');
|
const redirectUri = c.req.query('redirect_uri');
|
||||||
const responseType = c.req.query('response_type');
|
const responseType = c.req.query('response_type');
|
||||||
const scope = c.req.query('scope') || 'openid profile email';
|
const scope = c.req.query('scope') || 'openid profile email';
|
||||||
const state = c.req.query('state') || '';
|
const state = c.req.query('state') || '';
|
||||||
|
|
@ -4963,11 +4968,6 @@ app.get('/oidc/authorize', async (c) => {
|
||||||
return c.text('Unknown client_id', 400);
|
return c.text('Unknown client_id', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// "auto" redirect_uri: use the client's first registered redirect URI (from invite accept flow)
|
|
||||||
if (redirectUri === 'auto') {
|
|
||||||
redirectUri = client.redirectUris[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!client.redirectUris.includes(redirectUri)) {
|
if (!client.redirectUris.includes(redirectUri)) {
|
||||||
return c.text('Invalid redirect_uri', 400);
|
return c.text('Invalid redirect_uri', 400);
|
||||||
}
|
}
|
||||||
|
|
@ -5353,39 +5353,6 @@ function oidcAuthorizePage(appName: string, clientId: string, redirectUri: strin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-authorize if coming from invite accept flow with a stored session token
|
|
||||||
(async () => {
|
|
||||||
if (STATE !== 'invite_accept') return;
|
|
||||||
const storedToken = localStorage.getItem('eid_token');
|
|
||||||
if (!storedToken) return;
|
|
||||||
localStorage.removeItem('eid_token');
|
|
||||||
loginBtn.disabled = true;
|
|
||||||
showStatus('Authorizing...');
|
|
||||||
try {
|
|
||||||
const authorizeRes = await fetch('/oidc/authorize', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
clientId: CLIENT_ID,
|
|
||||||
redirectUri: REDIRECT_URI,
|
|
||||||
scope: SCOPE,
|
|
||||||
state: STATE,
|
|
||||||
token: storedToken,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const authorizeResult = await authorizeRes.json();
|
|
||||||
if (authorizeResult.error) {
|
|
||||||
showError(authorizeResult.message || authorizeResult.error);
|
|
||||||
loginBtn.disabled = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showStatus('Redirecting...');
|
|
||||||
window.location.href = authorizeResult.redirectUrl;
|
|
||||||
} catch (err) {
|
|
||||||
showError('Auto-login failed. Please sign in manually.');
|
|
||||||
loginBtn.disabled = false;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue