IdentityServer Sessions, Dynamic Providers, and CIBA
When to Use This Skill
- Enabling and configuring server-side sessions for authentication state management
- Implementing session querying, revocation, and administrative tooling via
ISessionManagementService - Configuring inactivity timeout across IdentityServer and client applications
- Setting up the Entity Framework Core session store or implementing a custom
IServerSideSessionStore - Adding dynamic identity providers loaded from a database at runtime
- Implementing custom non-OIDC dynamic provider types (Google, SAML, etc.)
- Building a CIBA (Client Initiated Backchannel Authentication) flow
- Understanding edition requirements (Business vs Enterprise) for these features
Docs: https://docs.duendesoftware.com/identityserver/ui/server-side-sessions/
Server-Side Sessions
What Problem Do They Solve?
By default, ASP.NET Core stores all authentication session state in a self-contained cookie. This creates several challenges:
Server-side sessions store authentication state on the server, keeping only a session reference in the cookie.
Edition Requirements
Server-side sessions are part of the Duende IdentityServer Business and Enterprise Edition.
Enabling Server-Side Sessions
Important: This call must come after any custom IRefreshTokenService implementation registration. Order matters in the ASP.NET Core service provider.
By default, sessions are stored in-memory. For production, use Entity Framework Core or a custom store.
Using Entity Framework Core Store
The EF Core implementation is included in the operational store and supports the IServerSideSessionStore interface automatically.
Custom Session Store
Implement IServerSideSessionStore and register it:
Data Stored Server-Side
The session stores the serialized ASP.NET Core AuthenticationTicket (all claims + AuthenticationProperties.Items). The data is protected using ASP.NET Core's Data Protection API.
Queryable indices extracted from the session:
Configure the display name claim. Note: UserDisplayNameClaimType is unset (null) by default due to PII concerns. You must explicitly set it if you want display names stored in the session index:
Session Management with ISessionManagementService
Querying Sessions
Paging Through Results
Performance Note on Querying
When listing sessions, prefer GetSessionsAsync over QuerySessionsAsync. The QuerySessionsAsync method performs a full-text search and may be slower. Use QuerySessionsAsync only when advanced filtering is needed.
Terminating Sessions
Terminate sessions and optionally revoke tokens, consents, and send back-channel logout notifications:
Selective revocation (filtering by SessionId or ClientIds is also supported):
What Gets Cleaned Up
Internally, this uses IServerSideTicketStore, IPersistedGrantStore, and IBackChannelLogoutService.
Server-Side Session Custom Metadata
Store per-sign-in metadata (device name, auth method, region) in the AuthenticationTicket's AuthenticationProperties.Items. It stays inside IdentityServer and is NOT issued as claims.
Writing Metadata
With ASP.NET Identity, pass properties to SignInWithClaimsAsync:
If you use PasswordSignInAsync (which does not accept properties), override SignInWithClaimsAsync in a custom SignInManager to inject the metadata.
Reading Metadata
Limitation: custom metadata is not indexed by the built-in store — you cannot filter SessionQuery by it. Query by subject id, session id, or display name first, then inspect the tickets.
Inactivity Timeout
The Challenge
OpenID Connect does not natively provide distributed session management based on user inactivity. Multiple artifacts (cookies, refresh tokens, access tokens) have independent lifetimes controlled by different entities. Coordinating their expiration is non-trivial.
Design: Centralized Session Tracking
Server-side sessions at IdentityServer provide the central record for monitoring user activity:
- Activity signals: As the user's client uses refresh tokens, introspection, or userinfo, these protocol calls extend the server-side session automatically via an internal
ISessionCoordinationService(this is an implementation detail, not a public API for consumers). - Inactivity detection: When no activity occurs within the session timeout, the session expires and cleanup is triggered (back-channel logout, token revocation).
Configuration at IdentityServer
Three features must be enabled:
Note: ExpiredSessionsTriggerBackchannelLogout defaults to true, so step 3 is technically optional. The only setting you must explicitly enable is CoordinateClientLifetimesWithUserSession (step 2).
Alternatively, enable coordination per-client:
Client-Side Configuration
Critical: Configure access token lifetime to be shorter than the server-side session lifetime at IdentityServer, so that refresh token usage naturally keeps the session alive.
Session Expiration and Cleanup
When a session cookie expires without explicit logout, the server-side session record remains in the store. An automatic cleanup job periodically scans for and removes these expired records.
Expiration Configuration Options
All options are on options.ServerSideSessions:
Customizing the Cleanup Interval
Disabling Automatic Cleanup
Configuring Session Lifetime
The server-side session lifetime is inherited from the cookie authentication handler:
- Default (no ASP.NET Identity): Controlled by
options.Authentication.CookieLifetime(defaults to 10 hours) - With ASP.NET Core Identity: Controlled by
ConfigureApplicationCookie(options => options.ExpireTimeSpan = ...)(defaults to 14 days)
Session Renewal and Absolute Lifetime Cap
With server-side sessions the cookie expiration can extend beyond the configured lifetime: IdentityServer calls SignInAsync whenever the session's client list changes (e.g. the user signs into an additional client), re-issuing the cookie and resetting its timer. Without server-side sessions, the cookie expiration is set once at login.
To enforce an absolute cap, combine:
Client.UserSsoLifetime— forces interactive re-authentication after N seconds, regardless of cookie renewals.Client.AbsoluteRefreshTokenLifetimewithRefreshTokenExpiration = TokenExpiration.Absolute— caps refresh-token-driven session extension.
Dynamic Identity Providers
Edition Requirements
Dynamic identity providers are part of the Duende IdentityServer Enterprise Edition.
Problem Statement
Statically registering many authentication handlers via AddOpenIdConnect() has performance penalties in ASP.NET Core's DI system. It also requires application restart for configuration changes.
Solution
Dynamic providers are loaded from a store at runtime, avoiding DI overhead and enabling live configuration changes.
Store Options
Adding a Dynamic OIDC Provider (In-Memory)
Adding a Dynamic OIDC Provider (Entity Framework)
Caching Dynamic Providers
By default, dynamic provider configuration is loaded from the store on every request. Enable caching:
- EF stores: Use
AddConfigurationStoreCache() - Custom stores: Use
AddIdentityProviderStoreCache<T>()
Listing Dynamic Providers on the Login Page
Merge static and dynamic providers:
Callback Path Convention
Dynamic providers follow the convention ~/federation/{scheme}/{suffix}:
Customize the prefix:
Custom (Non-OIDC) Dynamic Providers
To add providers like Google or SAML:
Step 1: Create a custom IdentityProvider type:
Step 2: Register the handler mapping:
Step 3: Configure options mapping:
Register it:
Customizing OpenIdConnectOptions for Dynamic Providers
Implement IConfigureNamedOptions<OpenIdConnectOptions> for per-scheme customization:
Register: builder.Services.ConfigureOptions<CustomConfig>();
For customizations that need access to the OidcProvider data (e.g., the Properties bag), derive from ConfigureAuthenticationOptions<OpenIdConnectOptions, OidcProvider> instead.
CIBA (Client Initiated Backchannel Authentication)
Edition Requirements
CIBA is part of the Duende IdentityServer Enterprise Edition.
What Is CIBA?
CIBA allows a user to authenticate on a different device than the one running the client application. Example: a user at a bank kiosk authenticates via their mobile phone.
CIBA Flow
- Client sends a backchannel authentication request to IdentityServer's
/connect/cibaendpoint - IdentityServer validates the request and identifies the user via
IBackchannelAuthenticationUserValidator(you must implement this) - IdentityServer creates a pending login request in the
IBackchannelAuthenticationRequestStore - IdentityServer notifies the user via
IBackchannelAuthenticationUserNotificationService(you must implement this — e.g., push notification, email, SMS) - User reviews and approves/denies the request; your UI calls
IBackchannelAuthenticationInteractionService.CompleteLoginRequestAsync - Client polls the token endpoint and receives tokens (or an error if denied/timed out)
Required Implementations
Client Configuration
The client calls the backchannel authentication endpoint, receives an auth_req_id, then polls the token endpoint (poll mode). It must handle authorization_pending, slow_down, expiration, and user denial.
User Notification Service
Register it:
The built-in no-op implementation just logs a URL for testing — replace it in production. Treat InternalId as sensitive; never surface it in the notification. The user compares the BindingMessage shown on both the consumption device and their authentication device.
Approval UI
Use IBackchannelAuthenticationInteractionService:
The server rejects any scope not present in the original CIBA request. Raise ConsentGrantedEvent / ConsentDeniedEvent for audit.
IdentityServer supports the poll mode for clients to obtain results.
Common Anti-Patterns
-
❌ Using in-memory session store in production — sessions are lost on restart
-
✅ Use Entity Framework Core or a custom durable store for production
-
❌ Registering hundreds of static authentication handlers via
AddOpenIdConnect() -
✅ Use dynamic identity providers for scalable provider management
-
❌ Assuming inactivity timeout works automatically without enabling
CoordinateClientLifetimesWithUserSession -
✅ Explicitly enable coordination at the global or per-client level
-
❌ Using
QuerySessionsAsyncfor simple session listing -
✅ Prefer
GetSessionsAsync— it is faster; useQuerySessionsAsynconly for advanced filtering -
❌ Forgetting to implement
IBackchannelAuthenticationUserValidatorandIBackchannelAuthenticationUserNotificationServicefor CIBA -
✅ Both interfaces must be implemented and registered in DI — IdentityServer does not provide defaults
Common Pitfalls
-
Registration order matters:
AddServerSideSessions()must be called after any customIRefreshTokenServiceregistration. -
Data Protection dependency: Server-side session data is protected using ASP.NET Core Data Protection. Ensure Data Protection keys are persisted and shared across load-balanced instances.
-
Session expiration vs cookie expiration: The server-side session has its own lifetime. When a session expires server-side, the user's cookie becomes invalid even if the cookie itself hasn't expired.
-
Dynamic provider store is read-only:
IIdentityProviderStoreonly has query methods. To add/update/delete providers, useConfigurationDbContextdirectly (for EF) or your own mechanism (for custom stores). -
CIBA requires Enterprise Edition: Attempting to use CIBA features without the Enterprise Edition license will fail at runtime.
-
Access token lifetime must be shorter than session timeout: For inactivity timeout to work, refresh token usage must happen regularly enough to signal activity. If the access token lives longer than the session timeout, the client won't refresh in time.


