conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

commit e607d6432f741e4e8b42ad3504ed433b489a0317
parent f8f8ff8afe8732f6aee0b16dd9698ea58501f5ee
Author: finwo <finwo@pm.me>
Date:   Sat, 26 Sep 2026 22:23:00 +0200

Send id_token_hint and client_id when ending a provider session

Diffstat:
Mtest/provider.test.js | 12++++++++++++
Mweb/src/util/auth.ts | 6++++++
2 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/test/provider.test.js b/test/provider.test.js @@ -328,6 +328,18 @@ test('logout redirects only to a registered uri', async () => { }); }); +test('logout ignores id_token_hint and client_id', async () => { + await withConductor(async (c) => { + const res = await c.app.inject({ + method: 'GET', + url: `/auth/logout?post_logout_redirect_uri=${encodeURIComponent(PUBLIC_URL)}` + + '&client_id=conductor&id_token_hint=whatever', + }); + assert.equal(res.statusCode, 303); + assert.equal(res.headers.location, PUBLIC_URL); + }); +}); + test('the key set is advertised and empty', async () => { await withConductor(async (c) => { const res = await c.app.inject({ method: 'GET', url: '/auth/jwks' }); diff --git a/web/src/util/auth.ts b/web/src/util/auth.ts @@ -5,6 +5,7 @@ const STORE = 'conductor.refresh'; const PENDING = 'conductor.pending'; +const IDENTITY = 'conductor.identity'; export type Discovery = { issuer: string; @@ -120,6 +121,7 @@ async function exchange(doc: Discovery, body: Record<string, string>): Promise<v accessToken = grant.access_token; expiresAt = Date.now() + (grant.expires_in ?? 300) * 1000; if (grant.refresh_token) localStorage.setItem(STORE, grant.refresh_token); + if (grant.id_token) localStorage.setItem(IDENTITY, grant.id_token); } let refreshing: Promise<void> | null = null; @@ -153,10 +155,12 @@ export function forget(): void { accessToken = null; expiresAt = 0; localStorage.removeItem(STORE); + localStorage.removeItem(IDENTITY); } export async function signOut(): Promise<void> { const token = refreshToken(); + const identity = localStorage.getItem(IDENTITY); const doc = discovery; if (doc?.revocation_endpoint && token) { @@ -172,6 +176,8 @@ export async function signOut(): Promise<void> { if (doc?.end_session_endpoint) { const url = new URL(doc.end_session_endpoint); url.searchParams.set('post_logout_redirect_uri', redirectUri()); + url.searchParams.set('client_id', doc.client_id); + if (identity) url.searchParams.set('id_token_hint', identity); location.assign(url.toString()); return; }