UniAuth.ID

Logout & Session Propagation

UniAuth supports three complementary logout mechanisms from OIDC 1.0. The goal: when a user signs out of your app, the session is revoked at UniAuth too, and other apps the user signed into can be notified.

Common mistake: clearing your own session cookie does not log the user out of UniAuth. They can re-authenticate with a single click. Always use end_session_endpoint for real logout, and configure a back-channel logout URL so UniAuth can notify you when the user signs out elsewhere.

1. RP-Initiated Logout

When the user clicks “sign out” in your app, redirect them to the end_session_endpoint. UniAuth will revoke their session and redirect back to your configured post-logout URL.

// Build the logout URL from discovery
const params = new URLSearchParams({
  id_token_hint: userIdToken,            // so UniAuth knows who to log out
  post_logout_redirect_uri: 'https://yourapp.com/',
  state: 'xyz123',                       // optional, for CSRF
})
window.location.href =
  `https://uniauth.id/api/oauth/end-session?${params}`

With our SDK, this is a single call:

await auth.logout({ postLogoutRedirectUri: 'https://yourapp.com/' })
// SDK handles: revoke refresh token → redirect to end_session_endpoint

2. Back-Channel Logout

When the user logs out of UniAuth (from any app, or the UniAuth dashboard), UniAuth sends a signed logout token via HTTP POST to every registered backchannel_logout_uri. Your app invalidates the local session server-side. No browser redirects involved.

Register your back-channel URL when creating the OAuth client in the Developer Console.

Verifying the Logout Token

UniAuth sends a logout token as application/x-www-form-urlencoded with logout_token=.... It's an RS256-signed JWT:

// Node.js example using jose
import { jwtVerify, createRemoteJWKSet } from 'jose'

const JWKS = createRemoteJWKSet(
  new URL('https://uniauth.id/.well-known/jwks.json')
)

app.post('/logout-webhook', express.urlencoded(), async (req, res) => {
  const token = req.body.logout_token
  try {
    const { payload } = await jwtVerify(token, JWKS, {
      issuer: 'https://uniauth.id',
      audience: process.env.CLIENT_ID,
    })

    // Required claim checks per OIDC Back-Channel Logout 1.0
    if (!payload.sid && !payload.sub) {
      return res.status(400).end()
    }
    if (payload.events?.['http://schemas.openid.net/event/backchannel-logout'] == null) {
      return res.status(400).end()
    }
    if (payload.nonce) {
      // Logout tokens MUST NOT contain a nonce
      return res.status(400).end()
    }

    // Invalidate all your local sessions for this user / sid
    await invalidateSession({ sub: payload.sub, sid: payload.sid })

    res.status(200).end()
  } catch (err) {
    res.status(400).end()
  }
})

3. Front-Channel Logout

An alternative to back-channel: UniAuth returns iframe URLs to load during logout. Each iframe loads your registered frontchannel_logout_uri with ?iss=...&sid=.... Your app clears client-side state (cookies, localStorage) server-side using the sid.

Use back-channel when possible — front-channel requires third-party cookies, which most browsers now block.

Which Should You Use?

MechanismWhen It FiresRequires Browser?Recommended?
end_sessionUser clicks logout in your appYes (redirect)✅ Always
back-channelLogout from any app or UniAuth dashboardNo (server-to-server)✅ If you have a backend
front-channelSame as back-channelYes (iframe)⚠️ Third-party cookies often blocked

See Also