Documentation
¶
Overview ¶
Package mcpauth 是面向资源服务器(OAuth Resource Server)的 RS256 JWT 验签库。
它只做一件事:校验一枚由授权服务器签发的 Bearer JWT —— 按 kid 从授权服务器的 JWKS 端点选公钥验签名 → iss 精确匹配 → aud 精确匹配 → exp/nbf(±clock skew)。除此之外**不做任何 OAuth 行为**:不发起 discovery、 不重定向、不返回 WWW-Authenticate 这类会诱发客户端启动 OAuth 流程的响应头 (理由见 WriteUnauthorized),失败一律 HTTP 401 + 机器可读错误码。
默认取值一律选最严的一侧:算法锁定 RS256、aud 必须恰为单值、exp 必须存在、 kid 缺失不做兜底。这几条都**不是** golang-jwt 的默认行为,而漏掉任意一条 都是一个可伪造 token 的洞——各自的攻击场景写在对应的行内注释里。
生产考量:JWKS 拉取带 singleflight 合流、最小间隔限流与响应体积上限; 启动预热可选(Prime);观测通过回调注入(Config.OnResult),不绑定任何指标栈。
Index ¶
Constants ¶
const ( CodeMissingToken = "missing_token" // 无 Authorization 头或非 Bearer 形态 CodeInvalidToken = "invalid_token" // 签名/iss/aud/结构等一切非过期类失败 CodeExpiredToken = "expired_token" // 过期(含 nbf 未生效),单列便于发现时钟/TTL 问题 )
错误码取值 —— 401 响应体 {"error": "<code>"} 的机器可读部分。 客户端对任何 401 的处置通常都一样(就地刷新 token 重试一次),错误码只服务于诊断与指标; 但状态码必须是 401:回 403 或 500 会让客户端的自愈路径失灵,把一次本可静默 恢复的过期变成一次用户可见的失败。
Variables ¶
This section is empty.
Functions ¶
func WriteUnauthorized ¶
func WriteUnauthorized(w http.ResponseWriter, code string)
WriteUnauthorized 写出统一的 401 响应:{"error":"<code>"}。
刻意**不设置** WWW-Authenticate:RFC 6750 的挑战头会诱发部分客户端就地启动 OAuth discovery 流程,去本服务上找授权服务器元数据——而本库面向的是 「授权收敛在独立授权服务器、资源服务器只验签」的部署形态,资源服务器不该 暴露任何会把客户端引向自己的 OAuth 线索。需要挑战头的场景请自行包一层。
状态码恒为 401:客户端的「刷新 token 重试一次」自愈路径通常只挂在 401 上, 回 403 或 500 会让它直接放弃。
Types ¶
type AuthError ¶
AuthError 是一次验签失败的结论。
Code 是有界枚举(进 401 响应体与指标标签);Reason 是内部诊断文案, 只进日志——它可能携带「哪一步失败」这类对攻击者有价值的细节,绝不外发。
type Claims ¶
type Claims struct {
jwt.RegisteredClaims
ClientID string `json:"client_id"`
Name string `json:"name"`
}
Claims 是验签通过后暴露给业务层的载荷子集。
Name 仅供日志展示,**不得用于授权判定**:它是签发方填的展示名,可变、不唯一, 也不参与签名之外的任何约束。要标识主体请用 Subject。
type Config ¶
type Config struct {
// JWKSURL 是授权服务器的 JWK Set 端点(RFC 7517)。留空时按惯例由 Issuer 派生为
// {issuer}/.well-known/jwks.json;授权服务器若把 JWKS 挂在别处,显式填这个字段。
JWKSURL string
// Issuer 必须与 token 的 iss 声明逐字一致,精确匹配,不做前缀/后缀宽容。
Issuer string
// Audience 必须与 token 的 aud 声明逐字一致。本库要求 aud 恰为单值(见 verify)。
Audience string
// ClockSkew 是 exp/nbf 的容忍偏差,零值取 60s。
ClockSkew time.Duration
// MinRefreshInterval 是「未知 kid 触发 JWKS 重取」的最小间隔,零值取 10s。
// 没有它,一个伪造 kid 的请求流就能把授权服务器的 JWKS 端点打成 DDoS 放大器
// ——而且这个放大器是**免鉴权**的:伪造 kid 的请求根本不需要一枚有效 token。
MinRefreshInterval time.Duration
// HTTPTimeout 是拉取 JWKS 的超时,零值取 5s。
HTTPTimeout time.Duration
// OnResult 是可选观测钩子:每次验签结束回调一次,result 为 "ok" 或错误码。
// 用回调而不是直接依赖某个指标库,调用方引用本包时不被迫对齐指标栈。
// result 取值有界(只可能是 "ok" 或本文件定义的三个错误码),可直接用作指标标签。
OnResult func(result string)
}
Config 是验签器配置。
Issuer / Audience 刻意**没有默认值**:这两个值定义了信任域,任何「猜一个」 的默认值都可能让 token 在错误的信任域下被判为有效。缺失即构造失败, 把配置错误挡在启动期而不是运行期。取值必须逐字抄自授权服务器发布的契约。
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier 按 kid 从授权服务器的 JWKS 取公钥验签。并发安全,可长期复用。
func (*Verifier) Middleware ¶
func (v *Verifier) Middleware(onDenied func(r *http.Request, authErr *AuthError)) func(http.Handler) http.Handler
Middleware 返回标准 net/http 中间件:验签失败一律 401,成功把 Claims 注入 context。
onDenied 可选(可传 nil),在写出 401 前回调,供服务侧记结构化日志—— Reason 只该出现在日志里,所以由这里回调而不是写进响应。