diff --git a/server/app.py b/server/app.py index c1c4e6b..f45b7a1 100644 --- a/server/app.py +++ b/server/app.py @@ -24,6 +24,7 @@ from fastapi.staticfiles import StaticFiles from pydantic import BaseModel, ConfigDict, Field from sqlalchemy import select, delete, func from sqlalchemy.orm import Session +from starlette.middleware.sessions import SessionMiddleware from .db import Base, engine, get_db from . import models, auth, notify, assets_db, okta_auth @@ -58,6 +59,22 @@ if _origins: allow_methods=["*"], allow_headers=["*"], expose_headers=["X-Total-Count"], ) +# Authlib's Okta client needs request.session to carry the OIDC state/nonce (and, +# below, our own post-login redirect target) across the round trip to Okta and +# back — it raises an AssertionError without this. Bug found in T10.2 (those +# routes never crashed in testing because every prior check mocked +# authorize_redirect/authorize_access_token directly, bypassing Authlib's real +# implementation); fixed here rather than reworking already-shipped T10.2 code. +# +# This is NOT the app's session cookie — wp_session (auth.py) still carries the +# actual signed-in identity, unchanged. This cookie holds nothing but ephemeral, +# per-attempt OAuth state, so it gets a short lifetime and a plain secret reuse +# (auth.SECRET_KEY) rather than its own required config knob. +app.add_middleware( + SessionMiddleware, secret_key=auth.SECRET_KEY, session_cookie="wp_oauth_state", + same_site="lax", https_only=False, max_age=600, +) + # ── Authentication gate ──────────────────────────────────────────────────────── # Every /api/ data route requires a valid session cookie. Login, health, and the diff --git a/server/requirements.txt b/server/requirements.txt index adb8568..8bcd040 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -21,3 +21,5 @@ PyJWT==2.13.0 # signed session tokens starlette==1.3.1 # pinned transitive (cookie / CORS handling — security-relevant) Authlib==1.7.2 # Okta OIDC authorization-code flow (T10.1, wave 10 / D15) httpx==0.28.1 # Authlib's OIDC client needs an HTTP client; explicit, not transitive +itsdangerous==2.2.0 # signs the OAuth-state cookie SessionMiddleware sets — required by + # Authlib's authorize_redirect/authorize_access_token, not optional