Give users default roles in registration form, based on a query string parameter

I recently created an application in which we have two separate sign-in/signup buttons.

One button is designed for the owners of the clubs, the other is there for their clients.

Keycloak offers default role per realm that can be configured via the admin panel.

However, in our case, we need to be able to set roles, based on the button that the user clicks on.

We want to give them default roles, based on a query string parameter.

I can’t find anything for this. Is it possible? How do you guys support multiple signup buttons for different roles in your system?

1 Like

You want to let the user register himself in keycloak and have this user have a role based on that after the registration?

The way I see it, you can treat this problem in two steps. First, user registration (and login), and after login, you can call keycloak API to give the user the role. See Keycloak Admin REST API

For that, you need your app to have an API key for keycloak Admin API, with is different for the credentials used to login users via openid connect (the normal keycloak use).

Thank you. But this is not what I want. I want to complete user registration (two steps) in Keycloak and then send him back to the application.

I already knew about Keycloak’s admin rest API and I already use it for some scenarios (for example synching user names and emails). However, in this case, I want to provide TWO buttons on my site, saying “Are you an owner? Signup here. Are you a client? Signup here”.

And based on the clicked button, I want to send parameters to Keycloak.

One solution is that I create different realms for owners and clients and create default roles for each realm.

But that prevents me from serving users who are both club owners and also clients of other clubs.

Got it.

My idea (option 1):

  • App shows two buttons (owner and client)
  • User clicks the button (STEP A) and application remembers that.
  • User gets redirected to the normal registration flow on keycloak.
  • User gets redirected back to your application.
  • You observe that the user is now authenticated and proceed to to backend api request to keycloak REST api.
  • Based on the button clicked on STEP A, you set the roles accordingly.

Option 2:

  • you implement an authentication execution using keycloak’s SPI
  • you send a custom query parameter with the authentication request (client_type=owner, client_type=client)
  • your custom authentication execution is configured to run as last step, after user is registrated and authenticated
  • based on the client_type parameter, the custom execution sets the role.

Option 1 is probably easier to write and maintain. Option 2 is “cleaner” and supports single page applications.

1 Like

This might work:

You can add some query parameter to the regist* er url.

  • In your theme, parse (with the help of JS) the query params from the location and put your custom one into a hidden custom field, so that it becomes a user attribute (see Server Developer Guide).
  • Then, create a custom event listener, listen to the register event, get the user attributes (they are probably not in the event) and set the roles depending on the attribute.
  • Caveat: I don’t know if the user has then the default roles on their first login, this you’ll have to prove first. Maybe the event listener is too late for the first login.

I did not know about the second option. That seems the correct way to go. I start investigating it. Thank you so much for your help.

This has the downside of having the security risk. Someone can change the query parameter to be “admin” and register as admin. We still need some sort of backend check to make sure only end-users get roles and not superuser (Admins, Support Operators, etc.)

Of course you must not take the query param 1:1 as a role, but setting roles depending on the query param. You can do all of your validation logic in the event listener.

Yes, but you are providing your users with a self-service registration. If you show your users an option labeled “click here if you are an admin”, they will register as admin. How will you deal with that?

I suppose you can let the user decide himself if he is an owner of a client, but not an admin.

If you need to validate if the user is actually an owner, or a client or an admin, you don’t need all the fuss, just show a button “register here” and do the checks on the backend to decide if he is an owner or client or admin based on some user database and use Keycloak’s REST API to set the roles.

This registration is only for owners and clients. We don’t care if they choose to click what button. Admins and other super users are created directly using the Keycloak’s Admin Panel. Thus we won’t be accepting any role other than owner or client. Users can replace the query string with admin, but that won’t be applied. Because we would implement the server side’s logic to check it. That’s my point.

1 Like

And thus, as you implement the server side logic, it’s secure. No user can misuse an admin role. That’s exactly what I wrote. Or do we talk cross purposes?

Hi, @dasniko, apologies for resurrecting this old thread, but your recommendation is exactly what I’m implementing for a different use-case scenario.

  1. links to the registration page might have an utm_source query parameter for referrals
  2. a small javascript function parses the url and sets a hidden input field’s value so I can save it as a user attribute

This works fine, but what I found is that if a username is already taken, Keycloak reloads the registration page this time without the query parameter arguments. That way the referrer information is lost when the customer registers with an available username on the second try.

Do you have any advice on how to overcome this issue?

EDIT:
After posting the question I realized that I could solve this by saving the utm_source query param to sessionStorage. Even if the registration fails and ?utm_source= goes missing from the url the script can double check sessionStorage for the value.

:+1:
I’d have suggested a similar way…