Skip to content

fix(monitoring): publish the Grafana OIDC guide in v1.6 - #647

Open
myasnikovdaniil wants to merge 1 commit into
mainfrom
docs/grafana-oidc-v16
Open

fix(monitoring): publish the Grafana OIDC guide in v1.6#647
myasnikovdaniil wants to merge 1 commit into
mainfrom
docs/grafana-oidc-v16

Conversation

@myasnikovdaniil

@myasnikovdaniil myasnikovdaniil commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Guide was merged in #597 but written only into content/en/docs/next/. v1.6 was cut from the trunk 90 minutes earlier in #628, and next/ is excluded from production builds (hidden: true plus the content mount exclusion), so the page has never been reachable on cozystack.io even though it is merged for three weeks now.

This copies it into v1.6/ verbatim, so both copies stay byte identical and make init-next keeps behaving. weight 5 puts it between setup (2) and dashboards (10), and the section index lists children automatically so no index change needed.

I swept next/ against v1.6/ and this was the only page affected, otherwise the trees are identical in both directions.

Summary by CodeRabbit

  • Documentation
    • Added comprehensive guidance for configuring OIDC authentication on Grafana Monitoring instances.
    • Documented supported authentication modes, prerequisites, configuration examples, and sign-in flow.
    • Clarified audience isolation, tenant-group authorization, user reconciliation, role assignment, and break-glass administrator access.
    • Included operational caveats and functionality that is outside the supported scope.

The guide was merged in #597 but written only into content/en/docs/next/.
v1.6 had been cut from the trunk ninety minutes earlier in #628, and
next/ is excluded from production builds (hidden: true plus the content
mount exclusion), so the page has never been reachable on cozystack.io
despite being merged for three weeks.

Copy it into v1.6/ verbatim, so the two copies stay byte-identical and
`make init-next` keeps behaving. weight: 5 places it between setup (2)
and dashboards (10); the section index lists children automatically, so
no index change is needed.

A sweep of next/ against v1.6/ shows this was the only page affected —
the trees are otherwise identical in both directions.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@netlify

netlify Bot commented Aug 12, 2026

Copy link
Copy Markdown

Deploy Preview for cozystack ready!

Name Link
🔨 Latest commit db1fb52
🔍 Latest deploy log https://app.netlify.com/projects/cozystack/deploys/6a7c7df6c6103100088d4210
😎 Deploy Preview https://deploy-preview-647--cozystack.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Added documentation for OIDC authentication on Grafana Monitoring instances, including supported modes, configuration, provisioning, authorization, sign-in behavior, operational caveats, and unsupported functionality.

Changes

OIDC authentication documentation

