{"record":{"id":"f0171dc2db847b69","repo":"siyuan-note/siyuan","slug":"oidc-claim-s-is-not-allowed","errorCode":null,"errorMessage":"OIDC claim [%s] is not allowed","messagePattern":"OIDC claim \\[(.+?)\\] is not allowed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/oidc.go","lineNumber":911,"sourceCode":"\t\tmatched := false\n\t\tfor _, claimValue := range claimValues {\n\t\t\tfor _, allowedValue := range rule.Values {\n\t\t\t\tswitch rule.Operator {\n\t\t\t\tcase conf.OIDCClaimOperatorEquals:\n\t\t\t\t\tmatched = claimValue == allowedValue\n\t\t\t\tcase conf.OIDCClaimOperatorContains:\n\t\t\t\t\tmatched = strings.Contains(claimValue, allowedValue)\n\t\t\t\t}\n\t\t\t\tif matched {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif matched {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !matched {\n\t\t\treturn fmt.Errorf(\"OIDC claim [%s] is not allowed\", rule.Claim)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc oidcClaimValues(value any) []string {\n\tswitch typed := value.(type) {\n\tcase string:\n\t\treturn []string{typed}\n\tcase bool, float64, float32, int, int64, json.Number:\n\t\treturn []string{fmt.Sprint(typed)}\n\tcase []string:\n\t\treturn typed\n\tcase []any:\n\t\tret := make([]string, 0, len(typed))\n\t\tfor _, item := range typed {\n\t\t\tvalues := oidcClaimValues(item)\n\t\t\tif len(values) == 1 {","sourceCodeStart":893,"sourceCodeEnd":929,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/oidc.go#L893-L929","documentation":"authorizeOIDCClaims enforces admin-configured claim rules: for each rule, the ID token's claim values must match one of the allowed values (rules combined per AND semantics, as tested by TestAuthorizeOIDCClaimsCombinesRulesWithAnd). If a required claim's values contain none of the allowed entries, the token is rejected with this error naming the offending claim.","triggerScenarios":"A user whose ID token lacks one of the allowed values for a configured claim rule logs in or completes validation via finishOIDCExchange → authorizeOIDCClaims. E.g. rule claim=groups allowed=[admins] but the user's groups are [devs].","commonSituations":"User not assigned to the required group/role at the IdP; claim name typo in settings (e.g. 'Groups' vs 'groups'); IdP emits claim as a single string while rules expect a list (handled by oidcClaimValues) or vice versa; IdP omits the claim entirely for that user.","solutions":["Add the user's actual claim value to the allowed values list in the OIDC auth settings, or remove/loosen the rule","Verify the claim name against a real ID token (decode it at jwt.io) — check exact casing and nesting (e.g. 'roles' vs 'https://.../roles')","Check the user's group/role assignment at the identity provider and have them re-login to get a fresh token","Temporarily enable claim logging/debugging to see the token's claims and compare with configured rules"],"exampleFix":"// before\nrule: {Claim: \"groups\", Values: [\"admins\"]}  // user has [\"developers\"]\n// after\nrule: {Claim: \"groups\", Values: [\"admins\", \"developers\"]}","handlingStrategy":"validation","validationCode":"// decode the ID token client-side and check required claims before login\ndecoded := decodeJWT(idToken)\nfor _, rule := range configuredRules {\n    if !containsAny(toStringSlice(decoded[rule.Claim]), rule.Values) { return fmt.Errorf(\"claim %s not satisfied\", rule.Claim) }\n}","typeGuard":"func claimAllowed(claims map[string]any, claim string, allowed []string) bool {\n    v, ok := claims[claim]\n    if !ok { return false }\n    for _, s := range oidcClaimValues(v) { for _, a := range allowed { if s == a { return true } } }\n    return false\n}","tryCatchPattern":"if err := model.OIDCCallback(c, code); err != nil && strings.Contains(err.Error(), \"is not allowed\") {\n    renderAuthError(c, \"Your account does not have access: missing required attribute \" + extractClaim(err))\n}","preventionTips":["Copy claim names exactly from a real decoded ID token, including namespace prefixes","Confirm user group/role assignment at the IdP before granting access expectations","Keep the allowed-values list current when teams change","Test rules with a test account from each access tier"],"tags":["oidc","authorization","claims","access-control"],"backgroundTag":"permission-denied","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}