docs: document TOTP support and limitations (#428)

## Summary

- document TOTP enrollment, verification, recovery codes, lifecycle, and
security controls in Chinese and English
- add HTTP and gRPC client flows, MFA request examples, and
recovery-code status fields
- describe clock synchronization, data retention, OAuth2 behavior, and
encryption-key rotation limitations

## Validation

- `npm run validate:content`
- `npx astro check`
- `npx astro build`
- `git diff --check`
pull/430/head
zijiren 1 month ago committed by GitHub
parent 96d22abe99
commit 61a837a424
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -12,7 +12,7 @@ import { Aside, Steps } from '@astrojs/starlight/components';
SyncTV 把安全边界拆成几层:
- **用户认证**密码、OPAQUE、WebAuthn/passkey、邮箱验证码和 OAuth2。
- **用户认证**密码、OPAQUE、WebAuthn/passkey、TOTP、恢复码、邮箱验证码和 OAuth2。
- **用户 2FA**:用户可选开启,必须拥有至少两种本地验证方式。
- **访问 token**:登录完成后签发,用于 HTTP/gRPC 业务 API。
- **management 控制面**CLI 使用的管理端点,独立于普通用户 token。
@ -32,6 +32,8 @@ SyncTV 把安全边界拆成几层:
| --- | --- | --- | --- |
| 密码/OPAQUE | 是 | 仅当服务端返回 `MFA_METHOD_PASSWORD` 时可用 | OPAQUE 登录不等同于 password MFA verifier |
| WebAuthn/passkey | 是 | 是 | 需要启用 WebAuthn 配置并完成绑定 |
| TOTP 身份验证器 | 否 | 是 | 使用 SHA-1、6 位验证码和 30 秒周期;服务端必须配置 `security.totp_encryption_key` |
| TOTP 恢复码 | 否 | 是 | 每个恢复码只能使用一次;恢复码属于 TOTP 因素,不单独计为一种本地验证方式 |
| 邮箱验证码 | 是 | 是 | 依赖 SMTP 和验证码发送能力 |
| OAuth2 | 是 | 否 | OAuth2 不参与本地 2FA但 2FA 用户可以直接用 OAuth2 登录 |
@ -61,11 +63,23 @@ OAuth2 是独立登录信任来源,不作为本地 2FA 的第一步或第二
1. 完成第一因素登录请求。
2. 如果响应要求 MFA读取可用的剩余验证方式。
3. 如果剩余方式包含邮箱验证码,直接调用发送验证码接口。
4. 使用 MFA 会话 ID 和第二因素证明完成登录。
5. 保存最终 access/refresh token。
4. 如果剩余方式包含 `MFA_METHOD_TOTP`,调用 `VerifyMfaTotp`。
5. 如果剩余方式包含 `MFA_METHOD_RECOVERY_CODE`,调用 `VerifyMfaRecoveryCode`。
6. 使用 MFA 会话 ID 和第二因素证明完成登录。
7. 保存最终 access/refresh token。
</Steps>
失败的 MFA password 尝试会记录到暴力破解保护中,不能绕过普通密码登录的失败计数和锁定策略。
失败的 password、TOTP 和恢复码验证会进入对应的暴力破解保护。TOTP 和恢复码共享用户级失败计数,并结合客户端 IP 执行限制。
## TOTP 与恢复码
服务端生成 20 字节随机 TOTP secret并通过 `otpauth://` URI 返回给客户端。客户端使用该 URI 生成二维码,也可以显示响应中的 secret 供用户手动输入。绑定流程有 10 分钟期限;用户必须提交一个有效 TOTP 验证码,服务端才会确认凭据并返回恢复码。
服务端用 `security.totp_encryption_key` 加密 TOTP secret并把用户 ID 放入密文上下文。验证码验证接受当前 30 秒时间步及相邻时间步。服务端记录最近使用的时间步,同一验证码不能在登录和敏感操作之间重复使用。生产主机应启用 NTP 或 chrony避免时钟偏差超出验证窗口。
绑定完成后,服务端返回 10 个恢复码。服务端保存恢复码哈希并丢弃明文;客户端应要求用户保存这些恢复码。重新生成会使旧恢复码全部失效,成功验证一个恢复码后,服务端会删除该恢复码。
开始绑定、重新生成恢复码和删除 TOTP 都需要一次有效的敏感操作验证。删除 TOTP 会减少一种本地验证方式;如果删除后无法保留两种本地验证方式,用户必须先关闭 2FA。每个敏感操作验证 ID 只能使用一次。
## 用户偏好与安全设置
@ -74,7 +88,8 @@ OAuth2 是独立登录信任来源,不作为本地 2FA 的第一步或第二
约束:
- 开启 2FA 前必须有至少两种可用本地验证方式。
- 删除 passkey 时,如果 2FA 已开启,删除后仍必须保留至少两种可用本地方式。其他认证方式的删除入口应按同一产品规则实现。
- TOTP 与它的恢复码共同计为一种本地验证方式。
- 删除 passkey、解绑邮箱或删除 TOTP 时,如果 2FA 已开启,操作后仍必须保留至少两种可用本地方式。
- 管理员修改用户偏好时必须遵守角色层级,普通 admin 不能越权修改 root 或同级高权限用户。
## 管理控制面

@ -171,7 +171,7 @@ Provider instance 绑定不是用户偏好;用户通过某个 instance 登录
2FA 约束:
- 开启 2FA 必须有至少两种本地验证方式password、webauthn/passkey、verified email。
- 开启 2FA 必须有至少两种本地验证方式password、webauthn/passkey、TOTP、verified email。TOTP 恢复码属于 TOTP 因素,不单独增加方式数量。
- OAuth2 不计入本地 2FA 因素,但 2FA 用户可以使用 OAuth2 登录。
- 删除验证方式时,如果用户开启了 2FA删除后仍必须保留至少两种可用本地方式。

@ -12,7 +12,7 @@ description: 管理 SyncTV 用户、全局角色、封禁状态、账号安全
| 全局角色 | `root`、`admin`、`user` | 控制平台级管理能力 |
| 有效状态 | `active`、`banned` | 影响登录、建房和加入房间 |
| 用户偏好 | 2FA、通知、Provider 默认实例 | 用户级设置,不等同于 YAML 配置 |
| 认证方式 | password、OPAQUE、passkey、email、OAuth2 | 决定用户能否登录和恢复访问 |
| 认证方式 | password、OPAQUE、passkey、TOTP、恢复码、email、OAuth2 | 决定用户能否登录和恢复访问 |
## 角色边界

@ -12,7 +12,7 @@ import { Aside, Steps } from '@astrojs/starlight/components';
SyncTV separates security into several boundaries:
- **User authentication**: password, OPAQUE, WebAuthn/passkey, email codes, and OAuth2.
- **User authentication**: password, OPAQUE, WebAuthn/passkey, TOTP, recovery codes, email codes, and OAuth2.
- **User 2FA**: optional per user and requires at least two local authentication methods.
- **Access tokens**: issued after login completion for HTTP/gRPC business APIs.
- **Management control plane**: the CLI management endpoint, separate from user tokens.
@ -32,6 +32,8 @@ SyncTV separates security into several boundaries:
| --- | --- | --- | --- |
| Password/OPAQUE | Yes | Available only when the server returns `MFA_METHOD_PASSWORD` | OPAQUE login is not the same as the password MFA verifier |
| WebAuthn/passkey | Yes | Yes | Requires WebAuthn configuration and a bound credential |
| TOTP authenticator | No | Yes | Uses SHA-1, six-digit codes, and a 30-second period; the server requires `security.totp_encryption_key` |
| TOTP recovery code | No | Yes | Each code works once; recovery codes belong to the TOTP factor and do not count as a separate local method |
| Email code | Yes | Yes | Requires SMTP and email-code delivery |
| OAuth2 | Yes | No | OAuth2 does not participate in local 2FA, but 2FA-enabled users may log in with OAuth2 |
@ -61,11 +63,23 @@ Client flow:
1. Complete the first-factor login request.
2. If MFA is required, read the remaining available methods.
3. If email is available, call the email-code send API immediately.
4. Complete MFA with the session ID and second-factor proof.
5. Store the final access and refresh tokens.
4. If `MFA_METHOD_TOTP` is available, call `VerifyMfaTotp`.
5. If `MFA_METHOD_RECOVERY_CODE` is available, call `VerifyMfaRecoveryCode`.
6. Complete MFA with the session ID and second-factor proof.
7. Store the final access and refresh tokens.
</Steps>
Failed MFA password attempts are recorded by brute-force protection and cannot bypass the normal password failure counters.
Failed password, TOTP, and recovery-code verification attempts enter their matching brute-force controls. TOTP and recovery codes share a per-user failure counter and include the client IP in enforcement.
## TOTP and Recovery Codes
The server generates a random 20-byte TOTP secret and returns it in an `otpauth://` URI. The client renders a QR code from that URI and can show the response secret for manual entry. Setup expires after 10 minutes. The user must submit a valid TOTP code before the server confirms the credential and returns recovery codes.
The server encrypts the TOTP secret with `security.totp_encryption_key` and binds the ciphertext context to the user ID. Verification accepts the current 30-second time step and its adjacent steps. The server records the latest accepted step, so a code cannot be reused across login and sensitive-operation verification. Production hosts should run NTP or chrony to keep clock drift within the verification window.
Setup returns 10 recovery codes. The server stores their hashes and discards the plaintext; the client should require the user to save the codes. Regeneration invalidates all previous codes, and successful verification removes the submitted code.
Starting setup, regenerating recovery codes, and deleting TOTP require sensitive-operation verification. Removing TOTP reduces the local-method count. The user must disable 2FA first if removal would leave fewer than two local methods. Each sensitive-operation verification ID works once.
## User Preferences and Security Settings
@ -74,7 +88,8 @@ User preferences store user-level settings. `two_factor_enabled` is security-sen
Constraints:
- Enabling 2FA requires at least two usable local methods.
- Removing a passkey while 2FA is enabled must still leave at least two usable local methods. Other authentication-method removal flows should follow the same product rule.
- TOTP and its recovery codes count as one local method.
- Removing a passkey, unbinding email, or deleting TOTP must leave at least two usable local methods while 2FA is enabled.
- Admin-initiated preference updates must respect role hierarchy. A non-root admin cannot modify root or equally privileged users.
## Management Control Plane

@ -171,7 +171,7 @@ Provider instance bindings are not user preferences. They are stored on provider
2FA constraints:
- Enabling 2FA requires at least two local methods: password, WebAuthn/passkey, verified email.
- Enabling 2FA requires at least two local methods: password, WebAuthn/passkey, TOTP, or verified email. TOTP recovery codes belong to the TOTP factor and do not increase the method count.
- OAuth2 is not counted as a local 2FA factor, but 2FA-enabled users may log in with OAuth2.
- Removing an authentication method while 2FA is enabled must still leave at least two usable local methods.

@ -12,7 +12,7 @@ Users have global roles, effective state, and security preferences. When handlin
| Global role | `root`, `admin`, `user` | Platform administration capability |
| Effective state | `active`, `banned` | Sign-in, room creation, and room joining |
| Preferences | 2FA, notifications | User-level settings, not YAML configuration |
| Auth methods | password, OPAQUE, passkey, email, OAuth2 | Sign-in and recovery options |
| Auth methods | password, OPAQUE, passkey, TOTP, recovery codes, email, OAuth2 | Sign-in and recovery options |
## Role Boundaries

@ -198,15 +198,66 @@ Client flow:
4. If `required=true`, store the short-lived `mfa.session_id` and present `available_methods`.
5. If `MFA_METHOD_EMAIL` is available, call `RequestMfaEmailCode` when the user chooses email.
6. If `MFA_METHOD_WEBAUTHN` is available, call `StartMfaPasskey` for WebAuthn options, then `FinishMfaPasskey`.
7. After the second factor succeeds, the final response returns usable tokens.
7. If `MFA_METHOD_TOTP` is available, call `VerifyMfaTotp` with `mfa_session_id` and the six-digit code.
8. If `MFA_METHOD_RECOVERY_CODE` is available, call `VerifyMfaRecoveryCode`. Tell the user that a successful request consumes that code.
9. After the second factor succeeds, the final response returns usable tokens.
</Steps>
`GetUserPreferences` returns `auth_factors`, which lets clients show whether the current user has password, WebAuthn, or verified email factors. Enabling 2FA requires at least two local factors. Password is a first factor completed through OPAQUE or direct password login. OAuth2 does not count as a local 2FA factor.
`GetUserPreferences` returns `auth_factors`, which lets clients show whether the current user has password, WebAuthn, TOTP, or verified email factors. `totp_recovery_codes_remaining` reports the remaining recovery-code count. Enabling 2FA requires at least two local factors; TOTP and its recovery codes count as one. Password is a first factor completed through OPAQUE or direct password login. OAuth2 does not count as a local 2FA factor.
<Aside type="note">
OPAQUE is the public client local password login protocol. When a password first factor triggers MFA, clients should complete the challenge with an available non-password method such as email or WebAuthn.
OPAQUE is the public client local password login protocol. When a password first factor triggers MFA, clients should complete the challenge with an available non-password method such as email, WebAuthn, or TOTP.
</Aside>
## TOTP and Recovery Codes
TOTP uses SHA-1, six-digit codes, and a 30-second period. The server returns a complete `otpauth_uri`; render the QR code from this URI instead of rebuilding the issuer, account name, or algorithm parameters in the client.
Enrollment flow:
<Steps>
1. Call `StartSensitiveOperationVerification`, complete the methods in `available_methods`, and obtain the one-time `verification_id`.
2. Call `StartTotpSetup`. The response contains `setup_id`, `secret`, `otpauth_uri`, and `expires_at`.
3. Render a QR code from `otpauth_uri` and provide `secret` for manual entry.
4. After the user enters an authenticator code, call `FinishTotpSetup` before `expires_at`.
5. Show the 10 returned `recovery_codes` and require the user to save and confirm them. The server does not return that plaintext set again.
6. To enable 2FA, obtain a new `verification_id` and call `SetTwoFactorEnabled`. TOTP enrollment and 2FA enablement are separate sensitive operations.
</Steps>
HTTP and gRPC endpoint mapping:
| Operation | HTTP | gRPC |
| --- | --- | --- |
| Start sensitive-operation verification | `POST /api/user/sensitive-verification/start` | `StartSensitiveOperationVerification` |
| Complete one sensitive-operation method | `POST /api/user/sensitive-verification/finish` | `FinishSensitiveOperationVerification` |
| Start enrollment | `POST /api/user/totp/setup/start` | `StartTotpSetup` |
| Confirm enrollment | `POST /api/user/totp/setup/finish` | `FinishTotpSetup` |
| Enable or disable 2FA | `PUT /api/user/two-factor` | `SetTwoFactorEnabled` |
| Complete MFA with TOTP | `POST /api/auth/mfa/totp/verify` | `VerifyMfaTotp` |
| Complete MFA with a recovery code | `POST /api/auth/mfa/recovery-code/verify` | `VerifyMfaRecoveryCode` |
| Regenerate recovery codes | `POST /api/user/totp/recovery-codes/regenerate` | `RegenerateTotpRecoveryCodes` |
| Delete TOTP | `DELETE /api/user/totp` | `DeleteTotp` |
HTTP login verification example:
```bash
curl -sS http://localhost:8080/api/auth/mfa/totp/verify \
-H 'Content-Type: application/json' \
-d '{"mfaSessionId":"<mfa-session-id>","code":"123456"}'
```
A recovery-code request uses the same MFA session:
```bash
curl -sS http://localhost:8080/api/auth/mfa/recovery-code/verify \
-H 'Content-Type: application/json' \
-d '{"mfaSessionId":"<mfa-session-id>","recoveryCode":"ABCD-EFGH-JKLM"}'
```
Regenerating recovery codes invalidates the old set. Deleting TOTP also requires a new sensitive-operation verification. If removal would leave one local method, use one `verification_id` to disable 2FA and another `verification_id` to delete TOTP.
The server rejects an accepted 30-second time step after its first use. If the user uses a code for login, wait for the authenticator to generate the next code before starting a TOTP-protected sensitive operation.
## Email Codes
Email codes can be used for login, MFA, email verification, and password reset. Keep those flows separate in client state.

@ -44,7 +44,7 @@ PostgreSQL is durable business state. Redis is shared short-lived state and coor
## Major Components
- **API and realtime**: HTTP REST, public gRPC, WebSocket, and room realtime events share the same business services and permission model.
- **Authentication**: Password, OPAQUE, passkey/WebAuthn, email codes, OAuth2, user-level 2FA, and JWT tokens make up the login layer.
- **Authentication**: Password, OPAQUE, passkey/WebAuthn, TOTP and recovery codes, email codes, OAuth2, user-level 2FA, and JWT tokens make up the login layer.
- **Media**: Providers resolve external media, the proxy performs controlled forwarding, and slice cache stores Range slices only.
- **Horizontal scaling**: Multi-node deployments use Redis, discovery, leader election, and transactional outbox delivery boundaries.

@ -21,7 +21,7 @@ import { Aside } from '@astrojs/starlight/components';
| --- | --- | --- |
| User identifiers | Username, user ID, role, creation time | Can be linked to user activity |
| Email | User email, email identity, notification recipient | PII; only return to the user themselves or administrators |
| Authentication data | Password credential, OPAQUE credential, WebAuthn credential, OAuth2 identity, email token, refresh token or blacklist | Highly sensitive; should not enter logs |
| Authentication data | Password/OPAQUE credential, WebAuthn credential, encrypted TOTP secret, recovery-code hashes, OAuth2 identity, email token, refresh token or blacklist | Highly sensitive; should not enter logs |
| Provider credentials | Alist token, Emby API key, Bilibili cookie, remote provider JWT secret | Highly sensitive; protected by `security.credential_encryption_key` |
| Room data | Room name, members, permissions, playlists, media, playback state, invitation notifications, review records | Business data |
| Message data | Chat and notification content | May contain user input and sensitive context |

@ -41,7 +41,8 @@ These limits apply to the current release.
| Topic | Current boundary |
| --- | --- |
| 2FA | Local 2FA uses password, passkey, verified email; OAuth2 is not a local factor |
| 2FA | Local factors include password, passkey, TOTP, and verified email; TOTP recovery codes do not count as a separate factor; OAuth2 bypasses local 2FA for the OAuth2 login path |
| TOTP | Uses fixed SHA-1, six-digit codes, and a 30-second period; clients render QR codes from the server-provided `otpauth_uri`; rotating `security.totp_encryption_key` requires ciphertext migration or user re-enrollment |
| JWT revocation | Strong revocation depends on Redis token blacklist and token lifetime |
| OPAQUE secret | Long-term binding to password records; not for routine rotation |
| Provider encryption | Depends on credential encryption key; losing it affects decryption |

@ -198,15 +198,66 @@ Authorization: Bearer <access_token_or_guest_token>
4. 如果 `required=true`,保存短期 `mfa.session_id`,展示 `available_methods`。
5. 如果可用方式包含 `MFA_METHOD_EMAIL`,客户端可以直接调用 `RequestMfaEmailCode` 发送第二步验证码。
6. 如果可用方式包含 `MFA_METHOD_WEBAUTHN`,先调用 `StartMfaPasskey` 获取 WebAuthn options再调用 `FinishMfaPasskey`。
7. 第二因素成功后,最终响应会返回可用 token。
7. 如果可用方式包含 `MFA_METHOD_TOTP`,调用 `VerifyMfaTotp`,提交 `mfa_session_id` 和 6 位验证码。
8. 如果可用方式包含 `MFA_METHOD_RECOVERY_CODE`,调用 `VerifyMfaRecoveryCode`。客户端应提示该恢复码成功后会失效。
9. 第二因素成功后,最终响应会返回可用 token。
</Steps>
`GetUserPreferences` 会返回 `auth_factors`,用于展示当前用户是否具备 password、webauthn、email 这些本地验证方式。开启 2FA 前必须至少有两种本地验证方式。Password 是通过 OPAQUE 或直接密码登录完成的第一因素OAuth2 不计入本地 2FA 因素。
`GetUserPreferences` 会返回 `auth_factors`,用于展示当前用户是否具备 password、webauthn、TOTP、verified email 这些本地验证方式。`totp_recovery_codes_remaining` 表示剩余恢复码数量。开启 2FA 前必须至少有两种本地验证方式TOTP 和恢复码共同计为一种。Password 是通过 OPAQUE 或直接密码登录完成的第一因素OAuth2 不计入本地 2FA 因素。
<Aside type="note">
OPAQUE 是本地密码登录协议。当 password 第一因素触发 MFA 时,客户端应使用服务端返回的非 password 方法完成挑战,例如 email 或 WebAuthn
OPAQUE 是本地密码登录协议。当 password 第一因素触发 MFA 时,客户端应使用服务端返回的非 password 方法完成挑战,例如 email、WebAuthn 或 TOTP
</Aside>
## TOTP 与恢复码
TOTP 使用 SHA-1、6 位验证码和 30 秒周期。服务端返回完整 `otpauth_uri`;客户端直接使用该 URI 生成二维码,不要自行拼接 issuer、账号名或算法参数。
绑定流程:
<Steps>
1. 调用 `StartSensitiveOperationVerification`,按返回的 `available_methods` 完成验证,取得一次性的 `verification_id`。
2. 调用 `StartTotpSetup`。响应包含 `setup_id`、`secret`、`otpauth_uri` 和 `expires_at`。
3. 用 `otpauth_uri` 生成二维码,同时提供 `secret` 作为手动输入方式。
4. 用户输入身份验证器生成的验证码后,调用 `FinishTotpSetup`。绑定必须在 `expires_at` 前完成。
5. 展示返回的 10 个 `recovery_codes`,要求用户保存并确认。服务端不会再次返回同一批明文恢复码。
6. 用户要启用 2FA 时,获取新的 `verification_id`,再调用 `SetTwoFactorEnabled`。绑定 TOTP 和启用 2FA 是两个独立的敏感操作。
</Steps>
HTTP 和 gRPC 接口对应关系:
| 操作 | HTTP | gRPC |
| --- | --- | --- |
| 开始敏感操作验证 | `POST /api/user/sensitive-verification/start` | `StartSensitiveOperationVerification` |
| 完成一种敏感操作验证方式 | `POST /api/user/sensitive-verification/finish` | `FinishSensitiveOperationVerification` |
| 开始绑定 | `POST /api/user/totp/setup/start` | `StartTotpSetup` |
| 确认绑定 | `POST /api/user/totp/setup/finish` | `FinishTotpSetup` |
| 开启或关闭 2FA | `PUT /api/user/two-factor` | `SetTwoFactorEnabled` |
| TOTP 完成 MFA | `POST /api/auth/mfa/totp/verify` | `VerifyMfaTotp` |
| 恢复码完成 MFA | `POST /api/auth/mfa/recovery-code/verify` | `VerifyMfaRecoveryCode` |
| 重新生成恢复码 | `POST /api/user/totp/recovery-codes/regenerate` | `RegenerateTotpRecoveryCodes` |
| 删除 TOTP | `DELETE /api/user/totp` | `DeleteTotp` |
HTTP 登录验证示例:
```bash
curl -sS http://localhost:8080/api/auth/mfa/totp/verify \
-H 'Content-Type: application/json' \
-d '{"mfaSessionId":"<mfa-session-id>","code":"123456"}'
```
恢复码请求使用同一个 MFA 会话:
```bash
curl -sS http://localhost:8080/api/auth/mfa/recovery-code/verify \
-H 'Content-Type: application/json' \
-d '{"mfaSessionId":"<mfa-session-id>","recoveryCode":"ABCD-EFGH-JKLM"}'
```
重新生成恢复码会立即废止旧恢复码。删除 TOTP 前也要完成新的敏感操作验证。如果删除后只剩一种本地验证方式,先用一个 `verification_id` 关闭 2FA再用另一个 `verification_id` 删除 TOTP。
服务端会拒绝已经使用过的 30 秒时间步。同一个验证码先用于登录后,用户需要等待身份验证器生成下一组验证码,才能执行需要 TOTP 的敏感操作。
## 邮箱验证码
邮箱验证码可以用于登录、MFA、邮箱验证和密码找回具体接口以 OpenAPI/protobuf 为准。客户端应区分用途,不要复用不同流程的验证码输入框状态。

@ -44,7 +44,7 @@ PostgreSQL 是持久业务数据源。Redis 是共享短期状态和集群协调
## 主要组件
- **API 与实时协作** HTTP REST、公开 gRPC、WebSocket 和房间实时事件共享同一套业务服务与权限模型。
- **身份认证** 本地密码、OPAQUE、passkey/WebAuthn、邮箱验证码、OAuth2、用户级 2FA 和 JWT token 共同构成登录层。
- **身份认证** 本地密码、OPAQUE、passkey/WebAuthn、TOTP 与恢复码、邮箱验证码、OAuth2、用户级 2FA 和 JWT token 共同构成登录层。
- **媒体能力** Provider 负责解析外部媒体proxy 负责可控转发slice cache 只缓存 Range slice不做 full-body cache。
- **横向扩展** 多节点通过 Redis、节点发现、leader election 和事务型 outbox 同步实时事件与后台任务边界。

@ -21,7 +21,7 @@ import { Aside } from '@astrojs/starlight/components';
| --- | --- | --- |
| 用户标识 | 用户名、用户 ID、角色、创建时间 | 可关联用户行为 |
| 邮箱 | 用户邮箱、邮箱验证身份、通知收件地址 | PII只应返回给本人或管理员 |
| 认证数据 | password credential、OPAQUE credential、WebAuthn credential、OAuth2 identity、email token、refresh token/blacklist | 高敏感,不应进入日志 |
| 认证数据 | password/OPAQUE credential、WebAuthn credential、加密 TOTP secret、恢复码哈希、OAuth2 identity、email token、refresh token/blacklist | 高敏感,不应进入日志 |
| Provider 凭据 | Alist token、Emby API key、Bilibili cookie、远程 Provider JWT secret | 高敏感,依赖 `security.credential_encryption_key` 保护 |
| 房间数据 | 房间名、成员、权限、播放列表、媒体、播放状态、邀请通知和审核记录 | 业务数据 |
| 消息数据 | 聊天、通知内容 | 可能包含用户输入和敏感上下文 |

@ -41,7 +41,8 @@ description: 当前版本在部署、媒体、集群、认证和 API 稳定性
| 主题 | 当前边界 |
| --- | --- |
| 2FA | 本地 2FA 依赖 password、passkey、verified emailOAuth2 不作为本地因素 |
| 2FA | 本地因素包括 password、passkey、TOTP 和 verified emailTOTP 恢复码不单独计为一种因素OAuth2 不参与本地 2FA并可直接完成 OAuth2 登录路径 |
| TOTP | 固定使用 SHA-1、6 位验证码和 30 秒周期;客户端负责用服务端返回的 `otpauth_uri` 生成二维码;轮换 `security.totp_encryption_key` 需要迁移密文或让用户重新绑定 |
| JWT 吊销 | 强吊销依赖 Redis token blacklist 和 token 有效期 |
| OPAQUE secret | 与密码记录长期相关,不适合常规轮换 |
| Provider 凭据加密 | 依赖 credential encryption keykey 丢失会影响解密 |

Loading…
Cancel
Save