Make grafana with existing JWT token

Hey everyone,
I am new to grafana and need some help regarding user authentication

Grafana version

Latest grafana docker image

Background

I have a backend running with fastapi supporting multiple users. Currently I am authenticating the user based on the JWT token received. I want each user to have a user account on grafana as well. How to enable this?

Options

  1. One way I can think is to create the grafana user when a new user is registered via Admin API. By this way, I’ll have to create/delete user at two places. One at my end and another would be at the grafana db side.
  2. Use same JWT token to auto_sign_up the user and login into the grafana.

I like the second approach but I am not sure how to pursue this one. I found this blog but have some questions.

Questions:

  1. My current system verifies the token using a specific algorithm. Is there a way to mention that in the env of the container?. And Instead of PEM, can it verify using JWT-secret ?

I have a dashboard that’s gonna use the logged-in user details like email, username later point so, would want those details to get populated by the auto-sign-up.

Also like if there is any other standard way to achieve this.

Why you are not using OAuth/OIDC. I would say that’s industry standard based on JWT under the hood. Your users will have SSO.

I can’t use available OAuth services because my system would be running on an environment without internet access. I would have to create my own custom authentication servers (not sure, how difficult and if it worth the hassle)

I would say that’s not a problem. Install identity provider (“authentication server”) e.g. Keycloak in local network and authenticate against it in local network only. Generally, that symmetrically signed by a shared secret using the HMAC algorithm is not used widely - you can expect problems with that - e.g. Grafana doesn’t support it. So you need more complex asymmetric signature in JWT.

Hey, thanks for the insight
I was able to start the keycloak container on 8081 port with the documentation and integrate it with grafana.

I followed the steps mentioned in the keycloak documentation, made a realm, made a client, add a user. But on login, I am getting this error message on the screen

I am running grafana container by nomad and using traefik as reverse proxy
My grafana env variables are

env {
        GF_LOG_LEVEL          = "DEBUG"
        GF_LOG_MODE           = "console"
        GF_SERVER_ROOT_URL = "%(protocol)s://%(domain)s:%(http_port)s/grafana/"
        GF_SERVER_SERVE_FROM_SUB_PATH = "true"
        GF_SERVER_HTTP_PORT   = "${NOMAD_PORT_http}"
        GF_PATHS_PROVISIONING = "/local/grafana/provisioning"
        GF_PATHS_DATA = "{{ nomad_monitoring_dir }}/grafana"
        GF_AUTH_GENERIC_OAUTH_ENABLED="true"
        GF_AUTH_GENERIC_OAUTH_NAME="Keycloak"
        GF_AUTH_GENERIC_OAUTH_ALLOW_SIGN_UP="true"
        GF_AUTH_GENERIC_OAUTH_CLIENT_ID="grafana-oauth"
        GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET="secret-id"
        GF_AUTH_GENERIC_OAUTH_SCOPES="email profile offline_access roles"
        GF_AUTH_GENERIC_OAUTH_AUTH_URL="http://localhost:8081/realms/myrealm/protocol/openid-connect/auth"
        GF_AUTH_GENERIC_OAUTH_TOKEN_URL="http://localhost:8081/realms/myrealm/protocol/openid-connect/token"
        GF_AUTH_GENERIC_OAUTH_API_URL="http://localhost:8081/realms/myrealm/protocol/openid-connect/userinfo"
        GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH="contains(roles[*], 'admin') && 'Admin' || contains(roles[*], 'editor') && 'Editor' || 'Viewer'"
      }

grafana client in keycloak


My Grafana logs:

