示例与排错 开发人员
上次更新:2026年8月6日
示例与排错
片段示例便于对照协议;完整可运行脚本见《Python 最小可运行示例》。请将常量换成你的实际值。
发现文档
curl -sS https://api-passport.swaymoon.com/.well-known/openid-configuration | jq .
请确认 authorization_endpoint、token_endpoint、userinfo_endpoint、jwks_uri 和 id_token_signing_alg_values_supported 等字段。
授权 URL(浏览器跳转)
https://api-passport.swaymoon.com/oauth2/authorize
?response_type=code
&client_id=swm_YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&scope=openid%20name%20picture%20email
&state=YOUR_STATE
&code_challenge=YOUR_CHALLENGE
&code_challenge_method=S256
拼 URL 时对查询参数只编码一次。若地址栏里出现 redirect_uri=http%253A%252F%252F…,属于双重编码,服务端会按字面值校验并失败。
换取令牌:机密客户端(curl)
ISSUER=https://api-passport.swaymoon.com
CLIENT_ID='swm_YOUR_CLIENT_ID'
CLIENT_SECRET='YOUR_SECRET'
CODE='...'
VERIFIER='...'
REDIRECT='https://app.example.com/callback'
curl -sS -u "$CLIENT_ID:$CLIENT_SECRET" \
-X POST "$ISSUER/oauth2/token" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code=$CODE" \
--data-urlencode "redirect_uri=$REDIRECT" \
--data-urlencode "code_verifier=$VERIFIER"
换取令牌:非机密客户端(curl)
ISSUER=https://api-passport.swaymoon.com
CLIENT_ID='swm_YOUR_CLIENT_ID'
CODE='...'
VERIFIER='...'
REDIRECT='https://app.example.com/callback'
curl -sS \
-X POST "$ISSUER/oauth2/token" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code=$CODE" \
--data-urlencode "redirect_uri=$REDIRECT" \
--data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode "code_verifier=$VERIFIER"
UserInfo(curl)
curl -sS https://api-passport.swaymoon.com/userinfo \
-H "Authorization: Bearer $ACCESS_TOKEN"
刷新令牌(仅机密客户端)
curl -sS -u "$CLIENT_ID:$CLIENT_SECRET" \
-X POST "$ISSUER/oauth2/token" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "grant_type=refresh_token" \
--data-urlencode "refresh_token=$REFRESH_TOKEN"
常见问题
| 现象 | 可能原因 | 处理 |
|---|---|---|
| 授权报错 / 无法跳转至回调地址 | redirect_uri 与登记值不一致,或双重编码 | 逐字符核对;地址栏不应出现 %253A / %252F |
invalid_request / PKCE 相关错误 | 缺少 code_challenge,或 code_challenge_method 不是 S256 | 在授权与换取令牌流程中配套使用 code_verifier 和 code_challenge |
invalid_client | Basic 凭证错误,或对非机密客户端误用了 Basic / secret | 核对类型;机密核对 client_id/client_secret;非机密改用表单 client_id |
invalid_grant | code 已使用或已过期,或 code_verifier 不匹配 | 重新发起授权,不要重复提交同一个 code |
| 同意页未显示预期权限 | 门户未配置该权限,或请求中未包含对应的 scope | 检查应用的可选权限和 scope 参数 |
回调 error=invalid_scope | 授权请求的 scope 超出该客户端已登记范围 | 在门户「功能」中勾选对应权限,或缩小请求中的 scope |
UserInfo 没有 email | 令牌未含 email(用户取消了可选邮箱,或请求未申请) | 检查令牌响应中的 scope;已授予 email 时应始终有该字段 |
| UserInfo 资料像「空的」 | 未授权资料细项时返回默认占位值(如昵称 摇月用户、头像 ""),不是字段缺失 | 按令牌 scope 判断是否为真实资料;见《令牌与用户信息》 |
| 用户选了隐私邮箱 | email 可能为 @privaterelay.swaymoon.com 中继地址 | 按普通邮箱处理即可;真实邮箱不会提供给你方 |
sub 与其他应用中的值不一致 | 这是 pairwise 机制的预期行为 | 仅在当前 client_id 或团队范围内使用 sub |
| ID Token 验签失败 | 未使用 ES256,或缓存的 JWKS 已过期 | 重新获取 /oauth2/jwks,并确认 alg 为 ES256 |
前端暴露 client_secret | 将机密客户端用于纯 SPA | 改为创建非机密客户端,或仅在后端保存密钥 |
非机密客户端没有 refresh_token | 预期行为 | Access Token 过期后重新走授权码 + PKCE |
联调提示
- 根据发现文档获取端点,避免硬编码路径。
- 完整本地脚本:《Python 最小可运行示例》。
- 登录和授权确认在浏览器中完成;机密客户端的换取令牌与 UserInfo 应在服务端进行。
- 生产环境的 Issuer 和前端域名分别为
api-passport.swaymoon.com和passport.swaymoon.com。
获取帮助
- 用户文档:关闭开发人员模式后,查看「指南」和「法律」分类。
- 开发者门户:develop.swaymoon.com
- 联系方式:hello@swaymoon.com(请勿通过公共渠道发送明文
client_secret;描述问题时可将其打码)