simov / grant

OAuth Proxy
MIT License
4.08k stars 257 forks source link

Keycloak resource claim are not mapped #257

Closed phxgbl closed 3 years ago

phxgbl commented 3 years ago

@simov I am trying to integrate Directus with Keycloak. Following is the JWT decode returned by the Keycloak

{
  "exp": 1627481458,
  "iat": 1627481157,
  "auth_time": 1627481157,
  "jti": "17b94735-025f-4e87-a74e-83c315d1827f",
  "iss": "http://localhost:9090/auth/realms/Adapt",
  "aud": "account",
  "sub": "09a3ceb7-fb2e-4983-9f19-cd6b236e050e",
  "typ": "Bearer",
  "azp": "directus-ui",
  "session_state": "fd0d6f25-e7ca-4640-bc23-0d7df8ab1f9e",
  "acr": "1",
  "allowed-origins": [
    "http://localhost:8050",
    "http://localhost:8055"
  ],
  "realm_access": {
    "roles": [
      "default-roles-adapt",
      "offline_access",
      "system_admin",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid email profile",
  "tenant_id": "system",
  "email_verified": false,
  "name": "admin adapt",
  "preferred_username": "system~~admin",
  "given_name": "admin",
  "family_name": "adapt",
  "email": "system~~admin@adapt.com"
}

Login works correctly. The only thing missing is Authorization, I mean I cannot find Keycloak roles in the Grantjs decoded token. Config used are these:

OAUTH_KEYCLOAK_SCOPE="openid email"

const config: any = {
    defaults: {
        origin: env.PUBLIC_URL,
        transport: 'session',
        prefix: '/auth/oauth',
        response: ['tokens', 'profile','jwt'],
    },
};

      response: {
        id_token: "xxxxxx",
        access_token: "xxxxxxx",
        refresh_token: "xxxxxxx",
        jwt: {
          id_token: {
            header: {
              alg: "RS256",
              typ: "JWT",
              kid: "IbC514Ciid6SJpPA6_JadZoTFsNEYp8pjOAJ4OCpeRs",
            },
            payload: {
              exp: 1627483234,
              iat: 1627482934,
              auth_time: 1627482934,
              jti: "d0c6d11f-f857-42c6-99e2-19bc08197fcb",
              iss: "http://localhost:9090/auth/realms/Adapt",
              aud: "directus-ui",
              sub: "09a3ceb7-fb2e-4983-9f19-cd6b236e050e",
              typ: "ID",
              azp: "directus-ui",
              session_state: "a28f0288-f389-485e-a4d6-32d1436a1bd6",
              at_hash: "b-_q7piDnSsUtwbT9jypMA",
              acr: "1",
              tenant_id: "system",
              email_verified: false,
              name: "admin adapt",
              preferred_username: "system~~admin",
              given_name: "admin",
              family_name: "adapt",
              email: "system~~admin@adapt.com",
            },
            signature: "xxxxxxx",
          },
        },
        profile: {
          tenant_id: "system",
          sub: "09a3ceb7-fb2e-4983-9f19-cd6b236e050e",
          email_verified: false,
          name: "admin adapt",
          preferred_username: "system~~admin",
          given_name: "admin",
          family_name: "adapt",
          email: "system~~admin@adapt.com",
        },
      },
    },
  },
'

Please help.

simov commented 3 years ago

This JWT token is generated and returned by KeyCloak, so if anything is missing there, then you probably have to request additional scopes.

phxgbl commented 3 years ago

@simov thanks for the reply. Please check the decoded JWT token posted above. We can find "realm_access" Object which has the roles. But when mapped in Directus, the same is missing. So keycloak must be returning the roles right? So is there anything I have to do in Grantjs request to map the roles?

simov commented 3 years ago

I don't know what mapping in Directus you are referring to, but as far as Grant is concerned everything returned from Keycloak is being returned to you. So that might be a question to ask in Directus repo instead.

phxgbl commented 3 years ago

@simov , I was confused where to ask.. Thanks for the help

phxgbl commented 3 years ago

@simov sorry for the confusion. Actually, I was referring to access_token(for Authorization) whereas GrantJs is using id_token for authentication which is absolutely correct. Is there any way to use access_token instead?

  if (output.id_token) {
    var jwt = oidc(provider, session, output)
    if (jwt.error) {
      return {provider, input, output: jwt}
    }
  }
simov commented 3 years ago

It all depends on the provider in question. If the provider returns an id_token and expects you to send it back for certain endpoints, then you need to do that. If the provider returns access_token and it expects you to use that for other endpoints then that's what you need to do. This is not being controlled by Grant.

simov commented 3 years ago

If your goal is to use Keycloak to login, and have access token for your API, then you can verify the id token, and then generate a JWT access token for your API. Here is an example for it.

phxgbl commented 3 years ago

@simov Thank you, I guess this is what I am looking for, that is getting information from access_token. Let me give a try.

phxgbl commented 3 years ago

@simov one more question, Keycloak returns both id_token and access_token, Is there any easy way(function) to decode access_token and get the details using Grant JS?

simov commented 3 years ago

Currently that's not exposed, but you can use this code:

var jwt = (str) => {
  var [header, payload, signature] = str.split('.')
  return {
    header: JSON.parse(Buffer.from(header, 'base64').toString('binary')),
    payload: JSON.parse(Buffer.from(payload, 'base64').toString('utf8')),
    signature,
  }
}

Or you can use the jws module, which you will need to verify your access token on every request to your API.

phxgbl commented 3 years ago

@simov your code did the trick. Screenshot 2021-07-29 at 2 58 02 PM You were really helpfull .. Thanks a lot.