Configuring keycloak SSO for TLJH on EC2, configurations seems entirely correct but not redirecting to keycloak login page, revealing 500 error

we think it might be a scope issue, but not entirely sure

# OAuth2 application info
c.JupyterHub.authenticator_class = GenericOAuthenticator
c.GenericOAuthenticator.oauth_callback_url = "https://stats.brainandspinegroup.org/hub/oauth_callback"
c.GenericOAuthenticator.client_id = ""
c.GenericOAuthenticator.client_secret = ""

# Identity provider info
c.GenericOAuthenticator.authorize_url = "https://auth.brainandspinegroup.org/realms/my_BSG/protocol/openid-connect/auth"
c.GenericOAuthenticator.token_url = "https://auth.brainandspinegroup.org/realms/my_BSG/protocol/openid-connect/token"
c.GenericOAuthenticator.userdata_url = "https://auth.brainandspinegroup.org/realms/my_BSG/protocol/openid-connect/userinfo"

# What we request about the user
c.GenericOAuthenticator.userdata_params = {"state": "state"}
c.GenericOAuthenticator.username_key = "preferred_username"
c.GenericOAuthenticator.login_service = "myBSG"
c.GenericOAuthenticator.userdata_method = 'GET'
c.GenericOAuthenticator.allow_all = True
c.GenericOAuthenticator.create_system_users = True
c.GenericOAuthenticator.auto_login = False
c.GenericOAuthenticator.scope = ["openid", "email","profile", "groups"]
c.GenericOAuthenticator.claim_groups_key = "groups"

here are some logs

2024-06-16 02:08:15,890 WARN  [org.keycloak.events] (executor-thread-361) type="LOGIN_ERROR", realmId="bdf513bd-3ccb-4eb4-8c3c-535892199860", clientId="jupyterhub-client", userId="null", ipAddress="173.73.145.221", error="invalid_redirect_uri", redirect_uri="https://auth.brainandspinegroup.org/realms/my_BSG/protocol/openid-connect/auth"
2024-06-16 02:42:29,120 WARN  [org.keycloak.events] (executor-thread-364) type="CUSTOM_REQUIRED_ACTION_ERROR", realmId="bdf513bd-3ccb-4eb4-8c3c-535892199860", clientId="null", userId="null", ipAddress="70.105.172.177", error="cookie_not_found"
2024-06-16 02:49:19,982 WARN  [org.keycloak.events] (executor-thread-366) type="CUSTOM_REQUIRED_ACTION_ERROR", realmId="bdf513bd-3ccb-4eb4-8c3c-535892199860", clientId="null", userId="null", ipAddress="70.105.172.177", error="cookie_not_found"
2024-06-16 03:03:35,494 WARN  [org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequestParserProcessor] (executor-thread-368) Parameter 'client_id' not present or present multiple times in the HTTP request parameters
2024-06-16 03:03:35,495 WARN  [org.keycloak.events] (executor-thread-368) type="LOGIN_ERROR", realmId="bdf513bd-3ccb-4eb4-8c3c-535892199860", clientId="null", userId="null", ipAddress="74.82.16.196", error="invalid_request"
2024-06-16 03:03:35,956 WARN  [org.keycloak.events] (executor-thread-368) type="USER_INFO_REQUEST_ERROR", realmId="bdf513bd-3ccb-4eb4-8c3c-535892199860", clientId="null", userId="null", ipAddress="74.82.16.196", error="invalid_token", auth_method="validate_access_token"

any and all help is appreciated!

Your config is bit strange. Why is authorize_url, token_url and userdata_url are at subdomain auth.brainandspine.org and your oauth_callback_url is at different sub domain stats.brainandspinegroup? More over there is no need to really configure oauth_callback_url, OAutheticator will build it based on other config.

Thanks, OP and I have been working on this together. This is helpful - I think our understanding of the callback URL has been wrong. stats.brainandspinegroup.org is the subdomain of our TLJH. auth.brainandspinegroup.org is the subdomain our keycloak server.

Fair enough. You are right. I misread your config. My bad, sorry! I think your config is ok. We have pretty similar config except that we didnt configure oauth_callback_url as hub will build one for us.

I guess your issue might exist on Keycloak side on how you configured your client.

It looks like you’re missing the client id-have you created the client?

Yes, client is created. But I suspect we may have the client configured improperly if there isn’t a glaring error in the config. Are there any good examples of keycloak client config for TLJH?

We use exactly the same config as you posted in the topic for our Keycloak. Could you share the browser console logs and hub logs? When you click button myBSG on Hub login page, what is the redirected URL in the browser?

We just commented out

c.GenericOAuthenticator.oauth_callback_url

