外观
Auth Server 配置
配置类:apps/auth-server/authserver/core/config.py(AuthServerConfig / load())。加载纪律见 分层构造。
环境变量统一以 DN42_AUTH_ 为前缀。
核心
| 环境变量 | TOML 键 | 类型 | 默认 | 说明 |
|---|---|---|---|---|
DN42_AUTH_DATABASE_URL | database.url | str | sqlite+aiosqlite:///<仓库根>/auth.db | SQLAlchemy 异步 DSN。独立于控制面的库——微服务边界:挑战、会话、签名钥、Passkey 只在这里。生产用独立 PostgreSQL database(dn42_auth) |
| — | kv.url | str | None | None | L4 瞬态凭据的 DSN(如 redis://valkey:6379/2,db 2 = auth-server)。OAuth 授权码(120s)与 WebAuthn ceremony(5min)改存 KV:过期即无用途、不参与关联查询,放 PG 只是给最短命的行制造 dead tuple。None 时全部回落 oauth_codes / webauthn_ceremonies 表,功能一行不变。⚠️ 实例是 noeviction,写进去的键必须带 TTL。角色与判据见 数据层参考,键定义见 KV 与缓存键空间 |
DN42_AUTH_ADMIN_TOKEN † | server.admin_token | str | None | None | 运维端点(OAuth 应用注册、手动 registry 同步)的 Bearer token。None 表示运维端点整体 fail-closed(403) |
DN42_AUTH_CORS_ORIGINS | server.cors_origins | 列表 | ("http://localhost:5173", "http://127.0.0.1:5173") | 前端跨源直连的白名单。生产授权页与本服务同源,本项主要供本地开发与门户前端跨源调用 |
† 标记表示 token 类语义:显式设空 = 关闭。
本服务的 schema 初始化用 create_all——库是全新拆出的、无历史包袱,表结构变更遵守「新列可空或带默认」纪律;规模长出来再引入独立迁移链。
会话令牌
| 环境变量 | TOML 键 | 类型 | 默认 | 说明 |
|---|---|---|---|---|
DN42_AUTH_TOKEN_ISSUER | token.issuer | str | http://127.0.0.1:8100 | 会话 JWT 与 ID Token 的 iss。生产必须显式配置为公开基址(https://auth.natlan.io):它写进每一枚 ID Token,且 OIDC discovery 文档的位置由它决定 |
DN42_AUTH_TOKEN_AUDIENCE | token.audience | str | dn42-peering | 会话 JWT 的 aud。所有 DN42 对等消费方共用一个 audience |
DN42_AUTH_SESSION_TTL_SECONDS | token.session_ttl_seconds | float 秒 | 7200.0(2h) | 会话 JWT 有效期 |
DN42_AUTH_CHALLENGE_TTL_SECONDS | token.challenge_ttl_seconds | float 秒 | 900.0(15min) | 归属验证挑战有效期 |
签名算法为 Ed25519(EdDSA),密钥对由服务自持,公钥经 /.well-known/jwks.json 发布。
人机验证
生产提供方是 Cap(自托管、基于工作量证明);Cloudflare Turnstile 仅作迁移期回落,[cap] 三项不齐时生效。
| 环境变量 | TOML 键 | 类型 | 默认 | 说明 |
|---|---|---|---|---|
DN42_AUTH_CAP_API_ENDPOINT | cap.api_endpoint | str | None | https://challenges.natlan.io | 浏览器用的公开地址(授权页 widget 直连解题)。内置默认即生产自建实例 |
DN42_AUTH_CAP_INTERNAL_ENDPOINT | cap.internal_endpoint | str | None | None | 服务端 siteverify 用的可选内网地址。与 Cap 同机部署时指到容器网内的服务名(http://cap:3000),免得服务器验证自己签的票要绕一圈公网与 CDN。未配时沿用公开地址 |
DN42_AUTH_CAP_SITE_KEY | cap.site_key | str | None | None | site key(公开值) |
DN42_AUTH_CAP_SECRET_KEY † | cap.secret_key | str | None | None | secret key(服务端专用) |
DN42_AUTH_TURNSTILE_SECRET_KEY † | turnstile.secret_key | str | None | None | Turnstile 回落用的服务端 secret。三项 Cap 与本项都没有时,挑战签发与枚举一律 fail-closed 400 |
DN42_AUTH_TURNSTILE_SITE_KEY | turnstile.site_key | str | None | None | Turnstile 回落用的 site key(公开值),由 authorize-context 下发给前端挂 widget |
cap_api_endpoint / cap_site_key / cap_secret_key 三项齐备即 Cap 生效并取代 Turnstile。生产只需配后两项(第一项有内置默认)。
widget 的提供方与 endpoint 由 GET /api/v1/oauth/authorize-context 的 captcha 字段运行时下发,前端不必硬编码。
邮件发送
| 环境变量 | TOML 键 | 类型 | 默认 | 说明 |
|---|---|---|---|---|
DN42_AUTH_MAIL_CF_ACCOUNT_ID | mail.cloudflare_account_id | str | None | None | Cloudflare Email Sending 账号 id |
DN42_AUTH_MAIL_CF_API_TOKEN † | mail.cloudflare_api_token | str | None | None | API token |
DN42_AUTH_MAIL_FROM_ADDRESS | mail.from_address | str | None | None | 发件地址 |
DN42_AUTH_MAIL_FROM_NAME | mail.from_name | str | NATLAN DN42 | 发件人显示名 |
DN42_AUTH_DEV_LOG_MAIL_SENDER | dev.log_mail_sender | bool | False | 开发用发送器:验证码打日志、不投递。生产严禁开启 |
四项齐备才装配真实发送器;否则 email 验证方式在挑战签发时返回 503(fail-closed,不静默假装发出)。
OAuth 资源白名单
| 环境变量 | TOML 键 | 类型 | 默认 | 说明 |
|---|---|---|---|---|
DN42_AUTH_OAUTH_RESOURCES | oauth.resources | 列表 | () | 可被请求的资源服务标识白名单(RFC 8707 的 resource) |
不在名单里的 resource 一律拒绝——否则任何 client 都能给自己签 aud 任意的令牌,资源服务端的 audience 校验就形同虚设。空列表表示只签给本服务自身(仅够打 /userinfo)。
Passkey(WebAuthn)
| 环境变量 | TOML 键 | 类型 | 默认 | 说明 |
|---|---|---|---|---|
DN42_AUTH_PASSKEY_RP_ID | passkey.rp_id | str | None | None(派生) | WebAuthn RP ID。留空取 token_issuer 的主机名 |
DN42_AUTH_PASSKEY_ORIGINS | passkey.origins | 列表 | ()(派生) | 核验 clientDataJSON.origin 的白名单。留空取 issuer 自身(scheme://netloc) |
生产的授权页 SPA 与本服务同源,默认派生值即正确。本地开发时 SPA 跑在独立端口,需显式把 http://localhost:5173 加进 origins。
最小配置示例
toml
# /etc/dn42-auth/auth-server.toml(chmod 600)
[server]
admin_token = "change-me"
cors_origins = ["https://peering.example.dn42"]
[database]
url = "postgresql+asyncpg://dn42:secret@postgres:5432/dn42_auth"
[registry]
service_url = "http://registry-server:8200"
service_token = "change-me"
[cap]
internal_endpoint = "http://cap:3000"
site_key = "…"
secret_key = "…"
[mail]
cloudflare_account_id = "…"
cloudflare_api_token = "…"
from_address = "noreply@example.dn42"
[oauth]
resources = ["https://api.example.dn42"]
[token]
issuer = "https://auth.example.dn42"registry 副本服务
| 环境变量 | TOML 键 | 类型 | 默认 | 说明 |
|---|---|---|---|---|
DN42_AUTH_REGISTRY_SERVICE_URL | registry.service_url | str | None | None | registry-server 地址 |
DN42_AUTH_REGISTRY_SERVICE_TOKEN † | registry.service_token | str | None | None | registry-server API 的 Bearer token |
两项不配齐 = 客户端 fail-closed:验证方式枚举、挑战签发、OAuth claims 富化一律按 503 处置。registry 是本服务的信任锚——公钥指纹、邮箱、维护者归属全部来自它,取不到就不能签发身份。