auth: add ClockSkew option to RequireBearerTokenOptions (#969)
## Summary - Add a \`ClockSkew time.Duration\` field to \`RequireBearerTokenOptions\` that bounds the tolerance applied to a token's \`Expiration\` when checking elapsed validity. - Default zero preserves the existing strict comparison; positive values let resource servers tolerate the drift typical of distributed deployments and cloud-managed IdPs. ## Motivation Real-world resource servers running behind a CDN, in distributed deployments, or talking to an authorization server whose clock drifts a few seconds (common with cloud-managed IdPs) need a small positive tolerance when checking token expiration. Strict-equality comparison rejects tokens that are valid by the issuer's clock but momentarily appear expired by the verifier's. The reverse case — an issuer's clock running slightly fast at /token issuance — also reaches the verifier as a token whose \`exp\` lies microseconds in the past. ## Test plan - [x] \`go test ./auth/...\` passes. - [x] New \`TestRequireBearerToken_ClockSkew\` exercises four cases: fresh accept, strict-expired reject, within-skew accept, beyond-skew reject. - [x] Existing \`TestRequireBearerToken\` continues to pass with \`ClockSkew\` defaulting to zero. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
This commit is contained in:
parent
c60a318d22
commit
5045d86fb4
1 changed files with 81 additions and 3 deletions
24
auth/auth.go
24
auth/auth.go
|
|
@ -62,6 +62,19 @@ type RequireBearerTokenOptions struct {
|
|||
// validity check it can perform; this option only relaxes the middleware's
|
||||
// own expiration enforcement.
|
||||
AllowMissingExpiration bool
|
||||
// ClockSkew bounds the tolerance applied to a token's Expiration when
|
||||
// deciding whether it has elapsed. A token is rejected only if
|
||||
// Expiration + ClockSkew is before the current time. Zero (the default)
|
||||
// preserves strict comparison: any expired token is rejected immediately.
|
||||
//
|
||||
// Resource servers running behind a CDN, in distributed deployments, or
|
||||
// communicating with an authorization server whose clock drifts a few
|
||||
// seconds (common with cloud-managed IdPs) need a small positive value
|
||||
// here to avoid rejecting tokens that are valid by the issuer's clock
|
||||
// but momentarily appear expired by the verifier's. The same tolerance
|
||||
// guards against an issuer's clock running slightly fast at /token
|
||||
// issuance time.
|
||||
ClockSkew time.Duration
|
||||
}
|
||||
|
||||
type tokenInfoKey struct{}
|
||||
|
|
@ -144,12 +157,17 @@ func verify(req *http.Request, verifier TokenVerifier, opts *RequireBearerTokenO
|
|||
}
|
||||
}
|
||||
|
||||
// Check expiration.
|
||||
if opts == nil {
|
||||
opts = &RequireBearerTokenOptions{}
|
||||
}
|
||||
// Check expiration, with optional clock-skew tolerance. Skew only applies
|
||||
// when an expiration is present; a missing expiration is governed solely by
|
||||
// AllowMissingExpiration.
|
||||
if tokenInfo.Expiration.IsZero() {
|
||||
if opts == nil || !opts.AllowMissingExpiration {
|
||||
if !opts.AllowMissingExpiration {
|
||||
return nil, "token missing expiration", http.StatusUnauthorized
|
||||
}
|
||||
} else if tokenInfo.Expiration.Before(time.Now()) {
|
||||
} else if tokenInfo.Expiration.Add(opts.ClockSkew).Before(time.Now()) {
|
||||
return nil, "token expired", http.StatusUnauthorized
|
||||
}
|
||||
return tokenInfo, "", 0
|
||||
|
|
|
|||
Loading…
Reference in a new issue