This project demonstrates an implementation of the Device Bound Session Credentials (DBSC) protocol using TypeScript + Cloudflare Workers.
- Start the secure development server:
npm install npx wrangler dev --local-protocol https --port 8787
- Open https://localhost:8787 in Chrome.
⚠️ Important: Since we use a self-signed certificate for local HTTPS, Chrome will show a "Your connection is not private" warning. Click Advanced and then Proceed to localhost (unsafe). DBSC requires HTTPS to function correctly.
npm run deployDBSC (Device Bound Session Credentials) is a technology that transforms a session from "simple cookie presentation" into a process requiring proof of possession of a cryptographic secret that cannot be stolen.
sequenceDiagram
participant Browser
participant TPM/SecureEnclave
participant Server
Note over Browser,Server: 1. Registration (Login & Binding)
Browser->>Server: Login (User/Pass)
Server-->>Browser: 200 OK + auth_cookie (Long-lived) + Secure-Session-Registration header
Browser->>TPM/SecureEnclave: Generate P-256 Key Pair
TPM/SecureEnclave-->>Browser: Public Key
Browser->>Server: POST /StartSession (Secure-Session-Response: dbsc+jwt with JWK)
Server-->>Browser: 200 OK + sl_auth_cookie (Short-lived, 30s) + dbsc_state (Signed PK)
Note over Browser,Server: 2. Refresh (Challenge-Response)
Browser->>Server: POST /RefreshSession (no proof)
Server-->>Browser: 403 + Secure-Session-Challenge header
Browser->>TPM/SecureEnclave: Sign Challenge
TPM/SecureEnclave-->>Browser: Signature
Browser->>Server: POST /RefreshSession + Secure-Session-Response (dbsc+jwt proof)
Server-->>Browser: 200 OK + New sl_auth_cookie
- Secret key never leaves the "vault": The private key is generated and stored in a hardware chip (TPM, Secure Enclave). It cannot be copied even if the device is infected with malware.
- Short-lived cookies: DBSC uses cookies with a very short lifespan (e.g., 30 seconds or 5 minutes). If an attacker steals such a cookie, it will expire before they can reasonably use it.
- Magic of Renewal (Challenge-Response): To obtain a new cookie, the browser must sign a random "challenge" from the server using its private key. A hacker cannot replicate this without access to the hardware key. Importantly, Chrome handles this automatically at the network level. If the browser realizes the
sl_auth_cookieis expired (or receives a 403 response indicating it needs a refresh), Chrome pauses the request, performs the challenge-response cycle in the background to get a new cookie, and then automatically resumes the actual request. - Origin Binding: The website's domain is always "baked into" the signature. This protects against Relay attacks (where a hacker tries to use your key to log in to another site).
A key feature of native DBSC is that the background refresh requests are completely invisible to the client-side JavaScript.
As shown in the screenshot below, the browser's DevTools Network tab (bottom left) only shows the regular frontend requests (like /api/status). However, the server logs (right) clearly show the POST /RefreshSession requests happening in the background.
This proves that:
- The frontend application is completely unaware of the refresh cycle; it's handled entirely by the browser's internal network stack.
- The responsibility for session security is shifted away from the application code (which can be vulnerable to XSS) and directly into the browser and the OS's hardware security module.
A key architectural security concept in DBSC is Graceful Degradation (or sessions with varying levels of trust).
In this implementation, there are two distinct cookies:
auth_cookie(Long-lived, e.g., 30 days): Proves that at some point, the user successfully authenticated.- Grants: Read-only actions (viewing public articles, showing the user's name in the header, browsing items).
sl_auth_cookie(Short-lived DBSC cookie, e.g., 30 seconds): Proves that the user is currently at the device that performed the original authentication, backed by a cryptographic hardware key.- Grants: High-security actions (transferring money, changing passwords, viewing sensitive data).
Why not just log the user out entirely when sl_auth_cookie expires?
If the short-lived cookie expires (e.g., due to a brief loss of internet connectivity, or the laptop going to sleep), the user would be abruptly logged out and forced to enter their password again. This creates a terrible User Experience (UX). By keeping the user conceptually in the system, we shift to a state where the user is authenticated, but not authorized for high-privilege actions (dbscActive: false). The application remains uninterrupted for normal browsing, while strictly enforcing security only when critical actions are requested.
If an attacker steals a user's cookies (e.g., via malware or XSS), their window of opportunity is minuscule.
- The Theft: The attacker steals
auth_cookie,dbsc_state, andsl_auth_cookie. - The brief window: For up to 30 seconds, the attacker can impersonate the user fully (while
sl_auth_cookieis alive). - The Block: Once the 30 seconds elapse,
sl_auth_cookieexpires. The attacker's high-privilege access is revoked. - The Failed Refresh: The attacker intercepts the 403 response and the
Secure-Session-Challengeheader, but fails to generate the signedSecure-Session-Response, because the necessary private key is locked inside the victim's TPM/Secure Enclave. The attacker is permanently locked out of sensitive actions.
You can try this yourself using the hacker-demo.sh script included in this repository!
- Secret from environment: HMAC signing key is loaded from
SECRET_KEYenv variable (Cloudflare Secret). Hardcoded fallback is used only in local dev. - Algorithm whitelist: Only
ES256andRS256are accepted in DBSC JWTs. Prevents algorithm substitution attacks (alg: none,HS256confusion). - Dynamic Secure flag: Cookies are set with
Secureflag automatically when served over HTTPS. - One-time challenges: Registration and refresh challenges are deleted after use to prevent replay attacks.
- HttpOnly cookies: All session cookies are
HttpOnly— not accessible to JavaScript. - SameSite=Lax: Prevents CSRF on POST endpoints.
- Set
SECRET_KEYviawrangler secret put SECRET_KEY - Deploy behind HTTPS (Cloudflare provides this automatically)

