Skip to content

Instantly share code, notes, and snippets.

@pedroigor
Last active May 14, 2024 19:25
Show Gist options
  • Save pedroigor/4764041132226b5174f7da40108dd2f9 to your computer and use it in GitHub Desktop.
Save pedroigor/4764041132226b5174f7da40108dd2f9 to your computer and use it in GitHub Desktop.

Authenticating Organization Members

The Keycloak Organizations feature introduce changes on how users authenticate to a realm in order to identify whether a user is authenticating in the scope of an organization or the realm.

One of the key changes introduced by the feature in terms of authentication is the introduction of an identity-fist login flow whenever you are authenticating to a realm that has the feature enabled.

In this playbook you will learn about:

  • How identity-first login works and how users are mapped to organizations

  • How the domain set to an organization changes how users authenticate to a realm

  • How the domain set to an identity provider associated with an organization changes how users authenticate to a realm

  • How the authentication flow changes when the user does not exist in a realm or not a member of any organization

  • How the authentication flow changes when the user exist in a realm and not yet a member of any organization

Start Keycloak

The Keycloak Organization feature is a experimental feature that needs to be enabled when starting (or building an optimized image of) the server:

docker run --name kc-orgs -d -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8080:8080 quay.io/keycloak/keycloak:nightly start-dev --features organization

Once you run the command above, make sure you can access the server at http://localhost:8080/ and log in into the administration console using the following credentials:

  • Username: admin

  • Password: admin

You can now follow the instructions in this playbook.

Configure the Admin CLI

In this playbook you are going to use the Keycloak Admin CLI to connect to the server instance and manage the realms. For that, run the command below to authenticate and configure the CLI:

kcadm.sh config credentials --server http://localhost:8080 --realm master --user admin

Once you run the command above, type admin when prompted for a password.

Create a realm

Let us start by creating a new realm called orgdemo:

kcadm.sh create realms -f - << EOF
{
  "realm": "orgdemo",
  "enabled": true
}
EOF

In this playbook, the orgdemo realm is a first-party company that wants to integrate with third-parties, the organizations, so that their users can have access to protected resources served by client applications available at the orgdemo realm.

Create users in the orgdemo realm

You also need some users in the orgdemo realm to authenticate and follow the next steps.

The mjane user is a realm user that has a email that does not match any organization in the realm. We will use this user to represent an existing realm user in the orgdemo realm that is not associated with any organization:

kcadm.sh create realms/orgdemo/users -f - << EOF
{
  "username": "mjane",
  "email": "mjane@orgdemo.com",
  "firstName": "Mary",
  "lastName": "Jane",
  "enabled": true,
  "credentials" : [
    {
      "type" : "password",
      "value" : "password"
    }
  ]
}
EOF

The alice@orga.com user is a user that uses one of the domains set to an organization. This user is an existing realm user that has an email that matches one of the domains set to an organization but is not yet a member of the organization. This user could have been created through self-registration, or by integrating with a custom identity store, or even federated from an identity provider available from the realm.

kcadm.sh create realms/orgdemo/users -f - << EOF
{
  "username": "alice",
  "email": "alice@orga.com",
  "firstName": "Alice",
  "lastName": "Chains",
  "enabled": true,
  "credentials" : [
    {
      "type" : "password",
      "value" : "password"
    }
  ]
}
EOF

Understanding the changes to authentication flows when the feature is enabled

When a realm is created, the authentication flows are automatically updated to enable specific steps to authenticate and onboard organization members. The authentication flows updated are:

  • browser

  • first broker login

The main change to the browser flow is that it defaults to an identity-first login so that users are identified before prompting for their credentials.

In regards to the first broker login flow, the main change there is to automatically add the user as an organization member once they authenticate through the identity provider associated with an organization and successfuly complete flow.

The decision to whether or not an identity-firt login should happen is based on the availability of any organization in a realm. If no organizations exist yet, the user will follow the usual steps to authenticate using both username and password, or any other step configured to the browser flow.

