Skip to content

API security hardening#100

Merged
shaddi merged 2 commits into
mainfrom
api-security-hardening
Jun 10, 2026
Merged

API security hardening#100
shaddi merged 2 commits into
mainfrom
api-security-hardening

Conversation

@shaddi

@shaddi shaddi commented Jun 10, 2026

Copy link
Copy Markdown
Member

Letting Claude take the wheel for a security review. Seemed like reasonable changes and the site seems to still work.

shaddi added 2 commits June 10, 2026 09:33
Token handling:
- Session JWTs and email verification/reset tokens share one signing key
  with no type discriminator, so a 15-minute email token — which users
  forward to each other by design in the join-organization flow — worked
  as a full login cookie. Both token families now carry and require their
  own audience (bdk-app / bdk-email); cross-type replay is rejected and
  pinned by tests. Outstanding sessions are invalidated once (re-login).
- Email-token expiry was minted from naive local time, which PyJWT reads
  as UTC — on any host west of UTC the tokens were born expired. Expiry
  is now timezone-aware UTC.
- Disabled (or deleted) users now lose API access immediately: every
  authenticated request re-checks the user row instead of letting the
  cookie ride out its 7-day expiry.
- The session cookie is HttpOnly in dev as well as prod (no frontend code
  reads it), and production refuses to boot with a JWT secret shorter
  than 32 bytes.

Input validation:
- update_profile coerced missing JSON fields through str(), so posting {}
  renamed the organization to "None" and changed the account email to
  "None" (unverifying it). Fields are now applied only when present, email
  changes are format- and uniqueness-checked, and internal error strings
  are no longer echoed to the client.
- register validates the email format and rejects empty passwords.
The session rode in a cookie with SameSite=Lax as the only cross-site
protection. Login/register now set flask-jwt-extended's cookie pair — the
HttpOnly session token plus a JS-readable csrf_access_token — and every
mutating /api request must echo the latter in an X-CSRF-TOKEN header
(double-submit), enforced server-side.

The frontend covers all call sites with one global fetch wrapper installed
at app startup; tests use an equivalent test-client. Logout and the admin
panel's impersonation start/stop use the same cookie helpers so the csrf
cookie stays in lockstep with the session.

Existing sessions are invalidated once (tokens minted before this change
lack the csrf claim); users just log in again.
@shaddi shaddi merged commit 70ad7d5 into main Jun 10, 2026
3 checks passed
@shaddi shaddi deleted the api-security-hardening branch June 10, 2026 13:43
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