siyuan-note/siyuan · warning

OIDC claim [ ] is not allowed

Error message

OIDC claim [%s] is not allowed

What it means

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.

Solutions

  1. Add the user's actual claim value to the allowed values list in the OIDC auth settings, or remove/loosen the rule
  2. 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')
  3. Check the user's group/role assignment at the identity provider and have them re-login to get a fresh token
  4. Temporarily enable claim logging/debugging to see the token's claims and compare with configured rules

Example fix

// before
rule: {Claim: "groups", Values: ["admins"]}  // user has ["developers"]
// after
rule: {Claim: "groups", Values: ["admins", "developers"]}
Defensive patterns

Strategy: validation

Validate before calling

// decode the ID token client-side and check required claims before login
decoded := decodeJWT(idToken)
for _, rule := range configuredRules {
    if !containsAny(toStringSlice(decoded[rule.Claim]), rule.Values) { return fmt.Errorf("claim %s not satisfied", rule.Claim) }
}

Type guard

func claimAllowed(claims map[string]any, claim string, allowed []string) bool {
    v, ok := claims[claim]
    if !ok { return false }
    for _, s := range oidcClaimValues(v) { for _, a := range allowed { if s == a { return true } } }
    return false
}

Try / catch

if err := model.OIDCCallback(c, code); err != nil && strings.Contains(err.Error(), "is not allowed") {
    renderAuthError(c, "Your account does not have access: missing required attribute " + extractClaim(err))
}

Prevention

When it happens

Trigger: 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].

Common situations: 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.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/f0171dc2db847b69. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/oidc.go:911

		matched := false
		for _, claimValue := range claimValues {
			for _, allowedValue := range rule.Values {
				switch rule.Operator {
				case conf.OIDCClaimOperatorEquals:
					matched = claimValue == allowedValue
				case conf.OIDCClaimOperatorContains:
					matched = strings.Contains(claimValue, allowedValue)
				}
				if matched {
					break
				}
			}
			if matched {
				break
			}
		}
		if !matched {
			return fmt.Errorf("OIDC claim [%s] is not allowed", rule.Claim)
		}
	}
	return nil
}

func oidcClaimValues(value any) []string {
	switch typed := value.(type) {
	case string:
		return []string{typed}
	case bool, float64, float32, int, int64, json.Number:
		return []string{fmt.Sprint(typed)}
	case []string:
		return typed
	case []any:
		ret := make([]string, 0, len(typed))
		for _, item := range typed {
			values := oidcClaimValues(item)
			if len(values) == 1 {

View on GitHub (pinned to 9f775e8a12)