Layer / File(s) Summary
Authentication model
content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md
Defines None, System, and CustomConfig modes, per-instance identity, tenant membership, and break-glass access.
Authentication mode configuration
content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md
Documents System and CustomConfig configuration, Keycloak provisioning, validation, Grafana OAuth settings, and OIDC prerequisites.
Authorization and operational behavior
content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md
Documents role assignment, tenant-scoped authorization, sign-in behavior, operational caveats, and unsupported functionality.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: kvaps

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes publishing the Grafana OIDC guide in the v1.6 documentation, which is the main change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/grafana-oidc-v16

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md`:
- Around line 82-90: Update the OIDC configuration example around
customConfig.config to warn that client_secret must not be stored inline in
production. Direct users to use secretRef or the documented Secret-backed
client-secret field, and place the warning next to the credential where it is
introduced.
- Line 10: The Monitoring documentation must clarify inherited-instance group
ownership. Update the section describing the namespace-scoped
<ns>-{view,use,admin,super-admin} groups to state that <ns> is the namespace
owning the Monitoring release; when a child tenant inherits its parent’s
instance, operators must use the parent namespace groups rather than creating
child-tenant groups.
- Around line 151-152: Update the `groups_attribute_path` guidance in the OIDC
authentication documentation to state that without it Grafana extracts an empty
group list and rejects logins when `allowed_groups` is configured. Remove the
inaccurate claim that `allowed_groups` becomes a silent no-op, while preserving
the existing configuration guidance.
- Around line 89-90: Update the OIDC configuration example and surrounding
documentation to identify groups as provider-specific rather than an OpenID
Connect Core scope. State that the identity provider must define the groups
scope or provide a group-claim mapper, and instruct operators to align scopes
and groups_attribute_path with their provider.
- Around line 62-65: Update the configuration example so
oauth_allow_insecure_email_lookup is rendered under the global [auth] section,
while keeping skip_org_role_sync and allow_sign_up under [auth.generic_oauth].
- Around line 62-68: Update the CustomConfig guidance for non-empty
spec.oidc.users so email-based OIDC account linking is permitted only with a
unique, validated, non-editable email claim from a trusted IdP; otherwise
configure identity mapping by (iss, sub) instead. Do not present
oauth_allow_insecure_email_lookup=true as sufficient based solely on
email_verified.
- Line 50: The OIDC users reconciliation documentation must cover the transition
from a non-empty users[] list to an empty list: describe how stale Grafana
memberships are cleaned up and how allow_sign_up remains false, or explicitly
document the required manual revocation and configuration steps. Update the
users-reconcile Job behavior description accordingly, preserving the existing
non-empty-list reconciliation details.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7cac756e-fac6-4d08-9c2b-95238f32b40d

📥 Commits

Reviewing files that changed from the base of the PR and between 1e21c76 and db1fb52.

📒 Files selected for processing (1)
  • content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md


Cozystack Grafana instances can authenticate users through OIDC instead of the shared `admin_user` / `admin_password` Secret. Each user then has their own identity, per-user audit, and a role that can be revoked by removing them from `spec.oidc.users` on the `Monitoring` CR.

The identity model is deliberately **per-instance** rather than per-tenant: every Monitoring instance (one inner `monitoring-system` release per namespace — each tenant's own, plus the platform's in `tenant-root`) gets its own OIDC audience, and a token minted for instance A is rejected by instance B's Grafana. Cross-tenant sign-in is additionally blocked by a `allowed_groups` gate on the release's namespace-scoped `<ns>-{view,use,admin,super-admin}` groups (chart-owned in the tenant chart; the platform-managed `groups` scope in the `cozy` realm makes them visible on every token). The full rationale is in the [design proposal](https://github.com/cozystack/community/pull/24). The tenant kube-apiserver's Phase 1 ([cozystack/cozystack#3044](https://github.com/cozystack/cozystack/pull/3044)) uses the same shape; this Grafana integration is the Phase-1 follow-up called out in that PR's body.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Clarify group ownership for inherited Monitoring instances.

The tenant guide states that a child tenant can use its parent tenant's Monitoring instance. In that case, <namespace>-view, <namespace>-use, <namespace>-admin, and <namespace>-super-admin refer to the namespace that owns the Monitoring release, not necessarily the child tenant. Add this inheritance case to prevent operators from creating groups that the inherited Monitoring instance does not check.

Also applies to: 117-117

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md`
at line 10, The Monitoring documentation must clarify inherited-instance group
ownership. Update the section describing the namespace-scoped
<ns>-{view,use,admin,super-admin} groups to state that <ns> is the namespace
owning the Monitoring release; when a child tenant inherits its parent’s
instance, operators must use the parent namespace groups rather than creating
child-tenant groups.

- A per-instance **`KeycloakClient`** in the `cozy` realm with `clientId` set to `<namespace>-<release>` (for the CR above: `tenant-acme-monitoring-system`). Confidential (`clientAuthenticatorType: client-secret`), `secret` sourced from a chart-owned Kubernetes Secret. `redirectUris` locked to `https://grafana.<host>/login/generic_oauth`.
- A per-instance **`KeycloakClientScope`** whose audience mapper pins the token's `aud` claim to that same `clientId` — the isolation primitive.
- A persistent Kubernetes **Secret** carrying the confidential `client-secret` (random on first install, preserved on upgrades).
- A chart-owned **users-reconcile Job** (`<release>-oidc-users`) that syncs `spec.oidc.users[]` into the Grafana instance on every reconcile: creates missing Grafana accounts, patches roles, and prunes stale org members. Runs as a post-install / post-upgrade hook when `users[]` is non-empty; omitted otherwise (BYO-IdP-friendly).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 8 \
  'users-reconcile|allow_sign_up|spec\.oidc\.users|prune|mode' .

Repository: cozystack/website

Length of output: 50374


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

file='content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md'
printf '%s\n' '--- target document ---'
sed -n '40,125p' "$file"