and used “/*” in the Valid redirect URIs client settings

And it got us further!
To this screen after clicking the “myBSG” button that first appears

This is the callback url we’re getting in the browser

[type or paste code here](https://auth.brainandspinegroup.org/realms/my_BSG/protocol/openid-connect/auth?response_type=code&redirect_uri=https%3A%2F%2Fstats.brainandspinegroup.org%2Fhub%2Foauth_callback&client_id=jupyterhub-client&state=eyJzdGF0ZV9pZCI6ICJlMzExZDM2YzdlMDU0NzlhYTIyZjNiY2E3ZDNjOWI2YSJ9&scope=openid+email+profile+groups)

Here is our client config json for more context

{
  "clientId": "jupyterhub-client",
  "name": "jupyterhub-client",
  "description": "",
  "rootUrl": "https://stats.brainandspinegroup.org/",
  "adminUrl": "https://stats.brainandspinegroup.org/",
  "baseUrl": "https://stats.brainandspinegroup.org/",
  "surrogateAuthRequired": false,
  "enabled": true,
  "alwaysDisplayInConsole": false,
  "clientAuthenticatorType": "client-secret",
  "secret": "",
  "redirectUris": [
    "/*"
  ],
  "webOrigins": [
    "https://stats.brainandspinegroup.org"
  ],
  "notBefore": 0,
  "bearerOnly": false,
  "consentRequired": false,
  "standardFlowEnabled": true,
  "implicitFlowEnabled": false,
  "directAccessGrantsEnabled": true,
  "serviceAccountsEnabled": true,
  "authorizationServicesEnabled": true,
  "publicClient": false,
  "frontchannelLogout": true,
  "protocol": "openid-connect",
  "attributes": {
    "oidc.ciba.grant.enabled": "false",
    "client.secret.creation.time": "",
    "backchannel.logout.session.required": "true",
    "login_theme": "keycloak",
    "oauth2.device.authorization.grant.enabled": "false",
    "display.on.consent.screen": "false",
    "backchannel.logout.revoke.offline.tokens": "false"
  },
  "authenticationFlowBindingOverrides": {},
  "fullScopeAllowed": true,
  "nodeReRegistrationTimeout": -1,
  "protocolMappers": [
    {
      "name": "username",
      "protocol": "openid-connect",
      "protocolMapper": "oidc-usermodel-attribute-mapper",
      "consentRequired": false,
      "config": {
        "introspection.token.claim": "true",
        "userinfo.token.claim": "true",
        "user.attribute": "username",
        "id.token.claim": "true",
        "access.token.claim": "true",
        "claim.name": "preferred_username",
        "jsonType.label": "String"
      }
    },
    {
      "name": "Client ID",
      "protocol": "openid-connect",
      "protocolMapper": "oidc-usersessionmodel-note-mapper",
      "consentRequired": false,
      "config": {
        "user.session.note": "client_id",
        "introspection.token.claim": "true",
        "id.token.claim": "true",
        "access.token.claim": "true",
        "claim.name": "client_id",
        "jsonType.label": "String"
      }
    },
    {
      "name": "Client IP Address",
      "protocol": "openid-connect",
      "protocolMapper": "oidc-usersessionmodel-note-mapper",
      "consentRequired": false,
      "config": {
        "user.session.note": "clientAddress",
        "introspection.token.claim": "true",
        "id.token.claim": "true",
        "access.token.claim": "true",
        "claim.name": "clientAddress",
        "jsonType.label": "String"
      }
    },
    {
      "name": "email",
      "protocol": "openid-connect",
      "protocolMapper": "oidc-usermodel-attribute-mapper",
      "consentRequired": false,
      "config": {
        "introspection.token.claim": "true",
        "userinfo.token.claim": "true",
        "user.attribute": "email",
        "id.token.claim": "true",
        "access.token.claim": "true",
        "claim.name": "email",
        "jsonType.label": "String"
      }
    },
    {
      "name": "Client Host",
      "protocol": "openid-connect",
      "protocolMapper": "oidc-usersessionmodel-note-mapper",
      "consentRequired": false,
      "config": {
        "user.session.note": "clientHost",
        "introspection.token.claim": "true",
        "id.token.claim": "true",
        "access.token.claim": "true",
        "claim.name": "clientHost",
        "jsonType.label": "String"
      }
    }
  ],
  "defaultClientScopes": [
    "web-origins",
    "acr",
    "roles",
    "profile",
    "groups",
    "email"
  ],
  "optionalClientScopes": [
    "address",
    "phone",
    "offline_access",
    "microprofile-jwt"
  ],
  "access": {
    "view": true,
    "configure": true,
    "manage": true
  }
}

Browser console logs:

Failed to load resource: the server responded with a status of 400 (Bad Request)

Have you set up client similarly? @mahendrapaipuri

update: we removed the “/” at the end of our keycloak client root, admin, and base URLs and that seems to have fixed the redirect URI error. So we’re getting routed back to JH properly, and getting a 500 Internal Server error. Appears to be a token problem: either keycloak isn’t sending the right token or JH isn’t looking for the correct token format.

update: we turned on more verbose logs and discovered we needed CA certificates among other things installed. The following was run

sudo apt-get update
sudo apt-get install -y ca-certificates
sudo mkdir -p /etc/pki/tls/certs
sudo ln -s /etc/ssl/certs/ca-certificates.crt /etc/pki/tls/certs/ca-bundle.crt
pip install --upgrade certifi
sudo systemctl restart jupyterhub

Thanks folks for contributing, we’re super grateful for quick eyes on this one. Cheers

1 Like