Try reaching http://localhost:8080/realms/orgdemo/account and you’ll see the usual login page. From this page, you can authenticate as usual to the realm using the following credentials:

  • Username: mjane

  • Password: password

Once you submit the login form, you are authenticated to the realm and automatically redirected to the client application acting on behalf of the user. In this case, the account console.

Authenticating to a realm when there are organizations

Now, let us create an organization in the orgademo realm:

kcadm.sh create realms/orgdemo/organizations -f - << EOF
{
  "name": "orga",
  "enabled": true,
  "domains": [
    {
      "name": "orga.com"
    }
  ]
}
EOF

Save the id of the realm after executing the command above. You will need it later whenever you see a {orgid} placeholder in the following commands.

Once the orga organization is created, sign out from the client application and try to log in again. At this time, you should be present to the identity-first login page.

Differently than the previous attempt, the orgdemo realm has a organization and the authentication flow changed to first identify the user before prompting for any credentials.

At the identity-first login page you can still authenticate as the mjane user. However, the user will now authenticate in two steps. The first step will ask for the username or email only, and then provide the password in a second step. The same will happen if you try to authenticate using a user that does not exist yet at the orgdemo realm.

Understanding the identity-first login flow

In addition to identify the user once the username is provided, the identity-first login is also responsible to:

  • Match an email domain to an organization

  • Decide if the authentication flow should continue or not if an account already exists for the username provided

  • Decide how the user should be authenticated depending on how the domains and the identity providers are configured to an organization

  • Seamless authenticate users through an identity provider associated with an organization if the email domain matches the domain set to the identity provider

The identity-first login provides the very same capabilities when using the usual login page with both username and password fields. The users can still self-register by clicking on the Register link or choose from any of identity/social providers available at the realm.

The organization domain plays an important role when managing an organization and their members. It helps to map users to an organization by looking at their email address so that an email that uses a domain from an organization can authenticate and register themselves within the scope of the organization.

In the next sections, you will look at the different steps and configuration when authenticating users with or without an account when their email address matches one of the domains of the an organization.

Trying to authenticate as a user that does not exist using an email domain that matches an organization

Try to log in again to http://localhost:8080/realms/orgdemo/account/ and type bob@orga.com. There is no account associated with that email in the orgdemo realm.

If a user that does not exist tries to authenticate using an email domain that matches an organization domain, the identity-first login page will be shown again and indicate that the username provided is not valid. At this point, there is no reason to ask the user for credentials in a second step.

There are several ways to register the user so that he can authenticate to the orgdemo realm and eventually join the orga organization.

