---
url: /docs/auth/sessions.md
---
# 会话与访问控制

## 单一会话来源

会话查询在 `apps/app/lib/queries/session.ts`。页面组件使用 `useSessionQuery()`，路由守卫使用 `sessionQueryOptions()`，不要再用独立的本地存储或另一份认证会话状态。

查询结果同时含 `user` 和 `session` 才算有效。未登录返回空值，不应当作服务器故障；网络失败则不能直接当作未登录。

## 私有路由

`apps/app/routes/(app)/route.tsx` 在进入页面前读取会话，未登录跳转 `/login`。登录回跳地址先通过安全校验，仅允许站内地址。

这是界面访问控制。真正的安全边界在服务端：私有 tRPC 操作使用 `protectedProcedure`，组织、用户数据与账单再校验归属和角色。

## 登录与退出

登录、注册或活动组织改变后，调用 `revalidateSession()` 删除旧查询并让路由重新检查。退出时先请求服务端结束会话；请求失败显示错误，不能只把界面改成退出。

服务端退出成功后清空会话查询并整页跳转 `/login`，同时丢弃内存中的用户数据和界面状态，避免下一个账号看到旧缓存。

## Cookie 与域名

`APP_ORIGIN` 应和访问网站的协议、域名、端口完全一致。生产使用 HTTPS；本地使用统一入口 `http://localhost:4400`。改变站点 ID 会改变认证 cookie 前缀，需要重新登录。

认证提示 cookie 只是历史路由提示，不证明会话有效；当前首页始终公开，不能据此判断登录状态。

## 排查清单

* 登录成功后检查 `/api/auth/get-session` 是否返回用户与会话。
* 检查请求是否带上对应域名 cookie，是否混用 localhost 与 IP 地址。
* 检查会话查询是否在认证成功后刷新，网络错误是否被错误地当作未登录。
* 退出后直接请求私有接口应被拒绝。
* 活动组织成员被移除后，旧会话不能继续读取组织账单。

会话需要实时数据库连接，不能使用 Hyperdrive 的旧查询结果作为授权依据。
