Skip to content

refactor(cookies): replace the cookie jar with the store gRPC already uses - #15

Merged
lakhansamani merged 1 commit into
mainfrom
refactor/mfa-session-header-echo
Aug 17, 2026
Merged

refactor(cookies): replace the cookie jar with the store gRPC already uses#15
lakhansamani merged 1 commit into
mainfrom
refactor/mfa-session-header-echo

Conversation

@lakhansamani

Copy link
Copy Markdown
Contributor

Follow-up to #11. That PR added a cookie jar; this removes the need for one.

The insight

The gRPC transport in this SDK has always carried cookies as a plain dict[str, str] — captured from set-cookie metadata, replayed as one cookie entry. No jar, no policy. Meanwhile the HTTP transports used http.cookiejar, which implements a user agent: domain matching, public-suffix rules, a policy layer.

An SDK talks to exactly one origin, fixed at construction. Every one of those rules is either inapplicable or a source of breakage — and it supplied both:

HTTP now uses the same store gRPC already had. One implementation, one cookie store per client — so a client that switches protocol keeps its session.

What the jar was doing that still gets done

  • Secure is still honoured (RFC 6265 §4.1.2.5): not stored when the client's own origin is plain http and not loopback. Loopback is excepted because Secure Contexts makes it trustworthy — the same call Chrome, Firefox and Go's stdlib jar make, and RFC 6265bis §5.6 explicitly delegates it to the user agent.
  • Max-Age<=0 still deletes, so logout / DeleteMfaSession clear the handle instead of leaving a dead one replayed forever.
  • Origin scoping is now structural, not policy: every RequestSpec is built from config.authorizer_url, so no code path attaches the store to another host. Pinned by a test that fails if one appears.

The cookie-injection hole #11's shim introduced cannot recur — with one origin there is no second host to accept a cookie from.

Verification

  • ruff, mypy, pytest clean on 3.9, 3.10, 3.11, 3.12, 3.13
  • a real MFA flow against a running server: signup withholds → skip_mfa_setup redeems → token issued
  • the 56-test live integration suite, green
  • the three adversarial tests rewritten for the new design (cross-origin, Secure, deletion) rather than deleted

Server-side contract documented in authorizerdev/docs#93.

… uses

The SDK was emulating a user agent to solve a problem that needs two
lines. http.cookiejar implements domain matching, public-suffix rules and
a policy layer; an SDK talks to exactly ONE origin, fixed at
construction, so every one of those rules is either inapplicable or a
source of breakage. It supplied both: Secure cookies dropped on
http://localhost, dotless-domain mismatch, and a set_ok-vs-set_cookie
layering that made the first fix silently do nothing on Python 3.9/3.10.

The replacement is not new — the gRPC transport has always carried
cookies as a plain name->value dict, captured from `set-cookie` metadata
and replayed as one `cookie` entry. HTTP now uses that same store, so the
two transports share one implementation and one cookie store per client
(a client that switches protocol keeps its session).

What the jar was doing that still has to be done, now done directly:

- Secure is still honoured (RFC 6265 4.1.2.5): a Secure cookie is not
  stored when the client's own origin is plain http and not loopback.
  Loopback is excepted because Secure Contexts makes it trustworthy —
  the same call Chrome, Firefox and Go's stdlib jar make, and RFC 6265bis
  5.6 explicitly delegates to the user agent.
- Max-Age<=0 still deletes, so logout/DeleteMfaSession clear the handle
  rather than leaving a dead one replayed forever.
- Origin scoping is now structural rather than policy: every RequestSpec
  is built from config.authorizer_url, so there is no path that attaches
  the store to another host. Pinned by a test that fails if one appears.

The cookie-injection hole the jar shim introduced cannot recur: with one
origin there is no second host to accept a cookie from.

Verified: ruff, mypy and pytest clean on 3.9-3.13, and a real MFA flow
(signup withholds -> skip_mfa_setup redeems) plus the 56-test live
integration suite against a running server.
@lakhansamani
lakhansamani merged commit 83ca9b0 into main Aug 17, 2026
6 checks passed
@lakhansamani
lakhansamani deleted the refactor/mfa-session-header-echo branch August 17, 2026 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant