AuthServer
Refresh and Logout
Rotate refresh tokens, switch organizations, and revoke sessions.
Refresh uses the old refresh token. SqlOS returns a new pair. Idle timeout extends.
var tokens = await authService.RefreshAsync(
new SqlOSRefreshRequest(refreshToken, OrganizationId: null), ct);OrganizationId: null means "keep the session's stored organization," not "skip organization checks." SqlOS requires the stored organization and membership to remain active before rotating the token. It also requires the user to remain active. The OAuth token endpoint reports lifecycle rejection as a generic invalid_grant; the detailed reason is available only in the audit log.
Browser backend-for-frontend:
var refreshToken = await tokenVault.GetRefreshTokenAsync(sessionId, ct);
var tokens = await authService.RefreshAsync(
new SqlOSRefreshRequest(refreshToken, OrganizationId: null), ct);
await tokenVault.ReplaceTokensAsync(sessionId, tokens, ct);Keep refresh tokens out of browser JavaScript. Store them in a server-side session or, for native and desktop apps, the platform credential vault. The browser should receive only your application's encrypted Secure, HttpOnly, SameSite session cookie.
Switch organizations without re-authenticating by passing a different organizationId:
var tokens = await authService.RefreshAsync(
new SqlOSRefreshRequest(refreshToken, organizationId: "org_newOrgId"), ct);Refresh tokens are single use. By default, SqlOS allows a 30-second retry grace period for the immediately previous token so a network retry does not destroy a healthy session. A retry inside that window receives the cached access token plus a fresh sibling refresh token.
Reuse outside the grace window revokes the entire token family and session. This protects against token theft: if an attacker captures and consumes a refresh token, a later replay shuts down the family instead of minting another usable branch. Set the initial default with options.AuthServer.RefreshTokenGraceWindowSeconds = 0 if your threat model values strict replay rejection over network-retry tolerance. A persisted dashboard or SqlOSSettingsService value overrides that startup default; see Security Settings.
Concurrent refreshes inside the configured grace window can receive the cached token produced by the winning rotation. Before returning that cached result, SqlOS rechecks session deadlines, client access, the user lifecycle, and the exact organization represented by the cached token.
Revoke a session by refresh token:
await authService.LogoutAsync(refreshToken: "rt_...", sessionId: null, ct);By session ID:
await authService.LogoutAsync(refreshToken: null, sessionId: "ses_...", ct);Revoke all sessions for a user:
await authService.LogoutAllAsync(userId, ct);Logout-all invalidates hosted AuthPage sessions as well as OAuth sessions and refresh-token families. Password reset has the same all-session invalidation behavior. Organization deactivation and the SSO organization-session revocation action invalidate the corresponding organization-bound AuthPage sessions.
Browser backend-for-frontend:
app.MapPost("/logout", async (
HttpContext httpContext,
SqlOSAuthService authService,
IApplicationTokenVault tokenVault,
CancellationToken ct) =>
{
var sessionId = httpContext.User.FindFirst("app_session_id")?.Value;
var refreshToken = sessionId is null
? null
: await tokenVault.GetRefreshTokenAsync(sessionId, ct);
if (refreshToken is not null)
await authService.LogoutAsync(refreshToken, sessionId: null, ct);
if (sessionId is not null)
await tokenVault.DeleteAsync(sessionId, ct);
await httpContext.SignOutAsync();
return Results.NoContent();
}).RequireAuthorization();Protect a cookie-authenticated logout endpoint against CSRF using an antiforgery token or a strict same-origin policy. Clear the local application session even if remote revocation times out; the ASP.NET Core example demonstrates that finally-style cleanup.