必须手动校验id_token,因go标准库不支持oidc身份验证;golang.org/x/oauth2仅处理授权码交换,需用go-oidc等库验证签名、iss、aud、exp、nonce等字段确保身份真实。

OIDC 认证流程在 Go 中必须手动处理 id_token 校验
Go 标准库不提供 OIDC 客户端,golang.org/x/oauth2 只管 OAuth2 授权码交换,不校验 id_token。漏掉这步等于绕过身份真实性验证——你拿到的可能是伪造的 token。
必须自己解析、验证签名、检查 iss、aud、exp、nonce(如果用了)、azp(多客户端场景下)。推荐用 github.com/coreos/go-oidc/v3/oidc,它封装了 JWKS 密钥轮换和标准校验逻辑。
-
oidc.Provider初始化时需传入 issuer URL(如https://auth.example.com),不是授权端点 - 校验时必须传入与注册客户端一致的
audience(通常是 client_id),否则aud校验失败 - 注意时钟偏移:生产环境建议用
jwt.WithAcceptableSkew(10 * time.Second),避免因服务器时间不同步导致exp校验失败
用 http.HandlerFunc 做中间件时别直接重定向到 /login
常见错误是把未认证请求无条件 302 到登录页,结果 API 请求(比如前端 fetch)收到重定向响应后静默失败,前端拿不到错误提示。
正确做法是区分请求类型:对 HTML 请求返回重定向,对 JSON 请求(检测 Accept: application/json 或路径含 /api/)返回 401 Unauthorized + 错误信息。
- 检查
r.Header.Get("Accept")是否包含application/json - 避免硬编码 redirect URL,从
r.URL.String()提取原始路径,拼成state参数带回去 - state 必须防重放:生成时加时间戳+随机数,校验时检查有效期(比如 5 分钟内)
oauth2.Config 的 RedirectURL 必须与 OIDC 提供方注册完全一致
哪怕多一个斜杠、少一个协议、端口没写全,都会导致 provider 拒绝回调,报错类似 invalid_request: redirect_uri_mismatch。
本地开发常踩坑:用 http://localhost:8080/callback 注册,但代码里写成 http://127.0.0.1:8080/callback ——二者 DNS 解析不同,OIDC provider 视为不同 URI。
- 开发时统一用
http://localhost:8080/callback,不要切 IP 地址 - 生产环境务必用 HTTPS,且
RedirectURL的 scheme、host、port、path 全部匹配注册值 - 若用反向代理(如 Nginx),确保
X-Forwarded-Proto和X-Forwarded-Host被正确传递,并在 Go 中用r.Header.Get("X-Forwarded-Proto")构造真实 redirect URL
用户信息获取别只依赖 userinfo 端点,优先从 id_token 解析
userinfo 端点需要额外 HTTP 请求,且部分 provider(如某些 Keycloak 配置)默认关闭该端点;而 id_token 是 JWT,已含 sub、email、name 等标准 claim,解码即可用。
但注意:id_token 的 payload 是 base64url 解码后的 JSON,不是原始 token 字符串;且某些字段(如 email_verified)可能缺失,不能假设总有值。
- 用
token.IDToken().Claims(&claims)解析结构体,别手动json.Unmarshalraw bytes - 字段映射要按 OIDC spec:例如
email是标准 claim,preferred_username才是用户名,sub才是唯一用户标识 - 如果 provider 返回非标准字段(如
x_user_role),需在 claims struct 中显式声明并启用json.RawMessage或自定义 unmarshal
最易被忽略的是 id_token 的签名算法兼容性:provider 可能用 RS256 或 ES256,go-oidc 默认支持前者,后者需确认 provider JWKS keys 是否暴露且 Go 客户端能正确解析 ECDSA key。











