keycloak / keycloak-nodejs-connect

Apache License 2.0
676 stars 421 forks source link

Write a migration guide for the Node.js Connect adapter #453

Open abstractj opened 1 year ago

abstractj commented 1 year ago

Once we have settled on a recommended alternative and covered all the edge cases and missing features we need to write a migration guide for users of the Node.js Connect adapter.

coeing commented 1 year ago

Hi @abstractj,

I am currently starting to make the connection from our Node.js backend to the KeyCloak auth server. Do you have a hint which might be the recommended alternative? I don't want to jump on this package when it is already deprecated :)

Thanks Christian

abstractj commented 1 year ago

Hi @coeing, @jonkoops has started his research about it and has more details. But it seems that right now the best alternative is https://github.com/panva/node-openid-client.

jonkoops commented 1 year ago

The openid-client library is a likely recommendation for a low-level approach to the spec. For easier higher level integration we're thinking of recommending express-openid-connect.

Feel free to try these out and let us know what your experience is.

coeing commented 1 year ago

@abstractj @jonkoops Thanks a lot for your quick suggestions! I will try those libraries and hope to remember to give you feedback :)

marcoBros commented 1 year ago

Any feedback @coeing about new node keycloak implementation ?

abstractj commented 1 year ago

@marcoBros only to bring some clarity here, there's no "new node Keycloak implementation". The Keycloak Node.js adapter will be deprecated, and it is recommended to users to transition to another library. The library suggested is the express-openid-connect.

jonkoops commented 1 year ago

Just a small update I am still in the process of gathering a list of all the features of this package so that we can provide a comprehensive overview of what possible replacement packages need to support.

coeing commented 1 year ago

Any feedback @coeing about new node keycloak implementation ?

Hi @marcoBros !

Thanks for the reminder :)

In the end we just verify the token send by the frontend with the public key from the Keycloak realm, so we can be sure that the token was created by Keycloak. There is no direct communication between Keycloak and our backend. There are some things we need in the backend, e.g. the ids of the clients the user belongs to. Those information is included in the token payload, so after verification the backend knows those information.

If anybody sees a problem with this approach, let me know! It looks like this is the most efficient way to handle access tokens that were provided by third-party services.

Cheers

lucrus73 commented 1 year ago

If anybody sees a problem with this approach, let me know! It looks like this is the most efficient way to handle access tokens that were provided by third-party services.

When it comes to Keycloak (and OpenID in general) I'm a total beginner, so I have hard time trying to imagine how to implement this, but I need to make sure that my webservices can be used only by authenticated users or well known trusted machines (which I assume can be represented as clients in KC).

Up until now, I 've been naively taking for granted the need to use some sort of middleware, in my express app, to filter requests via KC, asking KC something like: "Here is the payload I received from my Angular client app, please tell me what roles/capabilites this user has so that I can decide if I should grant access to this resource or not".

However now that I've read (and hopefully understood) your comment, seems like my express app does not need to talk to KC at all, but it only needs to check if the payload was signed by KC and then it will find all the needed informations about the user inside the payload itself. Am I wrong?

coeing commented 1 year ago

Hi @lucrus73 ,