printf '%s\n' '--- related files and exact references ---'
rg -n -C 3 --glob '!**/public/**' --glob '!**/static/**' \
  'users-reconcile|oidc-users|allow_sign_up|spec\.oidc\.users|prune stale|revok' \
  content charts deploy config . 2>/dev/null | head -n 500 || true

printf '%s\n' '--- versioned copies ---'
fd -t f 'oidc-authentication\.md$' content

Repository: cozystack/website

Length of output: 50375


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

target = Path("content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md")
text = target.read_text()
for term in ("users-reconcile", "oidc-users", "allow_sign_up", "spec.oidc.users", "prune", "revok"):
    print(f"{term}: {len(re.findall(re.escape(term), text, flags=re.I))} matches in {target}")
PY

Repository: cozystack/website

Length of output: 758


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

file='content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md'
printf '%s\n' '--- relevant v1.6 statements ---'
sed -n '56,70p;111,118p;145,156p' "$file"

printf '%s\n' '--- repository implementation candidates ---'
git ls-files | rg -i '(^|/)(charts?|templates?|monitoring|grafana|oidc|helm|cozystack)(/|$)|users-reconcile|oidc-users'

Repository: cozystack/website

Length of output: 15373


🌐 Web query:

Grafana official documentation generic OAuth allow_sign_up default oauth_allow_insecure_email_lookup

💡 Result:

In Grafana's configuration for Generic OAuth, the allow_sign_up and oauth_allow_insecure_email_lookup settings serve distinct purposes as follows: allow_sign_up This setting controls whether new Grafana users are automatically created when they authenticate via Generic OAuth [1][2]. Default value: true [1][2]. Behavior: When set to true, Grafana creates a new user account upon a successful login if one does not already exist [1][2]. If set to false, only users who already have a pre-existing account in Grafana can log in using the OAuth provider [1][2]. oauth_allow_insecure_email_lookup This setting is a global configuration option (located in the [auth] section of the configuration file, rather than the [auth.generic_oauth] section) that allows Grafana to use the user's email address as a unique identifier to map OAuth identities to existing users [3]. Default value: false [3]. Behavior: It is intended for scenarios where you need to support users authenticating with the same email address across multiple identity providers (e.g., using both Google OAuth and GitHub OAuth simultaneously) [3]. Security Note: Grafana strongly recommends against enabling this option due to potential security risks [3]. If enabled, ensure that your identity providers are securely configured to prevent email spoofing or unauthorized account takeovers [3]. You can enable oauth_allow_insecure_email_lookup via the configuration file [3], the Grafana HTTP API [3], or through the Grafana UI under Administration -> Authentication -> Auth settings [3].

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

text = Path("content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md").read_text()

required = [
    "When `spec.oidc.users[]` is non-empty",
    "allow_sign_up                     = false",
    "The users-Job renders only when `spec.oidc.mode` is not `None` *and* `spec.oidc.users[]` is non-empty",
    "Removing a user from the list revokes their access on the next chart apply",
]
missing = [item for item in required if item not in text]
if missing:
    raise SystemExit(f"missing expected documentation statements: {missing}")

nonempty_start = text.index("When `spec.oidc.users[]` is non-empty")
nonempty_end = text.index("### Prerequisite", nonempty_start)
nonempty_section = text[nonempty_start:nonempty_end]

mode_start = text.index("- **Mode toggle is non-destructive.**")
mode_end = text.index("\n", text.index("survive untouched", mode_start)) + 1
mode_section = text[mode_start:mode_end]

assert "allow_sign_up                     = false" in nonempty_section
assert "users-Job renders only when" in mode_section
assert "spec.oidc.users[]` is non-empty" in mode_section
assert "runs no reconcile and no prune pass" in mode_section
assert "allow_sign_up" not in mode_section

print("Documented empty-list transition: no users-Job/prune pass and no documented forced allow_sign_up=false setting.")
PY

Repository: cozystack/website

Length of output: 267


Handle the transition to an empty users[] list.

