Role Mapping with Keycloak

Hello.
Cant assign a role to the user. Im using auth.proxy with keycloak
Grafana - v8.3.6
Keycloak - v16.1.0
What did I miss?

In the logs I see the following information:

proxy logs:

> debug|keycloak-gatekeeper/handlers.go:88|incoming authorization request from client address|{access_type: , auth_url: <url>protocol/openid-connect/auth?client_id=grafana&redirect_uri=https%3A%2F%2Fgrafana1.net%2Foauth%2Fcallback&response_type=code&scope=openid+email+profile&state=<key>, client_ip: <ip>}|
> info|keycloak-gatekeeper/handlers.go:179|issuing access token for user|{email: user@example.com, expires: 2022-04-19T16:21:41Z, duration: 4m59.258862845s}|
> debug|keycloak-gatekeeper/session.go:51|found the user identity|{id: <id>, name: user, email: user@example.com, roles: grafana_admin, groups: }|
> debug|keycloak-gatekeeper/middleware.go:337|access permitted to resource|{access: permitted, email: user@example.com, expires: 299.144459781, resource: /*}|

grafana logs:

>  lvl=dbug msg="Trying to log user in" logger=auth.proxy username=user@example.com ignoreCache=false
>  lvl=dbug msg="Getting user ID via auth cache" logger=auth.proxy cacheKey=auth-proxy-sync-ttl:asfasw2345235dsfsd8998808ca7871e0
>  lvl=dbug msg="Failed getting user ID via auth cache" logger=auth.proxy error="cache item not found"
>  lvl=dbug msg="Syncing organization roles" logger=login.ext_user id=2 extOrgRoles=map[]
>  lvl=dbug msg="Not syncing organization roles since external user doesn't have any" logger=login.ext_user
>  lvl=dbug msg="Got user ID, getting full user info" logger=auth.proxy userID=2
>  lvl=dbug msg="Successfully got user info" logger=auth.proxy userID=2 username=user@example.com
>  lvl=dbug msg="Updating last user_seen_at" logger=context userId=2 orgId=1 uname=user@example.com user_id=2
>  lvl=dbug msg="Trying to log user in" logger=auth.proxy username=user@example.com ignoreCache=false
>  lvl=dbug msg="Getting user ID via auth cache" logger=auth.proxy cacheKey=auth-proxy-sync-ttl:asfasw2345235dsfsd8998808ca7871e0
>  lvl=dbug msg="Successfully got user ID via auth cache" logger=auth.proxy id=2
>  lvl=dbug msg="Got user ID, getting full user info" logger=auth.proxy userID=2
>  lvl=dbug msg="Successfully got user info" logger=auth.proxy userID=2 username=user@example.com
>  lvl=dbug msg="avatar.fetch(fetch new avatar)" logger=avatar url="https://secure.gravatar.com/avatar/b85af2188bffede41fe15268ea567e13?d=retro&r=pg&size=200"
>  lvl=dbug msg="Trying to log user in" logger=auth.proxy username=user@example.com ignoreCache=false
>  lvl=dbug msg="Getting user ID via auth cache" logger=auth.proxy cacheKey=auth-proxy-sync-ttl:asfasw2345235dsfsd8998808ca7871e0
>  lvl=dbug msg="Successfully got user ID via auth cache" logger=auth.proxy id=2
>  lvl=dbug msg="Got user ID, getting full user info" logger=auth.proxy userID=2
>  lvl=dbug msg="Successfully got user info" logger=auth.proxy userID=2 username=user@example.com
>  lvl=warn msg="Request Origin is not authorized" logger=live origin=https://grafana1.net host=127.0.0.1:3000 appUrl=http://localhost:3000/ allowedOrigins=
>  lvl=info msg="Request Completed" logger=context userId=2 orgId=1 uname=user@example.com method=GET path=/api/live/ws status=403 remote_addr=<ip> time_ms=1 size=10 referer=

My config:

 [analytics]
 check_for_updates = true
 [auth]
 disable_login_form = true
 disable_signout_menu = true
 [auth.anonymous]
 enabled = false
 [auth.basic]
 enabled = false
 [auth.proxy]
 auto_sign_up = true
 enabled= true
 header_name= X-Auth-Email
 header_property= email
 headers= Name:X-Auth-Username Role:X-Auth-Roles
 role_attribute_path= contains(roles[*], 'grafana_admin') && 'Admin' || contains(roles[*], 'grafana_editor') && 'Editor' || 'Viewer'
 [grafana_net]
 url = https://grafana.net
 [log]
 level = debug
 [paths]
 data = /var/lib/grafana/
 logs = /var/log/grafana
 plugins = /var/lib/grafana/plugins
 provisioning = /etc/grafana/provisioning
 security:
   admin_user: admin@example.com
extraContainers: |
  - name: oauth2-proxy
    image: quay.io/keycloak/keycloak-gatekeeper:6.0.1
    args:
      - -listen=:4181
      - -upstream-url=http://127.0.0.1:3000
      - -discovery-url=<realm url>
      - -enable-refresh-tokens=true
      - -encryption-key=<key>
      - -client-id=grafana
      - -enable-authorization-header=false
      - -client-secret=<secret>
      - -resources=uri=/*|roles=grafana_admin
      - -resources=uri=/metrics|white-listed=true
      - -resources=uri=/api/datasoruces|white-listed=true
      - -resources=uri=/api/dashboards|white-listed=true
    ports:
      - name: proxy-web
        containerPort: 4181

How your access token looks like (response from /oauth/token proxy endpoint)?
Are you sure that you have X-Auth-Roles header with values, which are matching Grafana role names (by default Admin,Editor,Viewer)?
BTW: role_attribute_path is not valid config property for [auth.proxy] section.

access token:

{
  "exp": ,
  "iat": ,
  "auth_time": ,
  "jti": "",
  "iss": "<realm url>",
  "aud": "grafana",
  "sub": "",
  "typ": "Bearer",
  "azp": "grafana",
  "session_state": "",
  "acr": "",
  "allowed-origins": [
    "https://grafana1.net"
  ],
  "realm_access": {
    "roles": [
      "grafana_admin"
    ]
  },
  "scope": "openid profile email roles",
  "sid": "",
  "email_verified": true,
  "roles": [
    "grafana_admin"
  ],
  "name": "user",
  "preferred_username": "user",
  "given_name": "user",
  "email": "user@example.com"
}

If role_attribute_path is not valid config property for [auth.proxy] section, then what is the alternative?

I guess you have found old OIDC solution from the age when Grafana didn’t have OIDC support.

I would use OAuth authentication | Grafana documentation so you don’t need any proxy and you can use also role_attribute_path.

@student123 Have you got the solution for this? I’m also facing exact challenge with Keycloak and grafana auth.proxy. If yes please share the solution.

yes, it works. I used auth.generic_oauth and extraContainers for proxy

But I have a problem with Datasource:

POST request sent to http://localhost:3000/api/admin/provisioning/datasources/reload. Response: 403 Forbidden {
“message”: “Permission denied”
}

datasource config:

apiVersion: 1
datasources: