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_urland the provider’s registered redirect URI (/login/generic_oauth) in lockstep across every environment; see more Grafana guides. - Store
client_secretin a secret manager and rotate it in both Grafana and the IdP together. - Request
openid email profilescopes and confirm the userinfo endpoint returnsemailbefore enabling SSO. - Start with
role_attribute_strict = falseand 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:debugtemporarily 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:
NewTransportWithCode→ code-for-token exchange: check redirect URI, client secret,root_url.get info from token→ userinfo/JWT: check scopes and thatemailis present.- User-sync/role errors → mapping: relax
role_attribute_strict, verifyrole_attribute_pathand 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.
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.