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:
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;
}