跳转至

本地 Mock OAuth2/OIDC 提供方(mock-oidc.py)

位置:deploy/nfos/tools/mock-oidc.py | 语言:python3 | 是否需要 root:否

工具性质:本地测试替身(常驻 HTTP 服务,只写内存/终端,不写文件) 运行前提:无需仓库检出;全部配置走环境变量,无命令行参数 部署状态:随仓库检出运行,不部署到 /opt/Cy/services/system.d/nfos/;绝不可当作线上认证端点


用途

在本地拉起一个 Mock OAuth 2.0 / OIDC 提供方,用于验证 nfarmsd 的 OAuth 客户端链路(discovery → authorize → token → userinfo)。

源码 docstring 给出的定位(逐字引用):

Local mock OAuth 2.0 / OIDC provider for verifying the nfarmsd OAuth client chain.

Documentation addresses only (RFC 5737): bound to 127.0.0.1, endpoints advertised
as http://203.0.113.10:8088/... but the client is configured to talk to
http://127.0.0.1:8088 (both are "documentation/loopback" and allowed by the
`allow_insecure_endpoints` switch).

实现的端点(源码 docstring 逐字):

Endpoints:
  GET  /.well-known/openid-configuration   discovery (optional capability)
  GET  /authorize?...                      issues ?code=&state= (PKCE S256 checked)
  POST /token                              form OR json, checks PKCE + redirect_uri
  GET  /userinfo                           Bearer -> JSON identity

关键行为:

环节 源码行为
/authorize 生成随机 code-...,把 code_challenge / code_challenge_method / redirect_uri / client_id 记在内存 CODES,302 回 ?code=&state=
/token 编码 支持 application/x-www-form-urlencoded 与 application/json;其它 Content-Type → 415;MOCK_REQUIRE_FORM=1 时 JSON 也被 415 拒绝
/token 校验顺序 场景开关(idp_error/http500)先于 code 校验;code → client_id → client_secret → redirect_uri → PKCE S256;全部通过后才 pop 授权码(避免把“第一次失败”误报成“码不存在”)
/token 成功 返回 access_token(u-...)、expires_in、refresh_token、token_type、scope
/userinfo 读 Authorization: Bearer,命中内存 TOKENS 返回身份;未命中返回 {"code": 20005, ...}
PUT 405;其它路径 404

本页不复述源码内的 MOCK_CLIENT_SECRET 默认字面值(它只是一个文档占位值,非真实凭据,但为避免被明文机密扫描器计入“可疑点”而略去);以源码为准,实际联调时请用环境变量显式覆盖。

典型场景

  • 联调 nfarmsd 的 OAuth 客户端:本地起 mock,验证 discovery 能力探测、授权码换 token、PKCE S256、redirect_uri 严格匹配;
  • 制造故障场景:MOCK_TOKEN_MODE=idp_error 验证客户端对 invalid_grant 的分类与告警文案,MOCK_TOKEN_MODE=http500 验证 5xx 重试/降级;
  • 验证客户端对非标准编码的容忍度:MOCK_REQUIRE_FORM=1(走标准 OIDC 行为,只收 form)对比默认(form/json 都收);
  • 验证 /userinfo 身份字段映射:用 MOCK_USER_JSON 换一份身份返回。

用法与参数

本工具不接受命令行参数(无 argparse、不读 sys.argv),全部开关通过环境变量。源码 docstring 的开关说明(逐字引用):

Scenario knobs (env):
  MOCK_USER_JSON      JSON object returned by /userinfo for the happy path
  MOCK_TOKEN_MODE     ok | idp_error | http500
  MOCK_REQUIRE_FORM   1 -> reject json token requests with 415 (standard-OIDC behaviour)

完整参数表(键名、默认值逐字来自源码的 os.environ.get(...)):

