# 应用登录与用户字段映射

把认证中心作为登录提供方时，管理员在「应用接入」、开发者在「我的应用」创建应用，再把配置填入接入方的 OAuth2 / OpenID Connect 设置。最先查看接入方要求的字段格式：字段名、JSONPath 和模板表达式不是同一种语法。

## 最常用的字段映射

用户信息地址是本站 `/oauth/userinfo`，请求携带 `Authorization: Bearer <access_token>`。申请 `openid profile email`，且在应用允许范围里勾选这三项，重新授权后才能取得以下字段。`application_creator` 随 `openid` 始终返回，用于判断当前用户是否为当前应用的创建人。

| 用途 | 字段名 | JSONPath | 类型 | 所需 scope |
| --- | --- | --- | --- | --- |
| 用户唯一标识 | `sub` | `$.sub` | string | openid |
| 当前应用创建人 | `application_creator` | `$.application_creator` | boolean | openid |
| 显示名称 / 昵称 | `name` | `$.name` | string | profile |
| 邮箱 | `email` | `$.email` | string | email |
| 邮箱已验证 | `email_verified` | `$.email_verified` | boolean | email |

字段位于 JSON 根对象，没有 `data` 或 `user` 包装。若对方要求字段名，填 `sub`；若要求 JSONPath，填 `$.sub`。只有对方明确要求其他模板语法时，才使用其模板。

平台内部个人资料的 `id`、`nickname`、`avatar` 不是 OIDC 用户信息字段。当前不返回 `picture`、`avatar`、`username`、`preferred_username`、手机号或角色。头像映射留空；如接入方要求唯一用户名且允许 UUID，可使用 `sub`，不要把可重复昵称当作唯一账号。

应用可以在首次登录时把 `application_creator=true` 的用户设为应用管理员。该声明只回答“此人是否创建了当前 `aud` 对应的应用”，不会泄露创建人 ID、认证中心管理员身份或其他应用权限。ID Token 中的值必须先通过 JWKS 验签，并校验 `iss`、`aud`、`exp` 和 `nonce`；不能信任浏览器提交的字段或只解码未验签的 JWT。调用 UserInfo 时必须使用服务端取得的 access token，并核对 `sub` 与 ID Token 一致。

用户唯一关联应使用发行方 `iss` 与 `sub`。邮箱和昵称可以变化；不能仅凭未验证邮箱合并已有账号。

## 响应示例

以下由接口契约生成，全部为虚构数据，实际字段取决于令牌 scope。

```json
{
  "sub": "00000000-0000-4000-8000-000000000001",
  "name": "示例用户",
  "email": "person@example.test",
  "email_verified": false,
  "application_creator": true
}
```


`email_verified` 是布尔值，可能为 `false`，不是字符串 `"true"`。没有申请对应 scope 时字段直接省略。管理员修改应用允许范围不会给旧令牌追加权限，需要重新发起授权。

## 接入配置

| 接入方设置 | 本平台的值 |
| --- | --- |
| Issuer / 发行方 | 本站公开根地址，与发现文档 issuer 完全一致 |
| Discovery / 自动发现 | `/.well-known/openid-configuration` 的完整本站 URL |
| 授权地址 | `/oauth/authorize` 的完整本站 URL |
| Token 地址 | `/oauth/token` 的完整本站 URL |
| 用户信息地址 | `/oauth/userinfo` 的完整本站 URL |
| JWKS 公钥地址 | `/.well-known/jwks.json` 的完整本站 URL |
| Client ID | 应用记录中的 client_id，不是应用 UUID |
| Client Secret | 创建或重置时显示的 client_secret，仅用于机密客户端服务端 |
| Scope | `openid profile email`；需要刷新再加 `offline_access` |
| 回调地址 | 从接入方复制完整回调地址，在本平台精确登记，不使用通配符 |

生产与非回环地址必须使用 HTTPS；仅 localhost、127.0.0.1、[::1] 允许登记 HTTP 回调。APP_ORIGIN 是统一对外地址，不要混用前端、后端端口或不同域名。

## 授权流程

