{"record":{"id":"7df7da41867f4741","repo":"bytebase/bytebase","slug":"subject-mismatch-expected-pattern-q-got-q","errorCode":null,"errorMessage":"subject mismatch: expected pattern %q, got %q","messagePattern":"subject mismatch: expected pattern %q, got %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/plugin/idp/wif/wif.go","lineNumber":69,"sourceCode":"\t\tclaims.Expiry = registeredClaims.Expiry.Time().Unix()\n\t}\n\tif registeredClaims.IssuedAt != nil {\n\t\tclaims.IssuedAt = registeredClaims.IssuedAt.Time().Unix()\n\t}\n\n\t// Validate issuer\n\tif claims.Issuer != config.IssuerUrl {\n\t\treturn nil, errors.Errorf(\"issuer mismatch: expected %q, got %q\", config.IssuerUrl, claims.Issuer)\n\t}\n\n\t// Validate audience (skip if no allowed audiences configured)\n\tif len(config.AllowedAudiences) > 0 && !validateAudience(claims.Audience, config.AllowedAudiences) {\n\t\treturn nil, errors.Errorf(\"audience mismatch: token has %v, allowed %v\", claims.Audience, config.AllowedAudiences)\n\t}\n\n\t// Validate subject pattern\n\tif !matchSubjectPattern(claims.Subject, config.SubjectPattern) {\n\t\treturn nil, errors.Errorf(\"subject mismatch: expected pattern %q, got %q\", config.SubjectPattern, claims.Subject)\n\t}\n\n\t// Validate expiry\n\tif time.Now().Unix() > claims.Expiry {\n\t\treturn nil, errors.New(\"token has expired\")\n\t}\n\n\treturn claims, nil\n}\n\nfunc validateAudience(tokenAudience []string, allowedAudiences []string) bool {\n\tfor _, allowed := range allowedAudiences {\n\t\tfor _, aud := range tokenAudience {\n\t\t\tif aud == allowed {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/plugin/idp/wif/wif.go#L51-L87","documentation":"ValidateToken in the WIF (workload identity federation) plugin rejects a token whose `sub` claim does not match the configured SubjectPattern regex. The pattern is an admin-defined allow-list for which federated identities may exchange tokens; a mismatch means the token's subject is outside that allow-list. The error message includes both the expected pattern and the actual subject for debugging.","triggerScenarios":"ExchangeToken -> ValidateToken is called with a WIF token whose claims.Subject fails matchSubjectPattern against config.SubjectPattern (non-nil pattern that does not regex-match the sub claim).","commonSituations":"Admin tightens the subject pattern after tokens were already issued; token comes from a different service account/identity than the pattern expects (e.g. pattern `^repo:org/app:.*` but token subject is `repo:org/other:ref`); pattern uses wrong regex syntax or wrong platform prefix (AWS ARN vs GCP service-account email); trailing/leading whitespace or casing differences in the sub claim.","solutions":["Print/inspect the token's `sub` claim (decode the JWT payload) and compare against config.SubjectPattern.","Update the WIF config SubjectPattern to match the legitimate subject format, or re-issue/obtain the token from the expected identity.","Test the regex (e.g. Go regexp.MatchString) against the actual subject outside the service to confirm the pattern is correct.","If the subject should never be restricted, clear/omit SubjectPattern so the check is skipped (matchSubjectPattern passes when pattern is empty)."],"exampleFix":"// before: pattern assumes GitHub Actions but token is from AWS\nconfig.SubjectPattern = `^repo:myorg/myapp:.*`\n// after: match the AWS ARN subject actually present in the token\nconfig.SubjectPattern = `^arn:aws:sts::123456789012:assumed-role/.+`","handlingStrategy":"validation","validationCode":"import \"regexp\"\nfunc subjectMatches(pattern, subject string) bool {\n    if pattern == \"\" { return true }\n    re, err := regexp.Compile(pattern)\n    return err == nil && re.MatchString(subject)\n}\n// call before ExchangeToken:\nif !subjectMatches(cfg.SubjectPattern, claims.Subject) { /* surface error or fix pattern */ }","typeGuard":"func hasValidSubject(tok *wif.Token, pattern string) bool {\n    return tok != nil && tok.Claims != nil && subjectMatches(pattern, tok.Claims.Subject)\n}","tryCatchPattern":null,"preventionTips":["Unit-test SubjectPattern against real token subjects from each identity provider you federate.","Log claims.Subject (not the full token) on failure to speed debugging.","Prefer broad patterns initially, tighten gradually after confirming subject formats."],"tags":["auth","oidc","regex","token-validation"],"backgroundTag":"invalid-config-value","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}