logger=context userId=0 orgId=0 uname= t=2024-09-23T12:55:23.817832388Z level=info msg="Request Completed" method=GET path=/login/generic_oauth status=302 remote_addr=::1 time_ms=0 duration=178.041µs size=314 referer=http://localhost/grafana/login handler=/login/:name status_source=server
logger=authn.service t=2024-09-23T12:55:23.842041055Z level=error msg="Failed to authenticate request" client=auth.client.generic_oauth error="[auth.oauth.token.exchange] failed to exchange code to token: Post \"http://localhost:8081/realms/myrealm/protocol/openid-connect/token\": dial tcp [::1]:8081: connect: connection refused"
logger=context userId=0 orgId=0 uname= t=2024-09-23T12:55:23.85387843Z level=info msg="Request Completed" method=GET path=/login/generic_oauth status=302 remote_addr=::1 time_ms=13 duration=13.056084ms size=37 referer= handler=/login/:name status_source=server
logger=accesscontrol t=2024-09-23T12:55:23.858104263Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(licensing:read server.stats:read)"
logger=accesscontrol t=2024-09-23T12:55:23.858963763Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="action:dashboards:read scopes:"
logger=accesscontrol t=2024-09-23T12:55:23.858985013Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(folders:read folders:create dashboards:read dashboards:create)"
logger=accesscontrol t=2024-09-23T12:55:23.858992013Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="action:datasources:explore scopes:"
logger=accesscontrol t=2024-09-23T12:55:23.858998763Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(alert.rules:read alert.rules.external:read)"
logger=accesscontrol t=2024-09-23T12:55:23.85901418Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(alert.notifications:read alert.notifications.external:read)"
logger=accesscontrol t=2024-09-23T12:55:23.859028222Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(alert.instances:read alert.instances.external:read alert.silences:read)"
logger=accesscontrol t=2024-09-23T12:55:23.85904168Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(alert.instances:read alert.instances.external:read)"
logger=accesscontrol t=2024-09-23T12:55:23.859047638Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(alert.rules:create alert.rules.external:write)"
logger=accesscontrol t=2024-09-23T12:55:23.859056013Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(datasources:explore datasources:create all of datasources:read, any of datasources:delete, datasources:write)"
logger=accesscontrol t=2024-09-23T12:55:23.859070972Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(all of orgs:read, orgs:write all of orgs.preferences:read, orgs.preferences:write)"
logger=accesscontrol t=2024-09-23T12:55:23.859076888Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="action:settings:read scopes:settings:*"
logger=context userId=0 orgId=0 uname= t=2024-09-23T12:55:23.859085597Z level=debug msg="Failed to authenticate user in global scope" error="[auth.identity.unsupported] invalid identity type"
logger=accesscontrol t=2024-09-23T12:55:23.859103472Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="action:settings:read scopes:settings:*"
logger=accesscontrol t=2024-09-23T12:55:23.859114097Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(plugins:write plugins:install)"
logger=accesscontrol t=2024-09-23T12:55:23.859132805Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="action:datasources:read scopes:"
logger=accesscontrol t=2024-09-23T12:55:23.859139138Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(org.users:read users:read)"
logger=accesscontrol t=2024-09-23T12:55:23.859161555Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(teams:create all of teams:read, any of teams:write, teams.permissions:write, teams.permissions:read)"
logger=accesscontrol t=2024-09-23T12:55:23.859170305Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(serviceaccounts:read serviceaccounts:create)"
logger=accesscontrol t=2024-09-23T12:55:23.85977518Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="action:apikeys:read scopes:"
logger=accesscontrol t=2024-09-23T12:55:23.859827847Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(settings:read settings:write settings:read settings:write settings:read settings:write settings:read settings:write settings:read settings:write settings:read settings:write)"
logger=accesscontrol t=2024-09-23T12:55:23.859860638Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(support.bundles:read support.bundles:create)"
logger=accesscontrol t=2024-09-23T12:55:23.860299472Z level=debug msg="No permissions set" id=:0 orgID=0 permissions="any(dashboards:create folders:create)"
logger=sqlstore t=2024-09-23T12:55:23.860326388Z level=debug msg="ReadReplica not configured, using main SQLStore"

Not sure why the connection is getting refused.

Remeber that you are in the container. Each container has own localhost, so localhost:8081 in the grafana container! = localhost:8081 in the keycloak container (usually). Check nomad doc, how you can reference other container (for example you can use container name in the docker, of course they must be on the same network).

Thank you for the help. That was the issue indeed.
Also needed to add openid in GF_AUTH_GENERIC_OAUTH_SCOPES

Both were running as different jobs in nomad and that’s why they were not in the same docker network. Moved them in the same task-group and it worked like charm.

I have one more question

  • The default admin’s username and password is admin and admin. How to have keycloak sign-in of grafana admin ?

Use role mapping:

So you can set Grafana role based on info provided by IDP/keycloak (email, group, role).

Keep in mind that Grafana role GrafanaAdmin (“super admin”) != Grafana role Admin.