Grafana and Keycloak OAuth. Code not valid, already used for session

Hello everyone. I am trying to set up OAuth for Grafana with Keycloak. I have followed the required steps but after performing a login, I am receiving a ‘Failed to get token from provider’ message on the Grafana login page.

Here’s my Grafana configuration:

    envFromSecret: grafana-env
    grafana.ini:
      check_for_updates: false
      reporting_enabled: false
      server:
        enable_gzip: true
        domain: http://192.168.5.230:30030
        root_url: http://192.168.5.230:30030
      security:
        disable_gravatar: true
      auth.generic_oauth: 
        enabled: true
        name: Keycloak-OAuth
        allow_sign_up: true
        client_id: grafana-oauth
        client_secret: ${CLIENT_SECRET}
        #email_attribute_path: email
        login_attribute_path: username
        #name_attribute_path: full_name
        auth_url: http://192.168.5.230:30081/realms/tmf/protocol/openid-connect/auth
        token_url: http://192.168.5.230:30081/realms/tmf/protocol/openid-connect/token
        api_url: http://192.168.5.230:30081/realms/tmf/protocol/openid-connect/userinfo
        role_attribute_path: contains(roles[*], 'admin') && 'Admin' || contains(roles[*], 'editor') && 'Editor' || 'Viewer'
        scopes: openid email profile offline_access roles
        use_pkce: true

The grafana-env secret:

apiVersion: v1
kind: Secret
metadata:
  name: grafana-env
  namespace: default
stringData:
  CLIENT_SECRET: <secret-taken-from-Keycloak>

Now here is my Keycloak client configuration:



Plus this is the user configuration that I’m trying to test with:


Now, the interesting thing is that a session is actually created on the Keycloak side. I’m able to see the session both on the general Sessions tab and on the grafana-oauth client tab.

Finally, here are the logs from Grafana:

logger=context userId=0 orgId=0 uname= t=2024-10-04T06:50:17.796482664Z level=info msg="Request Completed" method=GET path=/login/generic_oauth status=302 remote_addr=10.240.0.1 time_ms=0 duration=979.388µs size=415 referer=http://192.168.5.230:30030/login handler=/login/:name status_source=server
logger=authn.service t=2024-10-04T06:50:23.23591675Z level=error msg="Failed to authenticate request" client=auth.client.generic_oauth error="[auth.oauth.token.exchange] failed to exchange code to token: oauth2: \"invalid_grant\" \"Code not valid\""
logger=context userId=0 orgId=0 uname= t=2024-10-04T06:50:23.245653288Z level=info msg="Request Completed" method=GET path=/login/generic_oauth status=302 remote_addr=10.240.0.1 time_ms=28 duration=28.930476ms size=29 referer= handler=/login/:name status_source=server

And the Keycloak logs:

2024-10-04 06:51:22,658 WARN  [org.keycloak.events] (executor-thread-103) type=CODE_TO_TOKEN_ERROR, realmId=tmf, clientId=grafana-oauth, userId=143d16bf-b7f6-4aae-9af3-a496da41df66, ipAddress=10.240.0.1, error=not_allowed, grant_type=authorization_code, code_id=ee7bd977-da6b-4dfb-b697-b8aeef1790a6, client_auth_method=client-secret
2024-10-04 06:51:22,659 WARN  [org.keycloak.protocol.oidc.utils.OAuth2CodeParser] (executor-thread-103) Code 'f7e2139a-b70c-405b-a0d8-31bd2aa6ea8f' already used for userSession 'ee7bd977-da6b-4dfb-b697-b8aeef1790a6' and client '03aab60c-e50d-4342-82a9-11cb0a5a2880'.
2024-10-04 06:51:22,659 WARN  [org.keycloak.events] (executor-thread-103) type=CODE_TO_TOKEN_ERROR, realmId=tmf, clientId=grafana-oauth, userId=null, ipAddress=10.240.0.1, error=invalid_code, grant_type=authorization_code, code_id=ee7bd977-da6b-4dfb-b697-b8aeef1790a6, client_auth_method=client-secret

The message saying that the code is already used is very odd considering that no session actually exists before attempting a login. I checked the Keycloak sessions and it’s entirely empty.

I would appreciate any help in resolving this error. Thanks!

1 Like

Weird is that you are using pkce flow and client secret. The point of pkce is that you don’t need client secret at all. Remove client secret and try debug steps again.

You need public client for pkce on the Keycloak side.

Yes, you are correct. I didn’t need the pkce option actually. It remained there from a previous configuration which was wrong. Just an oversight. In any case, here are the logs from both cases:

Grafana Logs using only pkce:

logger=context userId=0 orgId=0 uname= t=2024-10-04T09:52:06.953909059Z level=info msg="Request Completed" method=GET path=/login/generic_oauth status=302 remote_addr=10.240.0.1 time_ms=2 duration=2.671946ms size=415 referer=http://192.168.5.230:30030/login handler=/login/:name status_source=server
logger=authn.service t=2024-10-04T09:52:11.721651857Z level=error msg="Failed to authenticate request" client=auth.client.generic_oauth error="[auth.oauth.token.exchange] failed to exchange code to token: oauth2: \"unauthorized_client\" \"Invalid client or Invalid client credentials\""
logger=context userId=0 orgId=0 uname= t=2024-10-04T09:52:11.731715726Z level=info msg="Request Completed" method=GET path=/login/generic_oauth status=302 remote_addr=10.240.0.1 time_ms=27 duration=27.253088ms size=29 referer= handler=/login/:name status_source=server

Keycloak Logs using only pkce:

2024-10-04 09:52:11,715 WARN  [org.keycloak.events] (executor-thread-134) type=CODE_TO_TOKEN_ERROR, realmId=tmf, clientId=grafana-oauth, userId=null, ipAddress=10.240.0.1, error=invalid_client_credentials, grant_type=authorization_code
2024-10-04 09:52:11,720 WARN  [org.keycloak.events] (executor-thread-134) type=CODE_TO_TOKEN_ERROR, realmId=tmf, clientId=grafana-oauth, userId=null, ipAddress=10.240.0.1, error=invalid_client_credentials, grant_type=authorization_code

And Grafana Logs using client secret:

logger=authn.service t=2024-10-04T10:01:06.816360667Z level=error msg="Failed to authenticate request" client=auth.client.generic_oauth error="[auth.oauth.token.exchange] failed to exchange code to token: oauth2: \"invalid_grant\" \"Code not valid\""
logger=context userId=0 orgId=0 uname= t=2024-10-04T10:01:06.825189505Z level=info msg="Request Completed" method=GET path=/login/generic_oauth status=302 remote_addr=10.240.0.1 time_ms=17 duration=17.719663ms size=29 referer= handler=/login/:name status_source=server

Keycloak Logs using client secret:

2024-10-04 10:01:06,813 WARN  [org.keycloak.events] (executor-thread-142) type=CODE_TO_TOKEN_ERROR, realmId=tmf, clientId=grafana-oauth, userId=5340e833-0c27-43d3-88af-aa5b7da421b2, ipAddress=10.240.0.1, error=not_allowed, grant_type=authorization_code, code_id=b62966dc-81b6-4c47-a6ec-dd31c825c33e, client_auth_method=client-secret
2024-10-04 10:01:06,815 WARN  [org.keycloak.protocol.oidc.utils.OAuth2CodeParser] (executor-thread-142) Code '49a362c8-e92c-475a-92a7-190d803ea7ca' already used for userSession 'b62966dc-81b6-4c47-a6ec-dd31c825c33e' and client '03aab60c-e50d-4342-82a9-11cb0a5a2880'.
2024-10-04 10:01:06,815 WARN  [org.keycloak.events] (executor-thread-142) type=CODE_TO_TOKEN_ERROR, realmId=tmf, clientId=grafana-oauth, userId=null, ipAddress=10.240.0.1, error=invalid_code, grant_type=authorization_code, code_id=b62966dc-81b6-4c47-a6ec-dd31c825c33e, client_auth_method=client-secret

My client is not public, so I think I need my setup to work with the client secret option.

yes, so pkce on the grafana side must be disabled.

First focus on authentication - so remove anything related to authorization from keycloak client config (custom scopes, roles). When authentication is working, then work on authorization.

I guess you must have all default scopes defined in the grafana config otherwise code exchange will fail.

It is like you’ve done a thorough job setting everything up! The “code already used” message often means that the OAuth flow isn’t completing as expected, possibly due to a redirect issue or multiple submissions. You might want to check if the authorization code is being used more than once codm injector apk before Grafana processes it. Additionally, ensuring that the redirect URIs in Keycloak match exactly with those in Grafana’s configuration can help resolve the issue.

1 Like

Hello. I was able to resolve the problem.

I actually set up a new instance of Grafana and Keycloak, both in their latest versions. Also, I had to set up the client role mapper to send the user ID in the response token. And I also believe that the scopes don’t all have to be on default. I only set the ‘email’, ‘profile’ and ‘roles’ scopes and it worked without issue.

Finally I had to set an email for the test user. Regardless of whether the ‘email’ scope is enabled or not, this appears to be a requirement on the Grafana side.

I will have to make the SSO setup on some earlier version for a project I need. Should I encounter any new issues, I will make sure to create a new post.

1 Like