Problem
Starting with vmauth v1.147.0, JWT users are only processed when the token contains a vm_access claim or the user config defines default_vm_access_claim:
// app/vmauth/main.go (v1.147.0)
if tkn.HasVMAccessClaim() || ui.JWT.DefaultVMAccessClaim != nil {
processUserRequest(w, r, ui, tkn)
return true
}
If neither is present, a valid JWT that matches match_claims and passes OIDC verification still falls through to unauthorized_user / 401 Unauthorized. Metrics show vmauth_user_requests_total{username="jwt"}=0 and vmauth_http_request_errors_total{reason="invalid_auth_token"} increasing.
This is documented in vmauth v1.147.0:
For setups that use JWT claim matching only (e.g. sub + custom claims) without vm_access in the IdP token and without URL templating placeholders, the intended workaround is an empty default claim:
users:
- jwt:
default_vm_access_claim: {}
match_claims:
sub: admin
claims: some-role
oidc:
issuer: https://idp.example.com
url_map:
- src_paths: ["/prometheus/.*"]
url_prefix: ["http://vmselect:8481"]
Gap in the operator
VMUser.spec.jwt (v0.72.0 and v0.73.0) exposes:
matchClaims
oidc
publicKeys / publicKeyRefs
skipVerify
There is no CRD field for default_vm_access_claim, so operator-managed VMAuth configs cannot set it. Users must either:
- Patch vmauth config manually via
externalConfig (loses operator-managed VMUser sync), or
- Add
vm_access: {} in the IdP (not always possible).
Use case
Grafana with oauthPassThru: true forwarding OIDC access tokens to vmauth. Tokens contain sub, claims[], iss, etc., but no vm_access. VMUser is configured with jwt.oidc + jwt.matchClaims and targetRefs to VMCluster/vmselect (no JWT placeholders in URLs).
Request
Add support for defaultVMAccessClaim (or default_vm_access_claim in generated yaml) under VMUser.spec.jwt, mapping to vmauth’s jwt.default_vm_access_claim.
Suggested API shape (open to discussion):
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMUser
metadata:
name: example
spec:
jwt:
defaultVMAccessClaim: {}
oidc:
issuer: https://idp.example.com
matchClaims:
sub: admin
claims: admin-role
targetRefs:
- crd:
kind: VMCluster/vmselect
name: metrics
namespace: monitoring
paths: ["/prometheus/.*"]
Optional fields inside defaultVMAccessClaim should mirror vmauth’s VMAccessClaim (metrics/logs account/project IDs, extra filters/labels) for templating use cases.
Environment
- VictoriaMetrics operator: v0.72.0 / v0.73.0
- vmauth: v1.147.0
- Auth: OIDC (
jwt.oidc.issuer) + jwt.matchClaims
- No
vm_access claim in JWT from IdP
Expected behavior
Operator generates vmauth config with default_vm_access_claim when set on VMUser, enabling claim-match routing without requiring vm_access in the token.
Actual behavior
Generated config has jwt.match_claims + jwt.oidc only; valid JWTs are rejected with 401.
Problem
Starting with vmauth v1.147.0, JWT users are only processed when the token contains a
vm_accessclaim or the user config definesdefault_vm_access_claim:If neither is present, a valid JWT that matches
match_claimsand passes OIDC verification still falls through tounauthorized_user/401 Unauthorized. Metrics showvmauth_user_requests_total{username="jwt"}=0andvmauth_http_request_errors_total{reason="invalid_auth_token"}increasing.This is documented in vmauth v1.147.0:
For setups that use JWT claim matching only (e.g.
sub+ custom claims) withoutvm_accessin the IdP token and without URL templating placeholders, the intended workaround is an empty default claim:Gap in the operator
VMUser.spec.jwt(v0.72.0 and v0.73.0) exposes:matchClaimsoidcpublicKeys/publicKeyRefsskipVerifyThere is no CRD field for
default_vm_access_claim, so operator-managed VMAuth configs cannot set it. Users must either:externalConfig(loses operator-managed VMUser sync), orvm_access: {}in the IdP (not always possible).Use case
Grafana with
oauthPassThru: trueforwarding OIDC access tokens to vmauth. Tokens containsub,claims[],iss, etc., but novm_access. VMUser is configured withjwt.oidc+jwt.matchClaimsandtargetRefstoVMCluster/vmselect(no JWT placeholders in URLs).Request
Add support for
defaultVMAccessClaim(ordefault_vm_access_claimin generated yaml) underVMUser.spec.jwt, mapping to vmauth’sjwt.default_vm_access_claim.Suggested API shape (open to discussion):
Optional fields inside
defaultVMAccessClaimshould mirror vmauth’sVMAccessClaim(metrics/logs account/project IDs, extra filters/labels) for templating use cases.Environment
jwt.oidc.issuer) +jwt.matchClaimsvm_accessclaim in JWT from IdPExpected behavior
Operator generates vmauth config with
default_vm_access_claimwhen set on VMUser, enabling claim-match routing without requiringvm_accessin the token.Actual behavior
Generated config has
jwt.match_claims+jwt.oidconly; valid JWTs are rejected with 401.