跳转至

认证与授权

生态系统的所有平台都采用同一套认证模型。本页说明该模型,并为新接入的依赖方 (RP)提供实用指引。

模型概览

sequenceDiagram
    participant U as 用户
    participant RP as RP 应用
    participant SSO as Gerege SSO
    participant EID as eID Mongolia
    participant P as 手机

    U->>RP: 点击「登录」
    RP->>SSO: Authorization request (code + PKCE)
    SSO->>EID: 发起 eID 登录
    EID->>P: 二维码 / deep-link / 推送
    P-->>EID: PIN1 确认
    EID-->>SSO: 公民身份已确认
    SSO-->>RP: Authorization code
    RP->>SSO: code + code_verifier → 令牌
    SSO-->>RP: access + refresh + id_token
    RP->>SSO: /userinfo
    SSO-->>RP: 用户信息

核心原则:RP 从不直接访问 eID,一切都经由 SSO。

登录方式

方式 类型 说明
eID 主要 二维码 · 移动端 deep-link · 按登记号推送
Google 辅助 首次绑定必须经 eID 核验

不存在的东西:密码、邮箱/OTP 登录、短信 OTP 登录。

这是有意为之的决定。没有密码,就不会泄露密码、不会被撞库复用、也不会被钓鱼。

平台在哪里完成登录 —— AUTH_MODE

生态中的平台可以承担两种角色之一:

  • 身份服务 —— 平台自行完成用户认证。登录卡片(eID 二维码/登记号 · Google)直接出现在它自己的首页和 /login 上。
  • 依赖方(RP) —— 平台把登录委托给上游 SSO。点击「登录」会跳转到 SSO,用户在那里完成认证后再返回。

这两者不是代码差异,而是配置。由后端的 AUTH_MODE 决定:

取值 登录界面
provider 登录卡片显示在本平台上
client 跳转到上游 SSO(SSO_ISSUER

未设置时,会根据是否配置了 SSO_CLIENT_ID 自动推导。

前端从公开接口 GET /api/v1/site/auth 读取自身模式 —— 无需认证,响应中不含 任何机密:

{ "mode": "client", "sso_issuer": "https://sso.gerege.mn", "provider": false }

是否作为 issuer 是另一个问题

AUTH_MODE 回答的是「本平台的用户在哪里登录」。平台本身是否为 其他应用签发令牌,由 OAUTH_ISSUER 单独决定。两者可以同时启用 —— 形成链式结构:平台既为他人签发令牌,又把自己的用户送往上游 IdP。

实际效果:SSO 服务与使用它的平台运行同一份代码。同一个 Docker 镜像会根据 环境变量启动为其中任一角色。详见共享代码

PIN1 与 PIN2

PIN1 PIN2
证书 Authentication Signing
用途 登录 签名
法律后果 ——不可否认性

登录 ≠ 签名

以 PIN1 登录并不意味着用户同意了任何事情。对于具有法律后果的行为 (合同、授权、财务义务),必须另行以 PIN2 签名。

OIDC 技术参数

项目 取值
流程 Authorization code + PKCE (S256)
Access token 不透明(opaque)
id_token JWT,RS256
Refresh token 轮换式,带重用检测
机器对机器 client_credentials
Discovery /.well-known/openid-configuration
UserInfo /userinfo

refresh token 轮换

每次使用 refresh token 都会签发新的令牌,旧的随即失效。如果令牌被 再次使用,说明可能已遭窃取,因此整条链都会被作废。

所以 RP 在刷新之后必须立即保存新令牌。若继续保留旧的,下一次刷新就会让所有 会话全部掉线。

会话与登出

  • 会话由 JWT access + refresh 一对令牌承载。
  • 登出会同时作废 refresh 与 access(access 拒绝名单)。
  • 登出后用户会回到其发起时所在的域名

成为 RP 的步骤

1. 注册应用

Gerege SSO 控制台创建 client,可得到: client_idclient_secret

注册前需要准备:

  • redirect URI(所有环境——dev / staging / prod)
  • post-logout redirect URI
  • 需要申请的 scope
  • 应用名称与图标(会显示在用户的授权同意页上)

2. 读取 discovery

GET https://sso.gerege.mn/.well-known/openid-configuration

不要硬编码 endpoint,一律从这里读取。

3. Authorization request

实现 authorization code + PKCE (S256) 流程,并务必使用 statenonce

4. 换取令牌

code + code_verifieraccess_tokenrefresh_tokenid_token

5. 校验 id_token

必须逐项检查:

  • [ ] RS256 签名——使用从 JWKS 获取的密钥
  • [ ] iss 与 discovery 中的 issuer 一致
  • [ ] aud 等于您的 client_id
  • [ ] exp 尚未过期
  • [ ] nonce 与您发送的一致

6. 用户信息

/userinfo endpoint 获取。

常见错误

redirect URI 必须完全一致

要逐字符吻合:结尾的 /httphttps 之别、端口、子路径都很重要。 这是最常见的集成错误。

变更域名时的检查清单

变更域名或品牌时,须同时更新以下三处。漏掉任何一处,登录都会 悄无声息地失败

  • [ ] SSO 中的 redirect URI 清单
  • [ ] TLS 证书 SAN
  • [ ] RP 配置中的 issuer / endpoint 地址

不得跳过 PKCE

即便是机密客户端也要使用 PKCE。额外开销很小,防护效果却是实实在在的。

授权模型

认证完成之后就进入权限校验。生态系统的标准层级:

superadmin (1) → admin (2) → manager (3) → user (4)

详见通用约定页面。

在机构层面:成员关系由 Postgres RLS 保护——用户只能看到自己所属机构的 数据。这是数据库层面的限制,而不是应用代码里的判断。

授予 manager 权限需要本人以 PIN2 确认——详见 通用约定