I can understand your thinking, this was the way I imagined it in the beginning as well. But due to the magic of asymmetric encryption your server only needs the public key of the Keycloak Realm (which can be seen publicly under https://your-keycloak-server.com/auth/realms/your-realm-name). You will have to surround the key by -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY-----, otherwise jwt.verify won't like it. This is my entry in my .env file:

KEYCLOAK_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
MIIB.....QAB
-----END PUBLIC KEY-----"

Here is my code snippet which does the verification of the token that was send by the frontend:

import jwt from "jsonwebtoken";

  private validateUserToken(
    token: string,
    config: KeycloakConfig
  ): AuthUser | undefined {
    try {
      const result = jwt.verify(token, config.publicKey, {
        algorithms: [config.algorithm],
        complete: false,
      });
      debug(`Valid token:\n${JSON.stringify(result, undefined, 2)}`);
      if (typeof result === "string") {
        throw new Error(`Got invalid token payload: ${result}`);
      }

      const user: AuthUser = {
        name: result.preferred_username || result.email,
        email: result.email,
        groups: result.groups,
      };

      return user;
    } catch (error) {
      return undefined;
    }
  }
lucrus73 commented 1 year ago

the public key of the Keycloak Realm (which can be seen publicly under https://your-keycloak-server.com/auth/realms/your-realm-name)

In my case that page yelds a nice "We are sorry - Page not found" message... of course I replaced the placeholders with my domain and realm names.

coeing commented 1 year ago

the public key of the Keycloak Realm (which can be seen publicly under https://your-keycloak-server.com/auth/realms/your-realm-name)

In my case that page yelds a nice "We are sorry - Page not found" message... of course I replaced the placeholders with my domain and realm names.

You can also find it in the Admin Console (https://your-key-cloak-server/auth/admin/master/console) under "Realm Settings" > "Keys" > "RS256" > "Public Key"

lucrus73 commented 1 year ago

You can also find it in the Admin Console (https://your-key-cloak-server/auth/admin/master/console) under "Realm Settings" > "Keys" > "RS256" > "Public Key"

Sure, but that requires to manually copy the public key into .env. If I ever need to regenerate the keys (e.g. compromised key), then I have to remember to copy the new public key in every .env out there that's using the compromised one and do that manually. Granted: in that case I'll have bigger problems than some copy&paste extra work, but it's not only that: I also need to document the procedure, to have shell access to all the affected systems and so on.

I guess I could manually copy the key to some well-known URL, maybe on the same server that hosts KC, and use that URL to automatically get the public key in my express deployments, and that would solve some of the problems. Still, I wonder if this is really needed, or if it's only my KC that lacks the public key reachable from a public URL, maybe for some bad configuration on my part.

PaleHazy commented 1 year ago

@lucrus73 I am able to see my public key at https://[keycloak]/realms/[realm]

lucrus73 commented 1 year ago

Thanks for your help, and let's go back to the original question of this thread: writing a migration guide.

@marcoBros suggested the best candidate to replace keycloak-nodejs-connect would be express-openid-connect.

Shortly after @coeing raised the stakes, suggesting you don't even need a replacement, as the backend already has all it needs in the token. You can just verify that and you're all set.

While @coeing suggestion looks brilliant, it does bring up a few more questions (at least to me): assuming it is the way to go, what keycloak-nodejs-connect was written for in the first place? E.g. what its added value was back in the day? Maybe that value vanished with recent KC releases and that's exactly why it's now being deprecated? Or that is not the reason for deprecation as there are other problems that you could still solve easier with keycloak-nodejs-connect, so those solutions now need a migration guide?

Or, rephrasing: why has keycloak-nodejs-connect been deprecated?

valerii15298 commented 1 year ago

@abstractj @jonkoops can you please confirm that solution by @coeing can be used safely? 80% people who use this library, use it only to verify roles. I would very much appreciate code example how to do that with any library you suggested. Solution with only jwt and public key from keycloak(by @coeing) also seems nice but would be better to get confirmation from authors of this library.

If her approach is not suitable, then can you give us code example how to use it with https://github.com/panva/node-openid-client if possible? Thanks in advance 🙏

lucrus73 commented 1 year ago

80% people who use this library, use it only to verify roles

and what the remaining 20% use it for? Maybe that will shed some light on the reason why it was deprecated.

coeing commented 1 year ago

80% people who use this library, use it only to verify roles

and what the remaining 20% use it for? Maybe that will shed some light on the reason why it was deprecated.

What I can imagine to have used this library for: If you have an application with some custom UI to manage users and add them to groups, you could route those changes through your backend to Keycloak instead of directly changing it inside Keycloak. This would hide the implementation details (e.g. which groups will give which permissions) from the frontend.

Just my thoughts without having used the library myself :)

valerii15298 commented 1 year ago

I proposed another alternative which I already use quite for some time in my app: https://github.com/keycloak/keycloak-nodejs-connect/issues/492

Hope it will be helpful for someone.

Btw, It will fetch public keys from keyacloak server automatically when previous one will become invalid, so there will be no need to put key manually in .env file every time, @lucrus73

Sure, but that requires to manually copy the public key into .env

lucrus73 commented 1 year ago

@valerii15298 Thanks, I know, as @PaleHazy already suggested (I suppose you meant public keys, not private)

lucrus73 commented 7 months ago

it seems there is a good replacement now, straight from Keycloak itself:

https://github.com/keycloak/keycloak/tree/main/js/libs/keycloak-admin-client

valerii15298 commented 7 months ago

@lucrus73 no, it has different purpose, though you can use it to validate keycloak tokens

lucrus73 commented 7 months ago

@valerii15298 You're right, but I mean you could use keycloak-admin-client to cover only that 20% left out from the @coeing solution, which I'm using and I can confirm it works wonders.

Assuming that 20% is what @coeing described in his comment, which in fact happens to be in my case, then @coeing solution plus keycloak-admin-client is all you need to cover 100% of the previous use cases for keycloak-nodejs-connect. So we could have a migration path.

But this is only my 2 cents, which I don't even need myself, because I'm already doing well using this solution.

KJCheo-weston commented 5 months ago

The openid-client library is a likely recommendation for a low-level approach to the spec. For easier higher level integration we're thinking of recommending express-openid-connect.

Feel free to try these out and let us know what your experience is.

I have been able to use openid-client for confidential client authentication via client credentials grant and resource management using the protection API without much issues.

Working with keycloak version 23.