When spec.oidc.users[] becomes empty, the users-Job is omitted, so stale Grafana org memberships are not pruned. The chart also stops forcing allow_sign_up=false; Grafana defaults this option to true, so an authorized OIDC identity can create a Viewer account again. Run cleanup on the empty-list transition and keep allow_sign_up=false, or document manual revocation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md`
at line 50, The OIDC users reconciliation documentation must cover the
transition from a non-empty users[] list to an empty list: describe how stale
Grafana memberships are cleaned up and how allow_sign_up remains false, or
explicitly document the required manual revocation and configuration steps.
Update the users-reconcile Job behavior description accordingly, preserving the
existing non-empty-list reconciliation details.

Source: MCP tools

Comment on lines +62 to +65
```ini
skip_org_role_sync = true
oauth_allow_insecure_email_lookup = true
allow_sign_up = false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 8 \
  'oauth_allow_insecure_email_lookup|auth\.generic_oauth|spec\.config' .

Repository: cozystack/website

Length of output: 50375


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- repository files near monitoring documentation ---'
git ls-files | rg '(^|/)(charts?|packages|templates|values|.*monitoring.*|.*grafana.*)' | head -200

printf '%s\n' '--- exact configuration references ---'
rg -n -C 5 \
  'oauth_allow_insecure_email_lookup|skip_org_role_sync|allow_sign_up|auth\.generic_oauth|spec\.config' \
  content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md \
  content/en/docs/v1.6/operations/services/monitoring/parameters.md

Repository: cozystack/website

Length of output: 27436


🌐 Web query:

Grafana oauth_allow_insecure_email_lookup configuration section official documentation

💡 Result:

The oauth_allow_insecure_email_lookup configuration option in Grafana determines whether the system uses a user's email address as a unique identifier for authentication, rather than the unique ID provided by the Identity Provider (IdP) [1][2]. This option is disabled by default [1][3]. Grafana strongly recommends against enabling this feature because it can lower the security of your Grafana instance and may expose it to potential account takeovers or authentication bypasses, as it was originally changed to address security vulnerabilities such as CVE-2023-3128 [1][4]. Key details regarding this configuration: Configuration To enable it, you must set the following in your Grafana configuration file: [auth] oauth_allow_insecure_email_lookup = true Alternatively, it can be enabled via: - API: Using the /api/admin/settings endpoint [1][5]. - UI: Navigating to Administration > Authentication > Auth settings [1][2]. Security Warning If you choose to enable this option, ensure that settings such as Allowed organization, Allowed groups, and Allowed domains are strictly configured to prevent unauthorized access [1][3]. Do not enable this if you are using multi-tenant applications (such as Azure AD in a multi-tenant configuration) [4]. Context This feature was introduced as an escape hatch in Grafana v10.0 following breaking changes that restricted OAuth integrations from relying solely on email lookups to improve security [4]. Since Grafana v10.4, this setting has also been configurable via the API for Grafana Enterprise and Grafana Cloud [1][5].

Citations:


Render oauth_allow_insecure_email_lookup in [auth].