1. 每次登录生成随机 `state`（16–1024 字符）、`nonce`（16–512 字符）与 `code_verifier`（43–128 字符）。默认所有客户端都要求 PKCE S256。
2. challenge 为 verifier 的 SHA256 经无填充 Base64URL 编码。浏览器访问授权地址，传 `response_type=code`、client_id、redirect_uri、scope、state、nonce、code_challenge、code_challenge_method=S256。
3. 用户同意后，验证回调 state、iss；若回调含 error，按错误处理，不继续交换。
4. 60 秒内用 code、同一 redirect_uri 和 verifier POST 表单到 token 地址。机密客户端使用 Basic 或表单 client_secret，公开客户端仅使用 client_id，不能打包 secret。
5. ID Token 是 RS256 JWT，必须用 JWKS 验签并校验 iss、aud、exp、nonce。access_token 是不透明字符串，用它请求 UserInfo；UserInfo 的 sub 必须和验签后的 ID Token sub 一致。
6. 仅 `offline_access` 返回 refresh_token。刷新需串行执行并原子替换返回值；旧值重放将撤销令牌族。会话撤销或账号停用后令牌也失效。

### 授权页快捷登录

需要用户交互时，授权页默认显示快捷登录：认证中心小程序二维码位于左侧；浏览器已有有效会话时，右侧同时显示当前账号并可直接继续。浏览器没有有效会话时，快捷登录区只显示二维码。用户也可以切换到账号密码或管理员已启用的 GitHub、微信、QQ、微博等第三方登录，完成后会回到同一条授权请求，不会丢失 `state`、PKCE、`redirect_uri` 或待确认的 scope。

二维码是两分钟有效的一次性服务端挑战，不是静态登录链接。它同时绑定原浏览器持有的轮询密钥和完整 OAuth 请求；扫码会先绑定当前小程序会话，必须在手机上核对应用、账号和权限并明确确认，原浏览器才会建立 Web 会话。过期后在电脑端刷新二维码；取消、切换登录方式或成功消费都会使旧挑战失效。`prompt=login` 要求重新验证，不能由已有会话或扫码快捷登录代替；`prompt=none` 仍按 OIDC 语义直接返回 `login_required` 或 `consent_required`。

ID Token 始终提供 `application_creator`；profile 只提供 name，email 提供 email/email_verified；没有 picture。完整声明结构、表单参数和错误结构见[登录与授权接口参考](/help/integration-api)。

## 复制调用示例

下面是 POSIX shell 示例，变量由你的服务端安全注入；不要把真实令牌提交或发给 AI。

```sh
curl --fail-with-body "$ISSUER/oauth/userinfo" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

curl --fail-with-body "$ISSUER/oauth/token" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  --data-urlencode 'grant_type=authorization_code' \
  --data-urlencode "code=$CODE" \
  --data-urlencode "redirect_uri=$REDIRECT_URI" \
  --data-urlencode "code_verifier=$CODE_VERIFIER"
```

## 常见问题

| 现象 | 检查方法 |
| --- | --- |
| 找不到 id / nickname / data.email | 改为根层 sub / name / email；检查对方要求的映射语法 |
| 只有 sub | 实际授权缺少 profile/email；勾选范围并重新授权 |
| invalid_token（401） | 使用 access_token，检查是否过期、撤销或会话/应用停用；不要使用 id_token、API Key 或机器发布令牌 |
| insufficient_scope（403） | 重新申请含 openid 的令牌 |
| invalid_client（401） | 核对 Client ID、密钥、客户端类型和应用启用状态 |
| invalid_grant（400） | code 过期/已用、redirect_uri 不同、verifier 不匹配或刷新令牌重放 |
| 提示缺少 nonce | 升级接入客户端优先；确实无法发送时可对单个机密应用启用缺少 nonce 兼容，公开客户端不允许 |
| 接入方连 PKCE 都不支持 | 仅对能保存 Client Secret 的机密应用，在“应用接入 → 编辑”或“我的应用 → 编辑配置”开启传统 OIDC 客户端兼容；公开客户端不允许 |
| 429 / 503 | 按 HTTP 状态处理；限流中间件可能返回 code/message，OAuth 处理器返回 error |

[下载 OpenAPI JSON](/docs/integration.openapi.json) · [AI 文档索引](/llms.txt) · [软件授权与绑定](/help/integration)