If the realm has the self-registration setting enabled, the user can click on the Register link at the identity-first login page and create an account at the orgdemo realm. After that, the administrator can send an invitation link to the user or manually add him as a member of the orga organization. See the [https://gist.github.com/pedroigor/7c14b1e08e180d1f954105bb09c57f84](Onboarding Organization Members using a Registration Link) playbook.

If the organization has an identity provider without a domain set and they are marked as public, they can also click on the identity provider link at the identity-first login page to automatically create an account and joing the orga organization once they authenticate through the identity provider. See the [https://gist.github.com/pedroigor/5696da1f32370f32714fc61844fedec8](Onboarding Organization Members through an Identity Provider) playbook.

Similar to the above, if the organization has an identity provider set with one of the organization domains, the user will be automatically redirected to the identity provider to authenticate and automatically create an account and joing the orga organization once the flow is completed.

Authenticating as an existing user using an email domain that matches an organization

Try to log in again to http://localhost:8080/realms/orgdemo/account/ and type alice@orga.com.

Differently than before, the user is now presented with the second step to provide the credentials. Given that the user exists in the orgdemo realm, it should be possible to authenticate even though the user is not yet a member of the organization.

As an administrator, you can later choose to invite the user to join an organization or manually add it to an organization.

Authenticating as an existing user using an email domain that matches the domain set to an identity provider associated with an organization

The feature allows you to set a domain to an identity provider associated with an organization. This is useful when you want to make sure that users using a specific email domain always authenticate through the identity provider.

For that, run the following commands to create a new orga realm to federate users from it using an identity provider at the orgdemo realm, where the identity provider is linked to the orga organization:

kcadm.sh create realms -f - << EOF
{
  "realm": "orga",
  "enabled": true
}
EOF

kcadm.sh create realms/orga/clients -f - << EOF
{
  "protocol" : "openid-connect",
  "enabled" : true,
  "clientId" : "orgdemo-broker",
  "clientAuthenticatorType" : "client-secret",
  "secret" : "secret",
  "redirectUris" : [ "*" ],
  "standardFlowEnabled" : true,
  "directAccessGrantsEnabled" : true,
  "attributes" : {
    "backchannel.logout.session.required" : "true"
  }
}
EOF

kcadm.sh create realms/orga/users -f - << EOF
{
  "username": "jdoe",
  "email": "jdoe@orga.com",
  "firstName": "John",
  "lastName": "Doe",
  "enabled": true,
  "credentials" : [
    {
      "type" : "password",
      "value" : "password"
    }
  ]
}
EOF

kcadm.sh create realms/orgdemo/identity-provider/instances -f - << EOF
{
  "alias" : "orga",
  "displayName" : "OrgA Inc.",
  "providerId" : "oidc",
  "enabled" : true,
  "config" : {
    "tokenUrl" : "http://localhost:8080/realms/orga/protocol/openid-connect/token",
    "jwksUrl" : "http://localhost:8080/realms/orga/protocol/openid-connect/certs",
    "issuer" : "http://localhost:8080/realms/orga",
    "loginHint" : "true",
    "clientAuthMethod" : "client_secret_post",
    "clientId" : "orgdemo-broker",
    "clientSecret" : "secret",
    "userInfoUrl" : "http://localhost:8080/realms/orga/protocol/openid-connect/userinfo",
    "authorizationUrl" : "http://localhost:8080/realms/orga/protocol/openid-connect/auth",
    "logoutUrl" : "http://localhost:8080/realms/orga/protocol/openid-connect/logout",
    "sendIdTokenOnLogout" : "true",
    "kc.org.domain": "orga.com"
  }
}
EOF

kcadm.sh create realms/orgdemo/organizations/{orgid}/identity-providers -b orga

Try to log in again to http://localhost:8080/realms/orgdemo/account/ and type jdoe@orga.com. The user is now automatically redirected to the orga realm to authenticate.

When a user that does not exist yet in the realm tries to authenticate using an email domain that matches an organization domain, and that domain is also set to the identity provider associated with the organization, the user is automatically redirected to the identity provider.

By doing this, you can now authenticate at the orga realm using the following credentials:

And once the user completes the authentication, it will be automatically redirected back to the orgdemo realm to create an account and automatically join the orga organization.

The same is true if you re-authenticate as the jdoe@orga.com user. However, this time the user is already linked with the identity provider and will always authenticate through the identity provider.

Using organization metadata in bearer tokens to access protected resources from the clients in a realm

So far, we have been using the account console client at the orgdemo realm to authenticate the user. As an OpenID Connect client, an access token is issued as a result of a successful authentication.

When authenticating in the context of an organization, the access token is automatically updated with specific claims about the organization the user is a member.

In order to map organization-specific claims into tokens, a client needs to request the organization scope when sending authorization requests to the server.

As a result, the token will contain a claim as follows:

"organization": {
    "orga": {}
}

The organization claim can be used by clients (e.g.: from ID Tokens) and resource servers (e.g.: from access tokens) to authorize access to protected resources based on the organization that a user belongs to.

The organization scope is a built-in optional client scope at the realm. As such, it is added to any client created in the realm, by default.

Querying organization members

To list all members of an organization, you can run the following command:

kcadm.sh get realms/orgdemo/organizations/{orgid}/members
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment