A detailed production postmortem on implementing Device Bound Session Credentials (DBSC) at Report URI, covering the real-world bugs and edge cases the specification doesn't warn you about. Key lessons include: the refresh flow is two-phase (403 then 200 is normal), DBSC state must not live in a shared session blob due to race conditions, cookie and challenge values must rotate on every refresh, redirecting DBSC endpoints causes permanent browser deadlocks (use 401 instead), SSO/SAML logins silently fail to bind due to SameSite cookie restrictions, and challenge mismatches after signature verification are benign and should trigger a retry rather than session termination. The open-source PHP library (report-uri/dbsc-php) encodes all these fixes.

18m read timeFrom scotthelme.co.uk
Post cover image
Table of contents
What DBSC actually doesThe spec reads backwardsDon't put the state in your session storeEverything rotates, or the browser terminates youThe bug you cannot see on localhostNever redirect a DBSC endpointA challenge mismatch is not an attackYour SSO logins probably aren't binding at allFail open at the edges, fail closed at the gateSome decisions I'd defendWhere it stands

Questions this post answers

Why does my DBSC refresh endpoint return 403 and is that an error?

A 403 on a DBSC refresh endpoint is the normal, healthy first step — not an error. The browser POSTs to the refresh endpoint with no challenge, you respond 403 with a Secure-Session-Challenge header, the browser signs it and POSTs again, and only then do you respond 200 with a fresh cookie. The two-round-trip 403→200 pattern is the intended refresh flow. Developers wiring up DBSC for the first time find the full flow documented alongside real production gotchas on daily.dev.

Why are users getting randomly logged out after implementing DBSC cookie rotation?

Random logouts after DBSC cookie rotation are caused by a race between the refresh round-trip and concurrent requests. A request dispatched mid-refresh carries the pre-rotation cookie value, which the enforcement gate then rejects as a mismatch. The fix is a single-generation overlap window: accept the immediately-previous cookie value, but only until it would have naturally expired in the browser — no arbitrary grace constant needed. Teams shipping DBSC to real users track race condition patterns like this one on daily.dev.

What happens if I redirect my DBSC refresh endpoint to the login page when the session expires?

Redirecting a DBSC refresh endpoint (e.g. 302 to /login) causes Chrome to deadlock the deferred navigation permanently — the tab goes blank and never recovers, with no timeout. A 401 response instead tells Chrome the session is over, tears down the DBSC session cleanly, and releases the deferred navigation to proceed to the login page. DBSC endpoints must never emit redirects. Developers hardening session expiry flows for DBSC find edge cases like this covered on daily.dev.

960 Impressions