OIDC proxy does not enforce scope claims from incoming JWTs (--oidc-scopes only advertises, not validates)
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 2.2k
- Forks
- 300
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 184
Description
Summary
The --oidc-scopes flag on thv run is documented (and named) to suggest it enforces required scopes on incoming JWT tokens. In practice it only advertises scopes in the OAuth2 discovery document (RFC 9728 /.well-known/oauth-protected-resource) and in the WWW-Authenticate response header. It does not validate that incoming tokens carry those scopes.
Expected behaviour
When a proxy is started with --oidc-scopes required-scope, any JWT that does not contain required-scope in its scope claim should be rejected with 401 Unauthorized (or 403 Forbidden).
Actual behaviour
validateClaims in pkg/auth/token.go only checks issuer, audience, and expiry. The v.scopes field is never consulted during token validation — it is only used to populate:
- The
scopefield in theWWW-Authenticateresponse header (buildWWWAuthenticate) - The
scopes_supportedfield in the OAuth2 resource metadata document (NewAuthInfoHandler)
Any token with valid issuer, audience, and expiry is accepted regardless of its scope claim.
Impact
A downstream application that depends on thv enforcing OAuth scopes for access control is silently unprotected. Tokens issued without the required scope are accepted as if they were fully authorized.
Relevant code
pkg/auth/token.go — validateClaims function:
func (v *TokenValidator) validateClaims(claims jwt.MapClaims) error {
// Validates: issuer, audience, expiry only
// v.scopes is never checked here
...
return nil
}
Suggested fix
Add scope claim validation to validateClaims:
if len(v.scopes) > 0 {
scopeClaim, _ := claims["scope"].(string)
tokenScopes := strings.Fields(scopeClaim)
for _, required := range v.scopes {
found := slices.Contains(tokenScopes, required)
if !found {
return ErrInsufficientScope
}
}
}
Discovery context
Found while implementing DAST adversarial tests for scope validation in the enterprise platform. The test (missing_scope in the token variation matrix) cannot be automated end-to-end because the proxy accepts all valid OIDC tokens regardless of scope.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in pkg/auth/token.go at TokenValidator.validateClaims and review how issuer, audience, and expiry are currently checked. Use the missing_scope case from the token variation matrix as the acceptance scenario; done means tokens lacking any configured scope are rejected while valid tokens with the required scopes continue to pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- authentication, authorization, security
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100