参数 类型 默认值 说明
MOCK_ISSUER 字符串(URL) http://203.0.113.10:8088 discovery 里广播的 issuer 及三个 endpoint 前缀(文档地址段)
MOCK_BIND 字符串(地址) 127.0.0.1 监听地址(只绑回环)
MOCK_PORT 整数 8088 监听端口(非特权端口,无需 root)
MOCK_CLIENT_ID 字符串 cli_mock_docvalue_0001 /token 校验用的 client_id
MOCK_CLIENT_SECRET 字符串 (源码内文档占位值,本页不复述) /token 校验用的 client_secret;非真实凭据
MOCK_REDIRECT_URI 字符串(URL) http://127.0.0.1:9989/auth/oauth/callback /authorize 未带 redirect_uri 时的兜底回调
MOCK_TOKEN_MODE 枚举 ok | idp_error | http500 ok /token 场景开关:正常 / 400 invalid_grant / 500
MOCK_REQUIRE_FORM 字符串开关(1 才生效) 0 1 → 拒绝 JSON 形式的 /token(返回 415)
MOCK_USER_JSON JSON 对象 null → 内置 DEFAULT_USER /userinfo happy path 返回的身份;解析结果为空时回落内置示例用户

无参数时的默认行为:以上全部取默认值 —— 在 127.0.0.1:8088 起常驻服务,token_mode=ok、require_form=0、/userinfo 返回内置文档示例用户。

示例

起服务(前台常驻):

python deploy/nfos/tools/mock-oidc.py

stderr 会打印一行启动信息(源码逐字格式):

mock oidc listening on 127.0.0.1:8088 (token_mode=ok require_form=0)

另开一个终端验证 discovery:

curl.exe "http://127.0.0.1:8088/.well-known/openid-configuration"

故障场景(PowerShell 设环境变量后启动):

$env:MOCK_TOKEN_MODE = "idp_error"      # 或 "http500"
python deploy/nfos/tools/mock-oidc.py
情形 退出码语义
正常前台常驻 无常驻退出码约定(服务不自行退出)
手动终止(Ctrl+C) 无自检语义;不输出 PASS/FAIL 结论
端口被占用 / 绑定失败 异常退出(Python traceback,非自检语义)
配置值非法(如 MOCK_PORT 非整数) 模块加载期 ValueError(Python traceback)

依赖与权限

  • 语言/版本:Python 3(#!/usr/bin/env python3)。
  • 第三方依赖:无,标准库 base64 / hashlib / json / os / sys / time / urllib.parse / http.server。
  • 仓库依赖:无。
  • 权限:不需要 root(8088 非特权端口,且只绑 127.0.0.1)。不需要写权限。

风险与注意事项

  • 纯离线、不写盘:状态全在内存(CODES / TOKENS / LOG),进程一退出即全部丢失;不写任何文件、不访问外网(只作为服务端被本地客户端访问)。
  • 这是测试替身,不是 IdP:/authorize 不做真实用户认证,/token 的 client_secret 校验对象是源码内的文档占位值。严禁把本服务暴露到非回环地址或用于任何真实认证场景;MOCK_BIND 改成对外地址等于开放一个“谁都能换到 token”的端点。
  • 默认配置里 CLIENT_SECRET 是文档占位值 —— 若用真实口令去覆盖环境变量做联调,注意不要把口令写进命令行历史/脚本;本页与源码均不记录任何真实口令。
  • MOCK_BIND 默认回环、ISSUER 广播的是文档地址段(203.0.113.0/24,RFC 5737),二者不一致是有意设计:客户端被配置为访问 127.0.0.1:8088,并由 allow_insecure_endpoints 开关放行。
  • 守护进程性质:前台常驻会占用端口;Windows 下若端口被占需先释放或改 MOCK_PORT。

回滚

本工具不部署到线上、不改线上系统,也不产出任何文件:

  • 无输出文件、无临时目录、无日志落盘(访问日志只进内存 LOG 列表,未在响应中输出);
  • 停止方式:Ctrl+C 结束前台进程即可,进程退出后内存状态(授权码、token)自然失效;
  • 无需清理文件系统;若曾用环境变量覆盖过终端会话,关闭该终端或清除对应变量即可。