-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement "Select Organization" Authenticator. (#172)
* Implement "Select Organization" Authenticator. - Added ActiveOrganizationAuthenticator - Added ActiveOrganizationAuthenticatorFactory - Added "select-organization.ftl" - Added Documentation - Did Manual Testing - Add built-in browser and direct grant flows - Add Cypress tests - Extend documentation - Rebased on main - Fix conflict, Update tests according to v24 changes, add auth flow creation on PostMigrationEvent - Rework code of ActiveOrganizationAuthenticator - remove useless lines of code, add debug logs - revert cypress testcontainers config changes --- Manual Testing report: Browser Flow **Authenticator is Enabled** - prompt=select_account is not used - User has no Organizations = User is not required to select an organization. OK - User has 1 or more Organization = User is not required to select an organization. OK - prompt=select_account is used - User has no Organizations = User get "You are not part of any organization, Contact an Administrator." error. OK - User has 1 Organization = User doesn't have to select an organization. OK - User has 2 Organization = User have to select an organization. OK - account_hint=<org-id> is used (with a right organization) - User has no Organizations = User get "Invalid Organization." error. OK - User has 1 or more Organization = User doesn't have to select an organization. OK - account_hint=<org-id> is used (with a wrong organization) - All users get "Invalid Organization." error. OK **Authenticator is Disabled** - prompt=select_account is used - Users doesn't have to choose an organization. OK - account_hint=<org-id> is used - Account hint is ignored. OK Direct Grant Flow **Authenticator is Enabled** - account_hint=<org-id> is used with a right organization - Authentication is successfull. - account_hint=<org-id> is used with a wrong organization - Authentication fails with a 401 status. All case In all case, if the authentication is successfull, the attribute is updated. In all case, if the authentication is failure, the attribute isn't updated. * refactor - clean code * Put Cypress tests under cypress-tests profile, Fix tests conflicts by creating an AbstractCypressOrganizationTest (separate container), Update cypress documentation
- Loading branch information
Showing
48 changed files
with
3,456 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
# Active Organizations Authenticator | ||
|
||
## Contents | ||
<!-- TOC --> | ||
* [Active Organizations Authenticator](#active-organizations-authenticator) | ||
* [Contents](#contents) | ||
* [Overview](#overview) | ||
* [Explanation](#explanation) | ||
* [prompt=select_account](#promptselect_account) | ||
* [account_hint](#account_hint) | ||
* [account switching](#account-switching) | ||
* [Built-in Flows](#built-in-flows) | ||
* [Select Org Browser Flow](#select-org-browser-flow) | ||
* [Configuration](#configuration) | ||
* [Flow](#flow) | ||
* [Select Org Direct Grant Flow](#select-org-direct-grant-flow) | ||
* [Configuration](#configuration-1) | ||
* [Flow](#flow-1) | ||
* [Configuration & Example](#configuration--example) | ||
* [Browser Flow (Configuration)](#browser-flow-configuration) | ||
* [Examples (Tests)](#examples-tests) | ||
* [Direct Grant Flow (Configuration)](#direct-grant-flow-configuration) | ||
<!-- TOC --> | ||
|
||
## Overview | ||
This documentation is dedicated to the use of "Select Organization" Authenticator. | ||
Note that to have the active_organization information into tokens, you still need the "Active Organization" mapper. | ||
|
||
## Explanation | ||
You can add the `Select Organization` authenticator into the user authentication flow. | ||
With this authenticator enabled, you can pass the query parameters: | ||
- `prompt=select_account` | ||
- `account_hint={ORG-ID}` | ||
|
||
In all case, if the use try to access an organization where he isn't a member, an error will be returned. | ||
|
||
|
||
### prompt=select_account | ||
If the `prompt=select_account` is given for the authentication flow, the user will be requested to select an organization during login. | ||
Note that: | ||
- If the user doesn't have any organization, an error is returned. | ||
- If the user has only 1 organization, the selection will be skipped, and it will use this organization. | ||
|
||
_Example of authentication request:_ | ||
- `{HOSTNAME}/realms/{REALM}/protocol/openid-connect/auth?response_type=code&client_id={PUBLIC-CLIENT}&scope=openid&redirect_uri={HOSTNAME}/realms/{REALM}/account&prompt=select_account` | ||
|
||
|
||
### account_hint | ||
If the `account_hint={ORG-ID}` is given for the authentication flow, it will use the given organization without having to select one. | ||
_Example of authentication request:_ | ||
- `{HOSTNAME}/realms/{REALM}/protocol/openid-connect/auth?response_type=code&client_id={PUBLIC-CLIENT}&scope=openid&redirect_uri={HOSTNAME}/realms/{REALM}/account&account_hint={ORG-ID}` | ||
|
||
_or with the `prompt=select_account`_: | ||
- `{HOSTNAME}/realms/{REALM}/protocol/openid-connect/auth?response_type=code&client_id={PUBLIC-CLIENT}&scope=openid&redirect_uri={HOSTNAME}/realms/{REALM}/account&prompt=select_account&account_hint={ORG-ID}` | ||
|
||
|
||
### account switching | ||
To **switch account based on account_hint**, you will need to define the 'Select Organization' step after the Cookie step. For that, you will need to | ||
define a Sub-Flow like this: | ||
![account-switch](assets/active-organization-authenticator/account-switch.png) | ||
|
||
Then making an authentication request with **account_hint**: | ||
- `{HOSTNAME}/realms/{REALM}/protocol/openid-connect/auth?response_type=code&client_id={PUBLIC-CLIENT}&scope=openid&redirect_uri={HOSTNAME}/realms/{REALM}/account&account_hint={ORG-ID}` | ||
|
||
Will skip the select organization form and switch the organization (change user's attribute). | ||
|
||
|
||
## Built-in Flows | ||
A **browser flow** and **direct grant flow** have been added. They are similar to the default Keycloak built-in flows but include the Select Organization step. | ||
|
||
![built-in-flows](assets/active-organization-authenticator/built-in-flows.png) | ||
|
||
### Select Org Browser Flow | ||
#### Configuration | ||
![browser-config](assets/active-organization-authenticator/browser-config.png) | ||
|
||
#### Flow | ||
![browser-flow](assets/active-organization-authenticator/browser-flow.png) | ||
|
||
### Select Org Direct Grant Flow | ||
#### Configuration | ||
![direct-grant-config](assets/active-organization-authenticator/direct-grant-config.png) | ||
|
||
#### Flow | ||
![direct-grant-flow](assets/active-organization-authenticator/direct-grant-flow.png) | ||
|
||
|
||
## Configuration & Example | ||
|
||
### Browser Flow (Configuration) | ||
To configure the Browser Flow, you need to duplicate the default `Browser flow` and modify it. | ||
In the `forms` part _(with Username Password Form)_, select `Add Step`. | ||
|
||
![add-step](assets/active-organization-authenticator/add-step.png) | ||
|
||
In the selection, search and select `Select Organization`. | ||
|
||
![select-org-step](assets/active-organization-authenticator/select-org-step.png) | ||
|
||
Once selected, enable it by setting it to `Required`. | ||
|
||
![enable-select-organization](assets/active-organization-authenticator/enable-select-organization.png) | ||
|
||
Don't forget to bind this flow to "Browser flow". | ||
|
||
![bind-flow](assets/active-organization-authenticator/bind-flow.png) | ||
|
||
#### Examples (Tests) | ||
Once configured, you can test the authenticator with an authentication request including `prompt=select_account`. | ||
**Example:** `{HOSTNAME}/realms/{REALM}/protocol/openid-connect/auth?response_type=code&client_id={PUBLIC-CLIENT}&scope=openid&redirect_uri={HOSTNAME}/realms/{REALM}/account&prompt=select_account`. | ||
|
||
Once the user enter his username (or email) and password, he will have a drop-down to select an organization. | ||
|
||
![org-choice-1](assets/active-organization-authenticator/org-choice-1.png) | ||
|
||
![org-choice-2](assets/active-organization-authenticator/org-choice-2.png) | ||
|
||
If the user has no organization, instead of the selection step, he will get an Error message. | ||
|
||
![no-org](assets/active-organization-authenticator/no-org.png) | ||
|
||
If the user try to access an organization where is not a member (with `account_hint` for example), he will also get an error message. | ||
|
||
![not-member](assets/active-organization-authenticator/not-member.png) | ||
|
||
Not that doing the authentication request with `account_hint={ORG-ID}` will skip the selection during log-in but still verify that the user has an organization or has membership. | ||
|
||
|
||
### Direct Grant Flow (Configuration) | ||
|
||
`account_hint={ORG-ID}` of "Select Organization" authenticator can also be used for a `Direct Grant` flow. | ||
For that, apply the same configuration as the browser flow to the direct grant flow. | ||
|
||
![direct-grant](assets/active-organization-authenticator/direct-grant.png) | ||
|
||
Then, when you make the direct grant authentication, you can pass the query parameter `account_hint{ORG-ID}` and it will be used to define the active_organization. | ||
|
||
**Example:** | ||
`{{HOSTNAME}}/realms/{{REALM}}/protocol/openid-connect/token?account_hint={ORG-ID}` | ||
|
||
![direct-grant-2](assets/active-organization-authenticator/direct-grant-2.png) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+47.1 KB
docs/assets/active-organization-authenticator/direct-grant-config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+61.8 KB
docs/assets/active-organization-authenticator/enable-select-organization.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# Cypress Tests | ||
|
||
## Contents | ||
<!-- TOC --> | ||
* [Cypress Tests](#cypress-tests) | ||
* [Contents](#contents) | ||
* [Setup](#setup) | ||
* [pnpm](#pnpm) | ||
* [cypress.config.ts](#cypressconfigts) | ||
* [Running with Cypress](#running-with-cypress) | ||
* [Cypress Testcontainers](#cypress-testcontainers) | ||
<!-- TOC --> | ||
|
||
## Setup | ||
Versions used: | ||
- Node: **20.9.0** | ||
- npm: **10.2.3** | ||
- pnpm: **8.10.2** | ||
|
||
### pnpm | ||
Cypress setup use pnpm, you will first need to install it: | ||
```bash | ||
npm install -g pnpm | ||
``` | ||
|
||
Then in the `test/e2e` folder, install the dependencies | ||
```bash | ||
pnpm install | ||
``` | ||
|
||
### cypress.config.ts | ||
To configure cypress for you setup, modify `cypress.config.ts`. | ||
```typescript | ||
export default defineConfig({ | ||
e2e: { | ||
baseUrl: 'http://localhost:8080/', | ||
reporter: 'cypress-multi-reporters', | ||
reporterOptions: { | ||
configFile: 'reporter-config.json' | ||
} | ||
}, | ||
}); | ||
``` | ||
For example, if your Keycloak instance or container is not running on http://localhost:8080/, modify it. | ||
|
||
## Running the Cypress test | ||
Cypress test are under a specific profile. To run them with maven use `-P cypress-tests`. | ||
Example: `maven clean test -P cypress-tests` | ||
|
||
## Running with Cypress | ||
After the setting up your environment and configuring your `cypress.config.ts`. | ||
You start cypress UI by executing the command | ||
```bash | ||
npx cypress open | ||
``` | ||
in `test/e2e` folder. | ||
|
||
You should get the Cypress UI. | ||
![cypress](assets/cypress.png) | ||
|
||
Then, click on `E2E Testing`, choose the browser you want and click on `Start E2E Testing in ...`. | ||
After that, you can choose any `*.cy.ts` file in `Specs` to run the tests and debug. | ||
|
||
## Cypress Testcontainers | ||
A Cypress testcontainers based on https://github.com/wimdeblauwe/testcontainers-cypress is used. | ||
It populates dynamically the baseUrl with the Keycloak testcontainers instance and run all tests in the | ||
`test/e2e` folder. | ||
|
||
To run the tests with the testcontainers, run the `runCypressTests()` in `CypressOrganizationTest.java` like any Junit test. |
Oops, something went wrong.