Grafana reads this option only from the global [auth] section, not [auth.generic_oauth]. Keep skip_org_role_sync and allow_sign_up in [auth.generic_oauth].

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md`
around lines 62 - 65, Update the configuration example so
oauth_allow_insecure_email_lookup is rendered under the global [auth] section,
while keeping skip_org_role_sync and allow_sign_up under [auth.generic_oauth].

Source: MCP tools

Comment on lines +62 to +68
```ini
skip_org_role_sync = true
oauth_allow_insecure_email_lookup = true
allow_sign_up = false
```

`skip_org_role_sync=true` keeps a login from overwriting the users-Job's role assignments; `oauth_allow_insecure_email_lookup=true` lets Grafana attach the OIDC identity to the pre-provisioned local account by email; `allow_sign_up=false` is the isolation lever — without it, Grafana's default `allow_sign_up=true` combined with `skip_org_role_sync=true` would mint a Viewer account for every `cozy`-realm identity that hits the login flow.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
file='content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md'
printf '%s\n' '--- target file ---'
cat -n "$file"
printf '%s\n' '--- related identifiers and files ---'
rg -n --hidden -g '!node_modules' -g '!dist' \
  'oauth_allow_insecure_email_lookup|skip_org_role_sync|allow_sign_up|CustomConfig|users:|users\[\]|audience|email' \
  content | head -n 240
printf '%s\n' '--- matching versioned documents ---'
find content/en/docs -path '*/operations/services/monitoring/oidc-authentication.md' -print

Repository: cozystack/website

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- repository files related to monitoring OIDC ---'
git ls-files | rg -i 'monitoring|grafana|oidc|keycloak' | head -n 300
printf '%s\n' '--- implementation references ---'
rg -n --hidden -g '!content/**' -g '!node_modules' -g '!dist' \
  'oauth_allow_insecure_email_lookup|skip_org_role_sync|allow_sign_up|oidc\.users|users-reconcile|users-reconcile|email_verified|emailVerified|auth\.generic_oauth|CustomConfig' . \
  | head -n 300

Repository: cozystack/website

Length of output: 10615


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- repository files related to monitoring OIDC ---'
git ls-files | rg -i 'monitoring|grafana|oidc|keycloak' | head -n 300
printf '%s\n' '--- implementation references ---'
rg -n --hidden -g '!content/**' -g '!node_modules' -g '!dist' \
  'oauth_allow_insecure_email_lookup|skip_org_role_sync|allow_sign_up|oidc\.users|users-reconcile|email_verified|emailVerified|auth\.generic_oauth|CustomConfig' . \
  | head -n 300

Repository: cozystack/website

Length of output: 10615


🌐 Web query:

Grafana official documentation oauth_allow_insecure_email_lookup account takeover email lookup security

💡 Result:

The configuration setting oauth_allow_insecure_email_lookup is an "escape hatch" in Grafana designed to restore legacy behavior for user authentication, specifically allowing Grafana to identify users by their email address rather than relying exclusively on a unique ID provided by an identity provider (IdP) [1][2][3]. Security Risks and Context This setting is disabled by default because it introduces significant security risks, most notably account takeover and authentication bypass [1][2]. The primary vulnerabilities associated with relying on email lookups include: 1. Account Takeover and Authentication Bypass (CVE-2023-3128): Grafana previously validated accounts based on email claims [1][3]. In many IdP environments, such as multi-tenant Azure Active Directory (Azure AD) applications, email fields are not guaranteed to be unique and can sometimes be modified by users [1][4][5]. If an attacker can manipulate or use a non-unique email address that matches an existing Grafana user, they may be able to authenticate as that user, leading to a complete account takeover [1][5]. 2. Legacy Vulnerability (CVE-2022-31107): Prior to this, similar account takeover issues were identified where an attacker could link an external OAuth identity to an existing internal Grafana account by knowing the target user's username if the email or external ID was not already associated [6]. Official Recommendation Grafana Labs strongly recommends against enabling oauth_allow_insecure_email_lookup [2][7]. They advise that users ensure unique identification across identity providers by relying on the unique ID (e.g., the sub claim in OIDC) provided by the IdP rather than an email address [1][2]. If an organization absolutely must enable this setting, Grafana emphasizes that it should only be used when: - The identity provider is a single-tenant setup [2][7]. - The IdP provides unique, validated, and non-editable emails [2][7]. - Strict configuration of Allowed organization, Allowed groups, and Allowed domains is enforced to mitigate the increased risk of unauthorized access [2][8]. In summary, enabling oauth_allow_insecure_email_lookup reverts Grafana to a less secure authentication model that relies on mutable or non-unique email claims, thereby re-opening the application to potential account takeover vulnerabilities [1][3].

Citations:


🌐 Web query:

OpenID Connect Core official email_verified sub claim stable identifier email mutable

💡 Result:

In OpenID Connect (OIDC), the email address is not a stable identifier and must not be used as a primary key for identifying users [1][2][3][4]. The specification explicitly distinguishes between stable identifiers and mutable profile attributes: 1. The sub (Subject) Claim: This is the only claim that a Relying Party (RP) can rely on as a stable, unique identifier for an End-User at a specific Issuer [3]. The OpenID Connect Core specification mandates that the sub value must be locally unique and never reassigned within the Issuer for a particular End-User [3]. 2. The Email Claim: The email address is considered a mutable profile attribute, not a permanent identifier [2][3]. It can change over time, be recycled, or be reassigned by the Identity Provider (IdP) to different users [3][4]. Consequently, the specification states that the email claim must not be relied upon to be unique by the RP [5][3][4]. 3. Recommended Identifier Strategy: To uniquely and safely identify a user across federated services, RPs must use the combination of the issuer (iss) and subject (sub) claims [1][6][2][7]. The (iss, sub) pair provides a composite identifier that remains stable even if a user's email address, username, or other profile information changes [2][7]. 4. Email_Verified Claim: The email_verified claim is a boolean value indicating whether the IdP has verified that the email address belongs to the user [8][5][3]. It does not confer stability or uniqueness to the email address itself; it only indicates the verification status of that address at the time the ID Token was issued [3]. Using email as a primary key is considered a significant security anti-pattern (often called the False Identifier anti-pattern) that can lead to account takeover vulnerabilities and data merging issues if an email address is re-registered by a new user [6][2][4].

Citations:


Require a trusted email identity for CustomConfig.

When spec.oidc.users[] is non-empty, oauth_allow_insecure_email_lookup=true links the OIDC identity to a Grafana account by email. OIDC does not guarantee that email values are unique or stable, and email_verified only confirms verification at issuance. Require a unique, validated, non-editable email claim for BYO IdPs, or map users by (iss, sub) instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md`
around lines 62 - 68, Update the CustomConfig guidance for non-empty
spec.oidc.users so email-based OIDC account linking is permitted only with a
unique, validated, non-editable email claim from a trusted IdP; otherwise
configure identity mapping by (iss, sub) instead. Do not present
oauth_allow_insecure_email_lookup=true as sufficient based solely on
email_verified.

