Skip to content

Feature Guide

何家欢 edited this page Jun 7, 2026 · 3 revisions

Feature Guide

本页按功能解释 Passkey-Auth 已实现的能力,以及每个能力对应的代码位置。

现代 UI 和用户体验

项目的 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

Passkey 注册

注册流程:

  1. 用户解锁注册面板
  2. 前端请求 /api/register/options
  3. 后端生成 WebAuthn registration options
  4. 浏览器调用 navigator.credentials.create()
  5. 前端提交 credential 到 /api/register/verify
  6. 后端验证并保存 credential public key
  7. 注册成功后自动登录

后端入口:

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 范围:

  1. 用户提供 username
  2. 后端查找该用户 credential
  3. WebAuthn options 中设置 allowCredentials
  4. 浏览器只展示该用户可用 passkey
  5. 后端验证 credential 是否属于该用户

相关代码:

passkey_demo/app.py        /api/login/options, /api/login/verify
passkey_demo/storage.py    list_credentials_for_user, get_credential_by_id

无用户名登录

无用户名登录用于更自然的 SSO 体验:

  1. 前端向 /api/login/options 传空 username
  2. 后端生成 authentication options,不限制 allowCredentials
  3. 浏览器显示可用 discoverable passkey
  4. 后端从 credential 和 user handle 反查用户

相关配置在 webauthn_service.py

  • 注册时要求 resident key
  • 登录时 username 为空会移除 allowCredentials
  • 后端从 userHandle 找用户

OAuth Authorization Code Flow

标准 OAuth flow 面向第三方应用:

GET  /oauth/authorize
POST /oauth/authorize/complete
POST /oauth/token
GET  /oauth/userinfo

特点:

  • state 防 CSRF
  • code 一次性消费
  • code 绑定 client_idredirect_uri
  • token 通过 itsdangerous.URLSafeTimedSerializer 签名
  • userinfo 返回稳定 sub

适合:

  • 独立业务站点
  • 外部系统接入
  • 希望遵循 OAuth 心智模型的场景

Link Challenge Flow

link challenge flow 面向轻量跳转登录:

GET  /oauth/challenge/{challenge_id}
POST /oauth/challenge/{challenge_id}/complete

demo 页面:

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 当成登录依据。

Server Session Verify API

端点:

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
}

SQLite 存储模型

核心表:

作用
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

Clone this wiki locally