-
Notifications
You must be signed in to change notification settings - Fork 0
Feature Guide
何家欢 edited this page Jun 17, 2026
·
3 revisions
本页按功能解释 Passkey-Auth 已实现的能力,以及每个能力对应的代码位置。
项目的 Auth WebUI 不是传统账号密码表单,而是以 passkey 为中心的极简界面:
- 首屏只展示品牌 Logo 和状态输出
- 支持暗色模式
- 响应式布局适配移动端和桌面端
- 注册面板使用 View Transition 或 CSS 动画
- 状态提示可读、短句、低干扰
- OAuth 授权页自动呼出 passkey,减少用户点击
核心文件:
passkey_demo/templates/index.html
passkey_demo/templates/oauth_authorize.html
passkey_demo/static/styles.css
passkey_demo/static/oauth.css
passkey_demo/static/main.js
passkey_demo/static/oauth_authorize.js
注册流程:
- 用户解锁注册面板
- 前端请求
/api/register/options - 后端生成 WebAuthn registration options
- 浏览器调用
navigator.credentials.create() - 前端提交 credential 到
/api/register/verify - 后端验证并保存 credential public key
- 注册成功后自动登录
后端入口:
POST /api/ui/intent
GET /api/ui/register-client.js
POST /api/register/options
POST /api/register/verify
注册默认关闭,由配置控制:
PASSKEY_REGISTRATION_ENABLED=false
REGISTER_UNLOCK_TTL_SECONDS=120设计意图:
- 公开环境不应该让任意用户直接批量注册。
- 注册 UI 不直接写在 HTML 中,而是解锁后才动态加载。
- 注册入口有时间限制。
用户名登录会限制 credential 范围:
- 用户提供 username
- 后端查找该用户 credential
- WebAuthn options 中设置
allowCredentials - 浏览器只展示该用户可用 passkey
- 后端验证 credential 是否属于该用户
相关代码:
passkey_demo/app.py /api/login/options, /api/login/verify
passkey_demo/storage.py list_credentials_for_user, get_credential_by_id
无用户名登录用于更自然的 SSO 体验:
- 前端向
/api/login/options传空 username - 后端生成 authentication options,不限制
allowCredentials - 浏览器显示可用 discoverable passkey
- 后端从 credential 和 user handle 反查用户
相关配置在 webauthn_service.py:
- 注册时要求 resident key
- 登录时 username 为空会移除
allowCredentials - 后端从
userHandle找用户
标准 OAuth flow 面向第三方应用:
GET /oauth/authorize
POST /oauth/authorize/complete
POST /oauth/token
GET /oauth/userinfo
特点:
-
state防 CSRF - code 一次性消费
- code 绑定
client_id和redirect_uri - token 通过
itsdangerous.URLSafeTimedSerializer签名 - userinfo 返回稳定
sub
适合:
- 独立业务站点
- 外部系统接入
- 希望遵循 OAuth 心智模型的场景
link challenge flow 面向轻量跳转登录:
GET /oauth/challenge/{challenge_id}
POST /oauth/challenge/{challenge_id}/complete
示例页面:
GET /demo/link-login
POST /demo/link-login/start
GET /demo/link-login/callback
特点:
- 原网站先收集 username
- Auth 后端保存一次性 challenge
- Auth WebUI 用 username 发起 passkey
- 成功后回跳
challenge_result - callback 必须服务端校验签名和一次性状态
不要把 URL 中的 status=success 当成登录依据。
端点:
POST /api/server/session/verify
Authorization: Bearer PASSKEY_SERVER_API_TOKEN
用途:
- 业务后端确认 Auth session 是否已登录
- API gateway 或内部服务统一校验
- 服务端转发 Flask session cookie 到 Auth 服务
返回用户字段:
{
"sub": "stable-user-handle",
"id": 1,
"username": "alice",
"createdAt": 1780000000
}核心表:
| 表 | 作用 |
|---|---|
users |
用户名、user handle、创建时间 |
credentials |
WebAuthn credential public key 和 sign count |
oauth_authorization_codes |
一次性 OAuth code |
oauth_challenge_requests |
一次性 link challenge |
安全细节:
- authorization code 只存 hash
- code 和 challenge 都有过期时间
- code 和 challenge 都有 consumed 状态
- credential public key 存在服务端,用于后续认证验证
配置集中在:
passkey_demo/config.py
AppConfig 负责应用配置,ServerConfig 负责开发服务器配置。字段和值写在一起,并配有中文注释;环境变量可覆盖默认值。
测试位于:
tests/test_config.py
tests/test_registration_gate.py
tests/test_third_party_oauth.py
tests/test_link_login_challenge.py
运行:
.venv/bin/python -m unittest discover -s tests -v