Source: MCP tools

Comment on lines +82 to +90
customConfig:
config:
enabled: "true"
client_id: my-grafana
client_secret: xxxxxxxx
auth_url: https://idp.acme.example/protocol/openid-connect/auth
token_url: https://idp.acme.example/protocol/openid-connect/token
api_url: https://idp.acme.example/protocol/openid-connect/userinfo
scopes: openid email profile groups

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Warn against storing client_secret in inline CR configuration.

The example places the OAuth client credential directly in spec.oidc.customConfig.config. Add a warning to use secretRef for production, or provide a Secret-backed client-secret field. The safer Secret path is described later but is not recommended where the credential is introduced.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md`
around lines 82 - 90, Update the OIDC configuration example around
customConfig.config to warn that client_secret must not be stored inline in
production. Direct users to use secretRef or the documented Secret-backed
client-secret field, and place the warning next to the credential where it is
introduced.

Comment on lines +89 to +90
api_url: https://idp.acme.example/protocol/openid-connect/userinfo
scopes: openid email profile groups

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

file='content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md'
printf '%s\n' '--- target file ---'
sed -n '1,180p' "$file"

printf '%s\n' '--- related OIDC scope references ---'
rg -n -i --glob '*.md' --glob '*.yaml' --glob '*.yml' \
  'groups|openid|userinfo|scope' content/en/docs/v1.6 content/en/docs/next 2>/dev/null \
  | head -n 250

Repository: cozystack/website

Length of output: 50375


🌐 Web query:

OpenID Connect Core 1.0 standard scopes openid profile email groups scope provider-specific claims official documentation

💡 Result:

In OpenID Connect (OIDC) Core 1.0, scopes are used by clients to request that specific sets of information (Claims) be returned [1][2]. Standard OIDC Scope Values: The OIDC specification defines several standard scope values that map to specific sets of Claims: - openid: REQUIRED. This scope value must be included in the Authorization Request to indicate that the request is an OpenID Connect request [1][3]. - profile: Requests access to the End-User's default profile Claims (e.g., name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at) [1][4]. - email: Requests access to the email and email_verified Claims [1][4]. - address: Requests access to the address Claim [1]. - phone: Requests access to the phone_number and phone_number_verified Claims [1]. Groups and Provider-Specific Claims: The OIDC Core 1.0 specification does not define a standard groups scope or a standard groups claim [1][4]. Because there is no universal standard for representing groups, this functionality is handled as a provider-specific implementation [5]. - Implementation: When a provider (such as Okta or others) supports a groups claim, they typically implement it by defining their own custom scope (e.g., groups) or by including the claim automatically when other scopes are requested [5]. - Discovery: Providers that support custom claims or non-standard scopes often advertise these through the OpenID Connect Discovery 1.0 mechanism [6]. By accessing the provider's discovery endpoint (typically located at.well-known/openid-configuration), developers can see which claims and scopes the specific provider supports [6]. In summary, while the core specification provides a framework for standard scopes, any functionality not explicitly defined—such as specific groups handling—is left to the discretion of the OpenID Provider [1][4]. Developers should consult the provider's specific documentation or their discovery document to identify available custom scopes and claims [6].

Citations:


Make groups provider-specific.

groups is not an OpenID Connect Core scope. Document that the identity provider must define this scope or configure a group-claim mapper. Operators must adjust scopes and groups_attribute_path to match that provider. ( )

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md`
around lines 89 - 90, Update the OIDC configuration example and surrounding
documentation to identify groups as provider-specific rather than an OpenID
Connect Core scope. State that the identity provider must define the groups
scope or provide a group-claim mapper, and instruct operators to align scopes
and groups_attribute_path with their provider.

