Single Logout
OIDC Single Logout ties session termination in both directions. Receive Back-Channel Logout with createBackchannelLogoutHandler (reopt → your app), and trigger RP-Initiated Logout with buildEndSessionUrl (your app → reopt). Verification is zero-dependency (Web Crypto Ed25519); the dev server signs real tokens so the demo runs offline.
New in 3.2.0 — /logout sub-path.
The simulator seeds a local session tagged with a reopt sid, mints a real EdDSA logout_token from the in-memory dev IdP, and POSTs it form-encoded to the actual /api/auth/backchannel-logout receiver — the same wire shape reopt uses in production. Valid tokens revoke the session; each reject scenario exercises a different verify branch.
1 · Back-Channel Logout (reopt → your app)
Receive server-to-server notification and end the matching local sessions with createBackchannelLogoutHandler.
Local sessions (your app)
No local sessions. Seed one to represent a signed-in user whose session row carries a reopt sid.
Seed a session first — the back-channel logout revokes sessions by their reopt sid.
2 · RP-Initiated Logout (your app → reopt)
End the reopt session too by sending the user through buildEndSessionUrl.
Must be pre-registered on your OAuth client for reopt to redirect back
Echoed back on the return URL — carry a CSRF token here
import { createBackchannelLogoutHandler } from "@reopt-ai/brandapp-sdk/logout";
// Web-standard (Request) => Response — mount in Next.js, Hono, or any fetch server.
// The handler verifies the EdDSA logout_token against reopt's discovery + JWKS
// (Web Crypto, zero deps), then calls onLogout on success.
export const POST = createBackchannelLogoutHandler({
onLogout: async ({ sid, sub }) => {
// End every LOCAL session derived from this reopt session id.
// Make it idempotent — reopt retries on any non-2xx delivery.
await db.sessions.deleteMany({ where: { reoptSid: sid } });
},
});
// Response contract:
// 200 valid + onLogout ok 501 onLogout threw (reopt retries)
// 400 invalid/expired token 503 discovery/JWKS briefly unreachable
// 405 non-POST (retryable — NOT a bad token, 3.3.0)
// All responses carry Cache-Control: no-store (spec requirement).