Logout
Properly terminate both the application session and the Passport SSO session — local logout clears your app's tokens; Passport logout ends the SSO session for all connected applications.
Logout Types
| Type | What It Does | When to Use |
|---|---|---|
| Local Logout | Clears tokens from your application only. Passport session remains active — user can log back in without re-entering credentials. | Low-security apps; user clicks "switch account" |
| Passport Logout (End Session) | Terminates the Passport session. User must re-authenticate to access any SSO-connected app. Does not notify other SPs automatically (OIDC does not include SLO). | Default logout for most applications |
| Full SSO Logout (SAML SLO) | Terminates Passport session AND sends logout notifications to all SAML-connected SPs. User is logged out everywhere. | High-security / shared device scenarios |
OIDC End Session (Recommended)
// Step 1 — Clear local application session/tokens
await _tokenStorage.ClearTokensAsync(userId);
HttpContext.Session.Clear();
Response.Cookies.Delete(".BizFirst.Session");
// Step 2 — Redirect to Passport end session endpoint
// The id_token_hint ensures Passport knows which user to log out
var endSessionUrl = new UriBuilder("https://passport.bizfirst.ai/passport/logout");
endSessionUrl.Query = QueryString.Create([
("id_token_hint", idToken), // the id_token from the original login
("post_logout_redirect_uri", "https://myapp.example.com/"),
("state", Guid.NewGuid().ToString("N"))
]).ToString();
return Redirect(endSessionUrl.ToString());
ASP.NET Core Logout Action
[HttpPost("/auth/logout")]
[Authorize]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
var idInfo = HttpContext.GetIDInfo();
var idToken = await _tokenStorage.GetIdTokenAsync(idInfo!.UserId);
// Clear local session
await _tokenStorage.ClearTokensAsync(idInfo.UserId);
await HttpContext.SignOutAsync();
// Revoke refresh token at Passport
var refreshToken = await _tokenStorage.GetRefreshTokenAsync(idInfo.UserId);
if (refreshToken is not null)
{
await _passportClient.RevokeTokenAsync(refreshToken);
}
// Redirect to Passport end session
var logoutUrl = _passportOptions.BuildLogoutUrl(
idTokenHint: idToken,
postLogoutRedirectUri: Url.ActionAbsolute("Index", "Home"));
return Redirect(logoutUrl);
}
// Callback after Passport ends the session
[HttpGet("/auth/logged-out")]
[AllowAnonymous]
public IActionResult LoggedOut() => View();
Token Revocation
When logging out, revoke the refresh token at Passport. This prevents the token from being used even if an attacker somehow obtained it.
POST /passport/token/revoke
Authorization: Basic base64("workflow-api-prod:client-secret")
Content-Type: application/x-www-form-urlencoded
token=refresh-token-to-revoke
&token_type_hint=refresh_token
// Response — always 200 OK (even if token was already revoked)
// RFC 7009 — token revocation is idempotent
Handling Back-Channel Logout (Pushed Logout)
Passport can push back-channel logout notifications to registered applications when a session is administratively terminated (e.g., security incident, admin revokes session). Register a logout notification endpoint in your client registration:
// Consumer registration addition
{
"backChannelLogoutUri": "https://myapp.example.com/auth/back-channel-logout",
"backChannelLogoutSessionRequired": true
}
// Your back-channel logout handler
[HttpPost("/auth/back-channel-logout")]
[AllowAnonymous]
public async Task<IActionResult> BackChannelLogout([FromForm] string logout_token)
{
// Validate the logout_token JWT (same as access token validation)
var result = await _validator.ValidateAsync(logout_token);
if (!result.IsValid) return BadRequest();
// Extract the session ID or subject
var sub = result.Principal!.FindFirstValue("sub");
var sid = result.Principal!.FindFirstValue("sid");
// Terminate the user's session in your application
await _sessionService.TerminateAsync(sub: sub, sessionId: sid);
return Ok();
}
The correct logout sequence is: (1) clear local tokens, (2) revoke refresh token at Passport, (3) redirect to Passport end session endpoint. Skipping step 2 leaves the refresh token valid until it naturally expires — a security risk on shared devices.