From 4b84b66f457459b4988998e674f8fc2d2e5e5d85 Mon Sep 17 00:00:00 2001 From: allen0099 Date: Sun, 27 Sep 2026 11:06:31 +0000 Subject: [PATCH] docs(session): show a login that sets session.user Writing request.session["user_id"] is application data and does not satisfy require_user_session / AuthenticatedSession. Document the rotate, assign session.user, update_session pattern in the session guide (EN and zh-TW) and the rotate_session_id docstring, and point to examples/session_login.py and the planned login() helper (#293). --- docs/SESSION.md | 38 ++++++++++++++++++++++++++ fastapi_cachex/session/dependencies.py | 5 ++++ i18n/zh-TW/docs/SESSION.md | 27 ++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/docs/SESSION.md b/docs/SESSION.md index ab0b011..e1836c0 100644 --- a/docs/SESSION.md +++ b/docs/SESSION.md @@ -176,6 +176,13 @@ async def account(session: AuthenticatedSession): `UserSessionDep` does not check for a user despite its name; it is an alias of `SessionDep` until 0.4.0, which is planned to make it require one. +`session.user` is set only by passing `user=` to `create_session()` or by assigning it and +saving the session. Keys written to `request.session` (`request.session["user_id"] = ...`) +are application data: the library does not treat them as a login, so `AuthenticatedSession` +still answers `401` for such a session. See +[Regenerate the Session ID After Login](#5-regenerate-the-session-id-after-login) for a login +that sets the user. + ### 3. Full Example (Redis Backend) ```python @@ -670,6 +677,37 @@ A handler that already holds the request's session object can call `get_optional_session` and skip the call when it is `None`; `SessionDep` answers `401` to a visitor who has no session yet. +The example above stores the user ID as application data, which `require_user_session` and +`AuthenticatedSession` do not recognise. To pass them, attach a `SessionUser` after the +rotation and save the session yourself: assigning `session.user` does not mark +`request.session` as modified, so the middleware would not save it. + +```python +from fastapi_cachex.session import SessionUser +from fastapi_cachex.session.dependencies import OptionalSession + + +@app.post("/login") +async def login(request: Request, session: OptionalSession): + ... # verify the credentials + user = SessionUser(user_id="123") + if session is not None: + await rotate_session_id(request) + session.user = user + await session_manager.update_session(session) + else: + # No session yet: create one with the user and deliver its token yourself, + # as in the login example in Basic Usage. + _, token = await session_manager.create_session(user=user) + ... + return {"ok": True} +``` + +The complete version, including the cookie for a new visitor, is +[`examples/session_login.py`](https://github.com/allen0099/FastAPI-CacheX/blob/master/examples/session_login.py). +A `login()` helper that does all of this is planned +([#293](https://github.com/allen0099/FastAPI-CacheX/issues/293)). + Outside a middleware, load the session with the same bindings the middleware would pass, and hand the returned token to the client yourself: diff --git a/fastapi_cachex/session/dependencies.py b/fastapi_cachex/session/dependencies.py index 352343f..2649719 100644 --- a/fastapi_cachex/session/dependencies.py +++ b/fastapi_cachex/session/dependencies.py @@ -176,6 +176,11 @@ async def login(request: Request): return {"ok": True} ``` + ``request.session["user_id"]`` is application data. For + ``require_user_session`` / ``AuthenticatedSession`` to accept the + session, set ``session.user`` after the rotation and save it with + ``SessionManager.update_session()``; see the session guide. + Args: request: FastAPI request object diff --git a/i18n/zh-TW/docs/SESSION.md b/i18n/zh-TW/docs/SESSION.md index ef89787..51e5899 100644 --- a/i18n/zh-TW/docs/SESSION.md +++ b/i18n/zh-TW/docs/SESSION.md @@ -154,6 +154,8 @@ async def account(session: AuthenticatedSession): `UserSessionDep` 雖然名稱如此,卻不會檢查使用者;在 0.4.0 之前它是 `SessionDep` 的別名,0.4.0 預計改為要求使用者。 +只有在 `create_session()` 傳入 `user=`,或指定 `session.user` 後儲存 Session,才會設定 `session.user`。寫入 `request.session` 的鍵(`request.session["user_id"] = ...`)是應用程式資料:函式庫不會把它視為登入,因此這種 Session 仍會讓 `AuthenticatedSession` 回應 `401`。會設定使用者的登入方式,請見[登入後重新產生 Session ID](#5-regenerate-the-session-id-after-login)。 + ### 3. 完整範例(Redis 後端) {#3-full-example-redis-backend} ```python @@ -544,6 +546,31 @@ async def login(request: Request): 已經取得請求 Session 物件的 handler,也可以直接呼叫 `await manager.regenerate_session_id(session)`,效果相同。請從 `get_optional_session` 取得 Session,並在它為 `None` 時略過呼叫;`SessionDep` 會對還沒有 Session 的訪客回應 `401`。 +上面的範例把使用者 ID 存成應用程式資料,`require_user_session` 與 `AuthenticatedSession` 不會認得它。要通過它們的檢查,請在換 ID 之後附加 `SessionUser`,並自行儲存 Session:指定 `session.user` 不會把 `request.session` 標記為已修改,因此中介軟體不會儲存它。 + +```python +from fastapi_cachex.session import SessionUser +from fastapi_cachex.session.dependencies import OptionalSession + + +@app.post("/login") +async def login(request: Request, session: OptionalSession): + ... # 驗證帳號密碼 + user = SessionUser(user_id="123") + if session is not None: + await rotate_session_id(request) + session.user = user + await session_manager.update_session(session) + else: + # 還沒有 Session:建立帶有使用者的 Session,並自行交付其權杖, + # 做法同基本用法中的登入範例。 + _, token = await session_manager.create_session(user=user) + ... + return {"ok": True} +``` + +完整版本(包含為新訪客設定 Cookie)請見 [`examples/session_login.py`](https://github.com/allen0099/FastAPI-CacheX/blob/master/examples/session_login.py)。處理上述所有步驟的 `login()` 輔助函式已在規劃中([#293](https://github.com/allen0099/FastAPI-CacheX/issues/293))。 + 在中介軟體之外,請以中介軟體會傳入的相同綁定值載入 Session,並自行將回傳的權杖交給用戶端: ```python