Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Grafana By James Joyner IV · · 9 min read

Grafana Error Guide: 'login.OAuthLogin(...)' Failed / User Sync Error — Fix OAuth SSO

Fix Grafana OAuth login failed and user sync errors: diagnose bad redirect URI, token/userinfo failures, missing email, role mapping, and allowed-domain/org restrictions.

  • #grafana
  • #troubleshooting
  • #errors
  • #oauth

Overview

Grafana’s generic OAuth and provider integrations (Google, GitHub, Azure AD, Okta, Keycloak) exchange an authorization code for a token, fetch the user’s profile, then create or update the Grafana user (“user sync”). A failure anywhere in that chain redirects back to the login page with an error, and the log carries a login.OAuthLogin(...) entry pinpointing the step.

The literal errors you will see:

login.OAuthLogin(NewTransportWithCode)
login.OAuthLogin(get info from token)
User sync failed: "email not found in OAuth response"

The parenthetical is the exact failing stage: NewTransportWithCode (code→token exchange), get info from token (userinfo/JWT), or a downstream user-sync/role-mapping error.

Symptoms

  • Clicking “Sign in with ” bounces back to login with a red banner.
  • The log shows login.OAuthLogin(...) with a specific stage in parentheses.
  • Some users log in but others are rejected (domain/org/role restrictions).
  • SSO worked until a redirect URI, secret, or provider setting changed.

Common Root Causes

1. Redirect URI / root_url mismatch

Grafana’s root_url (and thus its callback /login/generic_oauth) does not match the redirect URI registered with the provider, so the code exchange fails.

2. Wrong client ID/secret or expired secret

A rotated or mistyped client_secret makes NewTransportWithCode fail during token exchange.

3. Userinfo/JWT missing required claims

Grafana cannot find email (or the configured attribute) in the userinfo response, failing user sync.

4. Role/org mapping rejects the user

role_attribute_strict = true with no matching role, or an org_mapping/allowed_groups that excludes the user, denies login.

5. Allowed domains/organizations restriction

allowed_domains or allowed_organizations (GitHub) filters the user out.

Diagnostic Workflow

Step 1: Read the exact failing stage

sudo journalctl -u grafana-server --no-pager | grep -iE "oauth|OAuthLogin|user sync|generic_oauth" | tail -30
kubectl logs deploy/grafana -n monitoring | grep -iE "oauth|OAuthLogin|sync" | tail -30
grep -iE "oauth|OAuthLogin" /var/log/grafana/grafana.log | tail -30

Enable debug logging for the auth logger if the reason is unclear:

# grafana.ini
[log]
level = debug
filters = oauth:debug

Step 2: Verify root_url and redirect URI

# grafana.ini
[server]
root_url = https://grafana.example.com/

[auth.generic_oauth]
enabled = true
client_id = grafana
client_secret = ${OAUTH_CLIENT_SECRET}
scopes = openid email profile
auth_url = https://idp.example.com/authorize
token_url = https://idp.example.com/token
api_url = https://idp.example.com/userinfo

The provider must list https://grafana.example.com/login/generic_oauth as an authorized redirect URI exactly.

Step 3: Inspect the token and userinfo endpoints

# Confirm the userinfo endpoint returns an 'email' claim
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" https://idp.example.com/userinfo | jq

If email is absent, add the email scope or map the correct attribute:

# grafana.ini
[auth.generic_oauth]
email_attribute_path = email
login_attribute_path = preferred_username
name_attribute_path = name

Step 4: Check role and org mapping

# grafana.ini
[auth.generic_oauth]
role_attribute_path = contains(roles[*], 'grafana-admin') && 'Admin' || 'Viewer'
role_attribute_strict = false     # true rejects users with no matching role
allowed_domains = example.com

role_attribute_strict = true is a common cause of “user sync failed” when the JMESPath yields nothing.

Example Root Cause Analysis

After migrating Grafana behind a new hostname, SSO breaks with:

logger=oauth.generic_oauth error="login.OAuthLogin(NewTransportWithCode)" ... oauth2: cannot fetch token: 400 Bad Request ... redirect_uri_mismatch

The NewTransportWithCode stage plus redirect_uri_mismatch shows the code-for-token exchange is being rejected because the callback URL does not match. Grafana’s root_url was updated to the new hostname, but the identity provider still lists the old redirect URI.

Fix: register https://grafana.example.com/login/generic_oauth in the IdP’s allowed redirect URIs (and remove the stale one), keeping root_url in sync. SSO succeeds. Root cause: a redirect-URI mismatch after a hostname change — a provider-side registration issue surfaced by the NewTransportWithCode stage.

Prevention Best Practices

  • Keep root_url and the provider’s registered redirect URI (/login/generic_oauth) in lockstep across every environment; see more Grafana guides.
  • Store client_secret in a secret manager and rotate it in both Grafana and the IdP together.
  • Request openid email profile scopes and confirm the userinfo endpoint returns email before enabling SSO.
  • Start with role_attribute_strict = false and a safe default role, then tighten mapping once verified.
  • Keep a local break-glass admin (do not fully disable_login_form) so an SSO outage does not lock everyone out.
  • Turn on oauth:debug temporarily when diagnosing; disable it afterward.

Quick Command Reference

# Exact failing OAuth stage
sudo journalctl -u grafana-server | grep -iE "OAuthLogin|oauth|user sync" | tail -30
kubectl logs deploy/grafana -n monitoring | grep -iE "OAuthLogin|oauth" | tail -30

# Does userinfo return an email claim?
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" https://idp.example.com/userinfo | jq

# Key grafana.ini knobs
# [server] root_url = https://grafana.example.com/
# redirect URI = <root_url>login/generic_oauth
# [auth.generic_oauth] email_attribute_path = email
# [auth.generic_oauth] role_attribute_strict = false
# [log] filters = oauth:debug   (temporary)

Conclusion

login.OAuthLogin(...) failures name the exact stage that broke — use it:

  1. NewTransportWithCode → code-for-token exchange: check redirect URI, client secret, root_url.
  2. get info from token → userinfo/JWT: check scopes and that email is present.
  3. User-sync/role errors → mapping: relax role_attribute_strict, verify role_attribute_path and allowed domains.

Read the parenthetical stage in the log, fix the corresponding config or provider registration, and keep a break-glass local admin so SSO problems never lock the whole team out.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.