External Idp integration via script

Hi all,
I trying to integrate external idp to Keycloak server. That external idp has no any adapter that supported by Keycloak(SAML, OIDC), it only support API. Until this time, I tried script execution in browser flow I can reach the external idp and validate user but how can I authenticate that user also in Keycloak too?

Is there any options for some kinda group based authentication in Keycloak ? or
Can I authenticate user in script, I did not see something like that but I still suspicious.
I am open to any advice.

Thank you

Any idea ?

Thank you.

Hello @ygtysr

I have come up with this problem recently and solved it via an external nodejs service that can call any api

See https://link.medium.com/OZGBvOHzHeb

Happy to help if you need

1 Like

Hi @guenoledc and thank you so much for your advice:)
I will try this approach immediately but before start to implement I have a doubt ;
I thought this method wont effect my token mechanism right?
Keycloak will produce same tokens as in OIDC flow and those token going to may introspect by Keycloak ?

Hi @ygtysr

The solution connects behind keycloak IDP services and it works with both OIDC and SAML. For keycloak it is a AuthenticatorSPI. So Keycloak external interface is not impacted.

So yes, keycloak tokens won’t be impacted.

Let me know if you face any issue

1 Like

This sounds is great :partying_face:
I am on it. I will update here according to consequences.
@guenoledc Thank you so much

So I tried this cool method and it work. What a great article @guenoledc
But I guess user must be defined already in keycloak otherwise I getting “Invalid username or password”. How can I skip this check. I need authenticate the user who does not exists in Keycloak via external Idp.

When you receive the first call in the processNew method you will see that the user object does not exists yet (see https://guenoledc-perso.gitlab.io/keycloak/rest-authenticator/interfaces/domain_types.authenticationrequest.html#user)

So you should test this and either respond with a challenge form to ask the user for his email or other id and process that info in the processInteraction method or have another way to identify your user (cookies, ip address, 
) using the other elements of the AuthenticationRequest

Review the code sample of the WAYF use case at src/samples/whereareyoufrom-auth.ts · master · Guenole Projects / Keycloak / Rest Authenticator · GitLab

Please allow me to ping @myr in since a similar discussion is ongoing.

1 Like

Thanks for your time.I got it whats your point. What about the Username Password form ? Do I have to modified it? Cause its blocking me if user does not exist.

image

BTW of course you can ping anyone you want :slight_smile:

Hi @ygtysr

In the authentication flow you should have in that order

  • authentication via session (alternative)
  • authentication via Kerberos (alternative but i guess you are not using it so tou can remove it)
  • authentication via identity providers (alternative)
  • authentication via your external service (alternative)
  • forms (alternative) - with inside forms and checks

So your service gets hit before the conventional forms are displayed. And you can display the forms you like , such as asking for the user name and optionally the password

Can you screen shot your flows and explain what your external service does?

1 Like

Actually there is no implantation yet, I am in discovery stage :slight_smile:
I did implement example that you put in article.
I observed, if username or password wrong then flow never direct to my service.
I need like a fall-through mechanism;

  1. Is it contain in Keycloak.If dont,
  2. Then check rest authenticator for external idp has it or not.
  3. If external idp has it, then authenticate user and return id_token, access_token and refresh_token.

My current authentication flow is;

Thank you so much

Ok so you are at the stage where we need to reason on the sequence of the actions.

So could you clarify a couple of points

  • your external IDP (reachable va APIs) contains all or a portions of your users ?
  • if authenticated once with your external IDP, do you expect the user to be added in keycloak for future authentication (like in a migration scenario)?

Regarding the current flow of your screenshot, the “rest authenticator” is called but receives no information about the user. So, rather than adding the “user password form” below, it is the rest authenticator that should respond with this form as a challenge so your rest authenticator can receive the username and password.

So your authenticator can return something like

      return {
        authNote: { someState: "data that you will retrieve at next call" },
        challenge: {
          formName: "login.ftl",
          message: "Enter your username and password",
          formData: {
            login: {
              username: "username that you could deduce from an hint to prefill the form"
            }
          }
        }

See the list of forms that exists for a challenge response here: keycloak/themes/src/main/resources/theme/base/login at 269a72d672c9b391d6ca44c69dad3c109384eed5 · keycloak/keycloak · GitHub

Then when the form is submitted it is your rest authenticator that will receive the form values (username and password) in the AuthenticationRequest.httpRequest.formParams

1 Like

Looks like another great features.
I got your suggestion. This way I will able to be the one who provide login screen and I will able to get username, password etc. but my inference is, it is not possible to generate any token without user information. It is possible with adding those “foreign” users into keycloak. My point is I can not get id_token and access_token if user does not exists in Keycloak.This is the reason why you did suggest ;

  • " if authenticated once with your external IDP, do you expect the user to be added in keycloak for future authentication (like in a migration scenario)?"

So, I have to consider about migrating scenario. Maybe I will add users in keycloak for a while and after they logout or their session terminated, I can delete users from keycloak. What you think about that flow.Is it sense ?

Hi @ygtysr

Happy that you find my approach interesting.

If I am not mistaking, if you authenticate your users directly against your own system, then keycloak would not be able to create tokens since it has no user in its session. This was something I did not consider in my initial use cases, so with version 0.1.x it is not possible, unless to feed keycloak using its admin APIs.

I just realize that and created the version 0.2.0 of the library to handle this, creating the user in keycloak on the authenticator’s response when some info are provided

So, update the library to version 0.2.0 and use the following code example (typescript)

import {
  KeycloakRestAuthenticator,
  AuthenticationRequest,
  AuthenticationResponse,
} from "keycloak-rest-authenticator";

export class UserPassword implements KeycloakRestAuthenticator {
  async processNew(
    req: AuthenticationRequest
  ): Promise<AuthenticationResponse | null> {
    return {
      challenge: {
        formName: "login.ftl",
        message: "Enter your username and password",
        formData: {
          login: {
            username: "user"
          }
        }
      },
    }
  }
  async processInteraction(
    req: AuthenticationRequest
  ): Promise<AuthenticationResponse | null> {
    if(req.httpRequest.formParams.password.join('') === "test password") {
      return {
        // this info will be used to create the user in keycloak
        user: {
          username: req.httpRequest.formParams.username.join(''),
          email: "myemail@yahoo.fr",
          firstName: "Guénolé",
          lastName: "de Cadoudal"
        },
      };
    } else {
      return {
        failure: "invalidCredentials"
      }
    }
  }
  async processInterruption(
    req: AuthenticationRequest
  ): Promise<AuthenticationResponse | null> {
    throw new Error("Method not implemented.");
  }
}

I hope this helps

1 Like

And for your migration point, it depends wether your legacy system is there to stay or wether you intend to put your users info and credentials in keycloak in the long run.

Assuming you wand to migrate, there are a couple of points to consider

  • your custom authenticator is before the default Browser Forms so it must know if the user is already created in keycloak
  • but you do not know the username yet, so you need to ask and refer to keycloak to know if it has been created
  • since you prompted yourself the user, you cannot let the default forms take over the flow
  • you would need to play with a ‘password only form’ and the password authenticator

I have not tested that but I can try if you are interested

1 Like

Thank you so much @guenoledc for all your helps.
I am really so appreciate :slight_smile:
I will test structure that your proposed and of course I will update here for the others.