Is it possible to have a kind of Invite users functionality for non-organization Keycloak users in similar way as it is supported by Invite member functionality for a user in organization?
Basically what I want is when user clicks the invitation link, he/she should enter first and last name, password or use an IdentityProvider and automatically be logged in in Keycloak at the end of flow. However, I want that public registration (without invitation mail) is not accessible.
If I use execute-actions-email to simulate invitation, it seems that the user is not logged in keycloak and have to enter username/password once more on login. Also IdentityProvider login is not straitforward in this case.
I looking through keycloak code, an can generate my custom token similar and handler in similar way as it is done by OrganizationInvitationResource in keyclock, but currently got an error on registration, that it is disabled. However, organisation invitation linke works in such configuration.
If I enable registration on the realm, there is another error in RegistrationPage.render that somehow requires organization, even if it Organizations are disabled.
Keycloak version 26.2.5
Thanks.
We looked at a similar idea for keycloak-orgs. We never implemented it, but the idea would be:
create a new invitations table in the DB and a JpaEntityProvider implementation so you can store invitations.
create a REST resource that can create a token to be embedded in a link with the invitation ID and email/username of the user being invited. This can optionally send an email that is formatted by custom .ftl. The included link should be to another REST endpoint….
create a REST resource that is the destination of the link sent in the invitation. The resource decodes the token and associates it with the invitation record in the database. It creates a User, and sets the UPDATE_PROFILE required action. Then it logs in the user (similar to how our keycloak-magic-link extension does it) which causes them to be challenged to complete their profile. Finally, it cleans up the invitation from the database.
So all this really does is create something like an action token flow, but you can’t use Keycloak’s ActionTokenHandler abstraction, since they require the user to already exist. Anyway, probably more work than you wanted, but we couldn’t figure out a better way.
Thank you for the very detailed answer. If possible, I’d like to elaborate on your proposal in a bit more detail.
I might be missing something since I’m not fully familiar with Keycloak. Why do we need an invitations table? Wouldn’t a custom ActionToken with an expiration be sufficient? From what I see in the Keycloak code, there doesn’t seem to be a DB record for invited members in Organizations.
My idea was to use the registration flow, but protected with a custom token. Do you think this would be a bad approach?
For OIDC/IdentityProvider first login via an invitation, I was considering restricting new user creation during the first login flow, allowing it only when a valid custom token is present.
What do you think? I’d really appreciate your feedback. Thanks!
If you don’t want the person who has been invited to be a User in Keycloak before they click on the link, it can’t be an ActionToken (which must encode an existing User). Then you must create a different pathway to allow them to create their User.
If you’re comfortable creating a User before “inviting” them, then you can just create a user, give them the UPDATE_PROFILE required action, and then send them a reset password email.
For an Org invitation, it’s not an ActionToken, but a special ORGIVT token type. That’s different to action tokens. Additionally the ORGIVT token does not contain any data of the user but the email address. No other data is preserved.
In fact, the org invitation works as you describe your desired use case. The registration endpoint is called with a custom token (ORGIVT)… but the ORGIVT token is especially for organizations. You could implement some custom authenticator(s) to build up something similar, but expect this to be complex if you want to preserve and support all the default behavior.
Currently, I’m experimenting with the following prototype:
Custom Keycloak add-ons
Admin REST resource that generates a custom token and registration URL with an invite query parameter, e.g. http://localhost:9090/realms/my-realm/protocol/openid-connect/registrations?response_type=code&client_id=account&invite={token}
InvitationTokenAuthenticator – checks if the invite query parameter is set. If yes, it saves data in session.authNote. If not, it shows an error.
InviteTokenFormAction – reads the token from session.authNote and puts it into a form attribute.
Custom theme register.ftl – lists IDP links and adds the invite query parameter to each IDP link.
Custom theme login.ftl – removes the default register link. Maybe there’s a better way to do this?
Custom authentication flows
Custom Registration flow – starts with InvitationTokenAuthenticator and InviteTokenFormAction steps. The flow should be associated with the realm registration flow.
Custom first broker login flow – starts with InvitationTokenAuthenticator. The flow should be associated with the required IDPs
Basic idea
If a user lands on the registration page without invite={token}, they see an error. If the invite token is valid, the registration page is shown with additional IDP links. The user can then register with username/password or sign in through an IDP using the invitation link.
It’s a bit more complex than I hoped, but the prototype looks promising.
What do you think — does this approach make sense, or do you see simpler alternatives? Do you also see any security concerns with it?
It looks like there is no need to provide an invitation token in IDP url in registration page. Custom Registration flow can set a custom attribute in authSession.clientNote Custom first broker login flow should check if authSession.clientNote contains the attribute