auth: add AllowMissingExpiration option to RequireBearerTokenOptions (#971)
## Summary - Add an `AllowMissingExpiration bool` field to `RequireBearerTokenOptions` that opts the middleware out of the existing `tokenInfo.Expiration.IsZero()` reject. - Default false preserves the existing strict behaviour: every TokenInfo must carry an Expiration. ## Motivation Some IdPs emit session-bound bearer tokens that do not carry a standalone `exp` claim — the token's lifetime is bounded by an external session and is not advertised in-band. Resource servers integrating with such IdPs today have to synthesise a fake Expiration from their TokenVerifier callback (dishonest data through the auth layer) or fork `RequireBearerToken` to bypass the check. This option lets the verifier honestly report "no expiration in the token" via a zero `Expiration` and have the middleware accept it — while still applying the elapsed-validity check for tokens that DO carry an Expiration. 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
ad054d3c5e
commit
d54751a8ee
2 changed files with 24 additions and 3 deletions
22
auth/auth.go
22
auth/auth.go
|
|
@ -47,6 +47,21 @@ type RequireBearerTokenOptions struct {
|
|||
ResourceMetadataURL string
|
||||
// The required scopes.
|
||||
Scopes []string
|
||||
// AllowMissingExpiration opts the middleware out of the
|
||||
// `tokenInfo.Expiration.IsZero()` reject. Default false preserves the
|
||||
// existing strict behaviour (every TokenInfo must carry an Expiration).
|
||||
//
|
||||
// Some IdPs emit session-bound bearer tokens that do not carry a standalone
|
||||
// `exp` claim — the token's lifetime is bounded by an external session and
|
||||
// is not advertised in-band. Resource servers integrating with such IdPs
|
||||
// need to opt in to validating the rest of the token (scopes, signature
|
||||
// via the verifier callback, etc.) without requiring the expiration field
|
||||
// to be present.
|
||||
//
|
||||
// When enabled, the verifier is still responsible for any session-level
|
||||
// validity check it can perform; this option only relaxes the middleware's
|
||||
// own expiration enforcement.
|
||||
AllowMissingExpiration bool
|
||||
}
|
||||
|
||||
type tokenInfoKey struct{}
|
||||
|
|
@ -131,9 +146,10 @@ func verify(req *http.Request, verifier TokenVerifier, opts *RequireBearerTokenO
|
|||
|
||||
// Check expiration.
|
||||
if tokenInfo.Expiration.IsZero() {
|
||||
return nil, "token missing expiration", http.StatusUnauthorized
|
||||
}
|
||||
if tokenInfo.Expiration.Before(time.Now()) {
|
||||
if opts == nil || !opts.AllowMissingExpiration {
|
||||
return nil, "token missing expiration", http.StatusUnauthorized
|
||||
}
|
||||
} else if tokenInfo.Expiration.Before(time.Now()) {
|
||||
return nil, "token expired", http.StatusUnauthorized
|
||||
}
|
||||
return tokenInfo, "", 0
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ func TestVerify(t *testing.T) {
|
|||
"no expiration", nil, "Bearer noexp",
|
||||
"token missing expiration", 401,
|
||||
},
|
||||
{
|
||||
"no expiration with AllowMissingExpiration accepts",
|
||||
&RequireBearerTokenOptions{AllowMissingExpiration: true}, "Bearer noexp",
|
||||
"", 0,
|
||||
},
|
||||
{
|
||||
"expired", nil, "Bearer expired",
|
||||
"token expired", 401,
|
||||
|
|
|
|||
Loading…
Reference in a new issue