redb.Identity, a custom OAuth 2.1 / OpenID Connect provider developed on .NET, tackles the problem of stolen access tokens with three concrete layers of defense rather than relying on declarative statements. Bearer tokens work for whoever possesses them, allowing an attacker who extracts a token via XSS, a compromised npm package, browser extension, or localStorage to operate from any machine until expiration, typically fifteen minutes to one hour.
Origin of Token Theft
Token theft commonly stems from XSS in the application itself, XSS through a dependency where a compromised npm package gains the same DOM and network access as first-party code, malicious browser extensions, or direct reads from localStorage. All four vectors allow the attacker to carry away the token itself, eliminating any need for the victim’s browser, network, or session.
Layer One: Preventing Theft with BFF
Most identity-server admin consoles, including Keycloak and WSO2 Identity Server, run as public OAuth clients in the browser and therefore store administrator access tokens in JavaScript-accessible memory. redb.Identity.Web instead uses Blazor Server with cookie authentication. Tokens are stored on the authentication ticket via AuthenticationTokenExtensions.StoreTokens and retrieved server-side with GetTokenAsync. All intermediate states such as MFA challenges and consent screens are likewise held in HttpOnly, SameSite=Lax, Secure cookies protected by Data Protection. Because markup renders on the server and only a SignalR diff reaches the browser, no JavaScript bundle holds application state.
BFF converts a silent compromise from another country into an active session inside the victim’s browser only while the page remains open. It therefore trades one detection signal (impossible travel) for a smaller blast radius, but still requires behavioral monitoring.
Layer Two: Rendering Stolen Tokens Useless with DPoP
DPoP (RFC 9449) binds tokens to a private key generated inside the client. At issuance the server records the key thumbprint in the cnf.jkt claim. Every subsequent request must include a fresh, signed proof JWT tied to the exact HTTP method and URL. A token stolen without its private key is rejected because the server demands a valid proof. The implementation includes stateless HMAC nonces, a jti replay cache per key, and an allow-list of asymmetric algorithms only. A separate package, redb.Identity.Resource.Dpop, lets resource APIs perform the same validation.
Layer Three: Rapid Revocation
Even strong binding requires fast revocation when a user departs, a device is lost, or an incident is confirmed. redb.Identity supports RFC 7009 token revocation, refresh-token rotation, idle-timeout sessions updated on real activity, and backchannel logout in both push and pull modes. The pull feed of revoked session identifiers allows any recovering replica to query “/revoked-sids/since?cursor=…” and catch up without missing events. BFF validation itself checks this list on every cookie request so that a global sign-out immediately terminates live sessions across all replicas.
Supporting Controls
Password history, TOTP step locking to prevent replay within the acceptance window, server-side hashed one-time codes for SMS and email, atomic recovery-code consumption, constant-time comparisons via CryptographicOperations.FixedTimeEquals, and three-tier rate limiting (IP, client_id token bucket, and IP-plus-user) are all implemented. Proxy headers are sanitized before reaching rate limiters or block counters, and idempotency caches sit after authorization checks.


