Portal Community

Logout Types

TypeWhat It DoesWhen to Use
Local LogoutClears 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();
}
Always Revoke, Then Redirect

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.