Source: MCP tools

Comment on lines +151 to +152
- **`emailVerified: true` on Keycloak users.** `email_verified` is not enforced by Grafana: its `auth.generic_oauth` provider has no claim-validation option (claim handling is JMESPath only, and `role_attribute_path` is unused here because the users-Job drives roles). Enforce it on the Keycloak side instead: set `emailVerified: true` on the `KeycloakRealmUser` (or add the `VERIFY_EMAIL` required action / complete the email-verify flow in the Keycloak UI) so the identity holding a given email is guaranteed authentic. The `cozy` realm's default `duplicateEmails: false` additionally prevents a second account from claiming an already-registered address.
- **`groups_attribute_path` is not optional on Grafana v11.x+.** The chart wires it automatically for `System` mode; in `CustomConfig` inline the operator must add it explicitly (`groups_attribute_path: groups`) if their IdP emits a top-level `groups` array. Otherwise `allowed_groups` becomes a silent no-op and every login fails.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

file='content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md'
printf '%s\n' '--- target section ---'
sed -n '120,165p' "$file"

printf '%s\n' '--- references to group settings ---'
rg -n -C 3 'groups_attribute_path|allowed_groups|CustomConfig|System' content/en/docs/v1.6 content/en/docs/next 2>/dev/null || true

Repository: cozystack/website

Length of output: 50376


🌐 Web query:

Grafana Generic OAuth groups_attribute_path allowed_groups empty groups login rejected official documentation

💡 Result:

When using Generic OAuth in Grafana, configuring allowed_groups creates a strict requirement that mandates the user to be a member of at least one group defined in that list [1][2]. If Grafana fails to retrieve any groups for the user—or if the groups retrieved do not match any of the values in the allowed_groups list—the login will be rejected [1][3]. Key points regarding this behavior include: 1. Mandatory Dependency: If you configure allowed_groups, you are required to also configure groups_attribute_path [1][2]. 2. Resolution Order: Grafana attempts to evaluate the groups_attribute_path JMESPath expression sequentially against three sources: the OAuth2 ID token, the UserInfo endpoint response, and finally the OAuth2 access token [1][4]. It stops once it successfully resolves a group array [1]. 3. Rejection Cause: If the expression results in an empty list, null, or a set of groups that does not overlap with allowed_groups, Grafana denies the user access [1][5]. 4. Troubleshooting: - Ensure your OAuth provider is actually returning the groups claim in the ID token or UserInfo response [6]. - Verify that your JMESPath expression in groups_attribute_path correctly targets the path where groups are located in the JSON response [7][4]. - Inspect Grafana logs (at debug level) to see the raw JSON response received from the provider, which will help verify if the group data is present or empty [8][9]. If a user is being rejected unexpectedly, it is usually because the groups are either missing from the token/UserInfo response or the JMESPath expression is incorrectly configured and failing to extract the group array [1][8].

Citations:


Correct the allowed_groups failure description.

Without groups_attribute_path, Grafana extracts no groups. When allowed_groups is configured, Grafana rejects the login because the extracted group list is empty. It does not treat allowed_groups as a no-op.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@content/en/docs/v1.6/operations/services/monitoring/oidc-authentication.md`
around lines 151 - 152, Update the `groups_attribute_path` guidance in the OIDC
authentication documentation to state that without it Grafana extracts an empty
group list and rejects logins when `allowed_groups` is configured. Remove the
inaccurate claim that `allowed_groups` becomes a silent no-op, while preserving
the existing configuration guidance.

Source: MCP tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant