Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Dbsc Bundle Laravel Package

spomky-labs/dbsc-bundle

Symfony bundle for implementing Device Bound Session Credentials (DBSC), binding sessions to device-held keys to reduce cookie theft and session hijacking. Provides configuration, endpoints, and integration with Symfony security for stronger, phishing-resistant session protection.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Know what you're installing. spomky-labs/dbsc-bundle is a Symfony bundle implementing the W3C Device Bound Session Credentials (DBSC) spec — the browser-side mechanism that binds a long-lived session cookie to a TPM-backed key pair. If you're in a Laravel app, you won't composer require and register a service provider: treat it as a reference implementation and port the two endpoints (registration + refresh) and the header emission logic into middleware/controllers. The spec surface you must reproduce is small: emit Secure-Session-Registration, verify the proof JWS, issue/rotate the bound cookie.
  • Install & enable (Symfony path): composer require spomky-labs/dbsc-bundle, enable the bundle, then configure per firewall. Look first at the bundle's configuration reference for the per-firewall keys, since almost every feature in 0.3.0 is a firewall option.
  • First use case: protect a "remember me"-style long-lived session on a sensitive area (admin, account settings, payments) rather than the whole app. Configure the registration trigger (e.g. on login), a session_lifetime, and the cookie attributes; leave include_site off initially.
  • Turn on the profiler early (dev/staging). The 0.3.0 panel is the fastest way to understand the flow: it shows the full per-firewall config plus, for any request hitting a DBSC endpoint, the decoded proof JWS, the issued challenge, the rotated/cleared cookie and the outcome. That's your first stop when a registration silently fails.

Implementation Patterns

  • The authorization round-trip (new in 0.3.0). DeviceBoundSessionBadge::setAuthorization($value) adds an authorization parameter to Secure-Session-Registration, binds it into the challenge, and verifies the registration proof echoes it back unchanged. Use it to carry a value only your backend can vouch for — a signed tenant/user claim, an enrollment nonce, a "this registration came from step 2 of our MFA flow" marker. Pattern: mint a short-lived signed value, stash the expected value keyed by challenge server-side, compare on verification. Treat the echoed value as evidence the browser saw your value, not as authentication by itself.
  • Scope: origin vs site. Default is origin-scoped. Set include_site: true when your frontend and refresh endpoint live on sibling subdomains (app.example.com refreshing against auth.example.com). Only widen once you've confirmed every host in scope is equally trusted — a site-scoped cookie is shared wider.
  • Cross-origin frontends. allowed_refresh_initiators lists origins permitted to initiate a refresh; it's only emitted in the session config document when non-empty. Add every frontend origin that will call the refresh endpoint. Pair this with proper CORS (Access-Control-Allow-Credentials: true and an explicit origin, not *).
  • Handle the three refresh outcomes explicitly. 0.3.0 now distinguishes them, and your client JS must too:
    • 200 + continue: true — session refreshed, cookie rotated; keep going.
    • 200 + continue: false — session ended gracefully (lifetime elapsed). Clear local state, prompt re-login/re-registration. Do not retry.
    • 401 — unknown session. Same user-facing outcome (re-register), but don't let your logs/alerts treat these as equivalent to expiry.
  • Exempt the endpoints from your normal guard. Both endpoints are hit by headless fetch() from the browser, often before your session is established. They must be reachable without CSRF tokens and must run before any auth middleware that would reject a request carrying only the bound cookie. In Laravel: put them outside the web group's CSRF, with the DBSC verification as the only guard.
  • Watch Secure-Session-Skipped. The bundle now logs at notice level when a browser reports it skipped DBSC (refresh endpoint unreachable, server error, quota exceeded). Ship a log-based alert on this — it's effectively an uptime/instrumentation signal for your refresh endpoint that you'd otherwise never see, because the browser fails silently.
  • Pick your store deliberately. In-memory stores are fine for dev; multi-node production needs a persistent store, or bindings vanish on deploy and every user re-registers. This is a per-firewall setting and the profiler shows you which is active.

Gotchas and Tips

  • Cookie names: prefixes are legal. The 0.2.0 note claiming browsers reject __Host-/__Secure- was wrong and is corrected in 0.3.0. A __Host- name needs secure, path=/ and no domain attribute — the bundle defaults satisfy all three. The default remains unprefixed dbsc_session for compatibility; switch to __Host-dbsc_session if you want the extra hardening, but don't set domain at the same time or the browser drops the cookie silently.
  • aud checking is conditional. Proof verification now checks aud against the endpoint URL when the browser includes it, and tolerates its absence. So aud is defence-in-depth, not a guarantee — never build a security decision on the assumption that a proof is endpoint-bound.
  • Key location changed. The embedded public key is now read from the jwk protected header first, falling back to the legacy key payload claim. If you have custom verification or tests that assert on the proof structure, update them; if you're mid-rollout, keep the fallback until your browser fleet has moved.
  • Expired ≠ unknown. An expired session now returns 200/continue: false with a cleared cookie; unknown sessions still 401 (deliberately, to avoid confirming existence). Your metrics should separate these — a spike in 401s is a bug or an attack; a spike in graceful expiries is just user inactivity.
  • Debugging a "nothing happens" registration: confirm the response actually carries Secure-Session-Registration, then open the profiler panel. If the panel shows no exchange, the request never hit a DBSC endpoint — usually the fetch was blocked by CORS, CSRF, or a redirect. The decoded JWS view tells you instantly whether the browser is minting proofs at all (i.e. whether the user's browser supports DBSC).
  • Fallback is mandatory. DBSC support is browser-dependent and Chrome-led. Always keep a conventional long-lived-session path for users whose browser skips or doesn't understand the header; the Secure-Session-Skipped signal is your hint that some users are on that path.
  • Extension points: per-firewall config is where you customise; the challenge/proof verification logic is the piece most likely to need replacing if you're porting to Laravel or adding custom claims — fork that service rather than reimplementing the header format by hand.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package