Guides

List and revoke signed-in sessions

Let users see where they are signed in and sign out other browsers or devices, for cookie and JWT sessions.

Active sessions are off by default. When you turn them on, every successful sign-in (password, emailed code, passkey, two-factor, or GitHub or Google) records one session. A user can list their sessions and sign out any of them. Cookie and JWT sessions are supported. Identity bearer tokens are not, and the app fails at startup if it maps them while active sessions are on.

Turn on active sessions

  1. Add the session table to your DbContext. With JWT, call UseRefreshToken() first so that refresh tokens get a SessionId column.
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.UseRefreshToken();   // JWT only
        builder.UseActiveSessions();
    }
    
  2. Register the services. With JWT, call this after AddJwtEndpoints (or AddPasswordAuth) and after any custom refresh-token service or access-token generator.
    builder.Services.AddActiveSessions<AppUser, AppDbContext>(options =>
    {
        options.LastSeenUpdateInterval = TimeSpan.FromMinutes(5); // default
        options.RevalidationInterval = TimeSpan.FromSeconds(30); // default
        options.CleanupInterval = TimeSpan.FromHours(1);          // default
    });
    
  3. Map the endpoints on the same prefix as your sign-in endpoints.
    app.MapGroup("/auth").MapActiveSessionEndpoints<AppUser>();
    
  4. Add a migration. It creates the AuthEndpointsActiveSessions table and, with JWT, the nullable SessionId column on refresh tokens.

Without these calls the model, the database, and the endpoints are unchanged.

Endpoints

EndpointResultRe-authenticationCSRF
GET /auth/sessions200 with [{ id, userAgent, createdAt, lastSeenAt, current }]NoNo
DELETE /auth/sessions/{id}204, or 404 when the session is not the caller'sYesYes for cookie callers
POST /auth/sessions/revokeOthers200 with { revoked }. The current session keeps working. 409 when the caller has no current session, such as a JWT issued before sessions were turned on.YesYes for cookie callers

The list only contains the caller's sessions that are still active. current is true for the session that made the request. lastSeenAt is updated at most once per LastSeenUpdateInterval.

Revoking needs a recent re-authentication, like unlinking an external account. Without it the endpoints return 403.

What happens after a revoke

  • Cookie. The revoked browser's next request returns 401, and its cookie is removed. To avoid a database read on every request, a cookie's session is checked at most once per RevalidationInterval (30 seconds by default). A revoke on the same server applies right away; with several servers, a revoke can take up to RevalidationInterval to apply elsewhere. Set it to TimeSpan.Zero to check every request.
  • JWT. The session's refresh tokens are revoked, so the next POST /auth/refresh fails. An access token that was already issued stays valid until it expires, so keep access tokens short-lived.

When sessions end on their own

  • Signing out (POST /auth/logout) ends the current session.
  • Setting or changing a password, or anything else that changes the security stamp, ends the user's other sessions. Cookie sessions end at their next security stamp check (SecurityStampValidatorOptions.ValidationInterval). JWT sessions end at their next refresh, or when the user lists their sessions.
  • A cookie or refresh token that expires ends its session.

A background job deletes expired and revoked session records every CleanupInterval.

Limits

  • Identity bearer tokens (AuthEndpointsSession.IdentityBearer) are not supported.
  • Cookies issued before you turned sessions on get a session the first time they are used.