Browse Source
This has been moved over to the wiki: https://github.com/dani-garcia/vaultwarden/wiki/Enabling-SSO-support-using-OpenId-Connectpull/5934/head^2
committed by
GitHub
1 changed files with 0 additions and 303 deletions
@ -1,303 +0,0 @@ |
|||||
# SSO using OpenId Connect |
|
||||
|
|
||||
To use an external source of authentication your SSO will need to support OpenID Connect : |
|
||||
|
|
||||
- An OpenID Connect Discovery endpoint should be available |
|
||||
- Client authentication will be done using Id and Secret. |
|
||||
|
|
||||
A master password will still be required and not controlled by the SSO (depending on your point of view this might be a feature ;). |
|
||||
This introduces another way to control who can use the vault without having to use invitation or using an LDAP. |
|
||||
|
|
||||
## Configuration |
|
||||
|
|
||||
The following configurations are available |
|
||||
|
|
||||
- `SSO_ENABLED` : Activate the SSO |
|
||||
- `SSO_ONLY` : disable email+Master password authentication |
|
||||
- `SSO_SIGNUPS_MATCH_EMAIL`: On SSO Signup if a user with a matching email already exists make the association (default `true`) |
|
||||
- `SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION`: Allow unknown email verification status (default `false`). Allowing this with `SSO_SIGNUPS_MATCH_EMAIL` open potential account takeover. |
|
||||
- `SSO_AUTHORITY` : the OpenID Connect Discovery endpoint of your SSO |
|
||||
- Should not include the `/.well-known/openid-configuration` part and no trailing `/` |
|
||||
- $SSO_AUTHORITY/.well-known/openid-configuration should return the a json document: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse |
|
||||
- `SSO_SCOPES` : Optional, allow to override scopes if needed (default `"email profile"`) |
|
||||
- `SSO_AUTHORIZE_EXTRA_PARAMS` : Optional, allow to add extra parameter to the authorize redirection (default `""`) |
|
||||
- `SSO_PKCE`: Activate PKCE for the Auth Code flow (default `true`). |
|
||||
- `SSO_AUDIENCE_TRUSTED`: Optional, Regex to trust additional audience for the IdToken (`client_id` is always trusted). Use single quote when writing the regex: `'^$'`. |
|
||||
- `SSO_CLIENT_ID` : Client Id |
|
||||
- `SSO_CLIENT_SECRET` : Client Secret |
|
||||
- `SSO_MASTER_PASSWORD_POLICY`: Optional Master password policy (`enforceOnLogin` is not supported). |
|
||||
- `SSO_AUTH_ONLY_NOT_SESSION`: Enable to use SSO only for authentication not session lifecycle |
|
||||
- `SSO_CLIENT_CACHE_EXPIRATION`: Cache calls to the discovery endpoint, duration in seconds, `0` to disable (default `0`); |
|
||||
- `SSO_DEBUG_TOKENS`: Log all tokens for easier debugging (default `false`, `LOG_LEVEL=debug` or `LOG_LEVEL=info,vaultwarden::sso=debug` need to be set) |
|
||||
|
|
||||
The callback url is : `https://your.domain/identity/connect/oidc-signin` |
|
||||
|
|
||||
## Account and Email handling |
|
||||
|
|
||||
When logging in with SSO an identifier (`{iss}/{sub}` claims from the IdToken) is saved in a separate table (`sso_users`). |
|
||||
This is used to link to the SSO provider identifier without changing the default user `uuid`. This is needed because: |
|
||||
|
|
||||
- Storing the SSO identifier is important to prevent account takeover due to email change. |
|
||||
- We can't use the identifier as the User uuid since it's way longer (Max 255 chars for the `sub` part, cf [spec](https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken)). |
|
||||
- We want to be able to associate existing account based on `email` but only when the user logs in for the first time (controlled by `SSO_SIGNUPS_MATCH_EMAIL`). |
|
||||
- We need to be able to associate with existing stub account, such as the one created when inviting a user to an org (association is possible only if the user does not have a private key). |
|
||||
|
|
||||
Additionally: |
|
||||
|
|
||||
- Signup will be blocked if the Provider reports the email as `unverified`. |
|
||||
- Changing the email needs to be done by the user since it requires updating the `key`. |
|
||||
On login if the email returned by the provider is not the one saved an email will be sent to the user to ask him to update it. |
|
||||
- If set, `SIGNUPS_DOMAINS_WHITELIST` is applied on SSO signup and when attempting to change the email. |
|
||||
|
|
||||
This means that if you ever need to change the provider url or the provider itself; you'll have to first delete the association |
|
||||
then ensure that `SSO_SIGNUPS_MATCH_EMAIL` is activated to allow a new association. |
|
||||
|
|
||||
To delete the association (this has no impact on the `Vaultwarden` user): |
|
||||
|
|
||||
```sql |
|
||||
TRUNCATE TABLE sso_users; |
|
||||
``` |
|
||||
|
|
||||
### On `SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION` |
|
||||
|
|
||||
If your provider does not send the verification status of emails (`email_verified` [claim](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)) you will need to activate this setting. |
|
||||
|
|
||||
If set with `SSO_SIGNUPS_MATCH_EMAIL=true` (the default), then a user can associate with an existing, non-SSO account, even if they do not control the email address. |
|
||||
This allow a user to gain access to sensitive information but the master password is still required to read the passwords. |
|
||||
|
|
||||
As such when using `SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION` it is recommended to disable `SSO_SIGNUPS_MATCH_EMAIL`. |
|
||||
If you need to associate non sso users try to keep both settings activated for the shortest time possible. |
|
||||
|
|
||||
## Client Cache |
|
||||
|
|
||||
By default the client cache is disabled since it can cause issues with the signing keys. |
|
||||
\ |
|
||||
This means that the discovery endpoint will be called again each time we need to interact with the provider (generating authorize_url, exchange the authorize code, refresh tokens). |
|
||||
This is suboptimal so the `SSO_CLIENT_CACHE_EXPIRATION` allows you to configure an expiration that should work for your provider. |
|
||||
|
|
||||
As a protection against a misconfigured expiration if the validation of the `IdToken` fails then the client cache is invalidated (but you'll periodically have an unlucky user ^^). |
|
||||
|
|
||||
### Google example (Rolling keys) |
|
||||
|
|
||||
If we take Google as an example checking the discovery [endpoint](https://accounts.google.com/.well-known/openid-configuration) response headers we can see that the `max-age` of the cache control is set to `3600` seconds. And the [jwk_uri](https://www.googleapis.com/oauth2/v3/certs) response headers usually contain a `max-age` with an even bigger value. |
|
||||
/ |
|
||||
Combined with user [feedback](https://github.com/ramosbugs/openidconnect-rs/issues/152) we can conclude that Google will roll the signing keys each week. |
|
||||
|
|
||||
Setting the cache expiration too high has diminishing return but using something like `600` (10 min) should provide plenty benefits. |
|
||||
|
|
||||
### Rolling keys manually |
|
||||
|
|
||||
If you want to roll the used key, first add a new one but do not immediately start signing with it. |
|
||||
Wait for the delay you configured in `SSO_CLIENT_CACHE_EXPIRATION` then you can start signing with it. |
|
||||
|
|
||||
As mentioned in the Google example setting too high of a value has diminishing return even if you do not plan to roll the keys. |
|
||||
|
|
||||
## Keycloak |
|
||||
|
|
||||
Default access token lifetime might be only `5min`, set a longer value otherwise it will collide with `Bitwarden` front-end expiration detection which is also set at `5min`. |
|
||||
\ |
|
||||
At the realm level |
|
||||
|
|
||||
- `Realm settings / Tokens / Access Token Lifespan` to at least `10min` (`accessTokenLifespan` setting when using `kcadm.sh`). |
|
||||
- `Realm settings / Sessions / SSO Session Idle/Max` for the Refresh token lifetime |
|
||||
|
|
||||
Or for a specific client in `Clients / Client details / Advanced / Advanced settings` you can find `Access Token Lifespan` and `Client Session Idle/Max`. |
|
||||
|
|
||||
Server configuration, nothing specific just set: |
|
||||
|
|
||||
- `SSO_AUTHORITY=https://${domain}/realms/${realm_name}` |
|
||||
- `SSO_CLIENT_ID` |
|
||||
- `SSO_CLIENT_SECRET` |
|
||||
|
|
||||
### Testing |
|
||||
|
|
||||
If you want to run a testing instance of Keycloak the Playwright [docker-compose](playwright/docker-compose.yml) can be used. |
|
||||
\ |
|
||||
More details on how to use it in [README.md](playwright/README.md#openid-connect-test-setup). |
|
||||
|
|
||||
## Auth0 |
|
||||
|
|
||||
Not working due to the following issue https://github.com/ramosbugs/openidconnect-rs/issues/23 (they appear not to follow the spec). |
|
||||
A feature flag is available (`oidc-accept-rfc3339-timestamps`) to bypass the issue but you will need to compile the server with it. |
|
||||
There is no plan at the moment to either always activate the feature nor make a specific distribution for Auth0. |
|
||||
|
|
||||
## Authelia |
|
||||
|
|
||||
To obtain a `refresh_token` to be able to extend session you'll need to add the `offline_access` scope. |
|
||||
|
|
||||
Config will look like: |
|
||||
|
|
||||
- `SSO_SCOPES="email profile offline_access"` |
|
||||
|
|
||||
|
|
||||
## Authentik |
|
||||
|
|
||||
Default access token lifetime might be only `5min`, set a longer value otherwise it will collide with `Bitwarden` front-end expiration detection which is also set at `5min`. |
|
||||
\ |
|
||||
To change the tokens expiration go to `Applications / Providers / Edit / Advanced protocol settings`. |
|
||||
|
|
||||
Starting with `2024.2` version you will need to add the `offline_access` scope and ensure it's selected in `Applications / Providers / Edit / Advanced protocol settings / Scopes` ([Doc](https://docs.goauthentik.io/docs/providers/oauth2/#authorization_code)). |
|
||||
|
|
||||
Server configuration should look like: |
|
||||
|
|
||||
- `SSO_AUTHORITY=https://${domain}/application/o/${application_name}/` : trailing `/` is important |
|
||||
- `SSO_SCOPES="email profile offline_access"` |
|
||||
- `SSO_CLIENT_ID` |
|
||||
- `SSO_CLIENT_SECRET` |
|
||||
|
|
||||
## Casdoor |
|
||||
|
|
||||
Since version [v1.639.0](https://github.com/casdoor/casdoor/releases/tag/v1.639.0) should work (Tested with version [v1.686.0](https://github.com/casdoor/casdoor/releases/tag/v1.686.0)). |
|
||||
When creating the application you will need to select the `Token format -> JWT-Standard`. |
|
||||
|
|
||||
Then configure your server with: |
|
||||
|
|
||||
- `SSO_AUTHORITY=https://${provider_host}` |
|
||||
- `SSO_CLIENT_ID` |
|
||||
- `SSO_CLIENT_SECRET` |
|
||||
|
|
||||
## GitLab |
|
||||
|
|
||||
Create an application in your Gitlab Settings with |
|
||||
|
|
||||
- `redirectURI`: https://your.domain/identity/connect/oidc-signin |
|
||||
- `Confidential`: `true` |
|
||||
- `scopes`: `openid`, `profile`, `email` |
|
||||
|
|
||||
Then configure your server with |
|
||||
|
|
||||
- `SSO_AUTHORITY=https://gitlab.com` |
|
||||
- `SSO_CLIENT_ID` |
|
||||
- `SSO_CLIENT_SECRET` |
|
||||
|
|
||||
## Google Auth |
|
||||
|
|
||||
Google [Documentation](https://developers.google.com/identity/openid-connect/openid-connect). |
|
||||
\ |
|
||||
By default without extra [configuration](https://developers.google.com/identity/protocols/oauth2/web-server#creatingclient) you won´t have a `refresh_token` and session will be limited to 1h. |
|
||||
|
|
||||
Configure your server with : |
|
||||
|
|
||||
- `SSO_AUTHORITY=https://accounts.google.com` |
|
||||
- `SSO_AUTHORIZE_EXTRA_PARAMS="access_type=offline&prompt=consent"` |
|
||||
- `SSO_CLIENT_ID` |
|
||||
- `SSO_CLIENT_SECRET` |
|
||||
|
|
||||
## Kanidm |
|
||||
|
|
||||
Nothing specific should work with just `SSO_AUTHORITY`, `SSO_CLIENT_ID` and `SSO_CLIENT_SECRET`. |
|
||||
|
|
||||
## Microsoft Entra ID |
|
||||
|
|
||||
1. Create an "App registration" in [Entra ID](https://entra.microsoft.com/) following [Identity | Applications | App registrations](https://entra.microsoft.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade/quickStartType//sourceType/Microsoft_AAD_IAM). |
|
||||
2. From the "Overview" of your "App registration", you'll need the "Directory (tenant) ID" for the `SSO_AUTHORITY` variable and the "Application (client) ID" as the `SSO_CLIENT_ID` value. |
|
||||
3. In "Certificates & Secrets" create an "App secret" , you'll need the "Secret Value" for the `SSO_CLIENT_SECRET` variable. |
|
||||
4. In "Authentication" add <https://warden.example.org/identity/connect/oidc-signin> as "Web Redirect URI". |
|
||||
5. In "API Permissions" make sure you have `profile`, `email` and `offline_access` listed under "API / Permission name" (`offline_access` is required, otherwise no refresh_token is returned, see <https://github.com/MicrosoftDocs/azure-docs/issues/17134>). |
|
||||
|
|
||||
Only the v2 endpoint is compliant with the OpenID spec, see <https://github.com/MicrosoftDocs/azure-docs/issues/38427> and <https://github.com/ramosbugs/openidconnect-rs/issues/122>. |
|
||||
|
|
||||
Your configuration should look like this: |
|
||||
|
|
||||
* `SSO_AUTHORITY=https://login.microsoftonline.com/${Directory (tenant) ID}/v2.0` |
|
||||
* `SSO_SCOPES="email profile offline_access"` |
|
||||
* `SSO_CLIENT_ID=${Application (client) ID}` |
|
||||
* `SSO_CLIENT_SECRET=${Secret Value}` |
|
||||
|
|
||||
## Rauthy |
|
||||
|
|
||||
To use a provider controlled session you will need to run Rauthy with `DISABLE_REFRESH_TOKEN_NBF=true` otherwise the server will fail when trying to read a not yet valid `refresh_token` (`Bitwarden` clients will trigger a refresh even if the `access_token` is still valid. Details on rauthy [side](https://github.com/sebadob/rauthy/issues/651)). Alternative is to use the default session handling with `SSO_AUTH_ONLY_NOT_SESSION=true`. |
|
||||
|
|
||||
No specific config needed when creating the Client. |
|
||||
|
|
||||
Your configuration should look like this: |
|
||||
|
|
||||
* `SSO_AUTHORITY=http://${provider_host}/auth/v1` |
|
||||
* `SSO_CLIENT_ID=${Client ID}` |
|
||||
* `SSO_CLIENT_SECRET=${Client Secret}` |
|
||||
* `SSO_AUTH_ONLY_NOT_SESSION=true` Only needed if not running `Rauthy` with `DISABLE_REFRESH_TOKEN_NBF=true` |
|
||||
|
|
||||
## Slack |
|
||||
|
|
||||
You will need to create an app in https://api.slack.com/apps/. |
|
||||
|
|
||||
It appears that the `access_token` returned is not in JWT format and an expiration date is not sent with it. As such you will need to use the default session lifecycle. |
|
||||
|
|
||||
Your configuration should look like this: |
|
||||
|
|
||||
* `SSO_AUTHORITY=https://slack.com` |
|
||||
* `SSO_CLIENT_ID=${Application Client ID}` |
|
||||
* `SSO_CLIENT_SECRET=${Application Client Secret}` |
|
||||
* `SSO_AUTH_ONLY_NOT_SESSION=true` |
|
||||
|
|
||||
## Zitadel |
|
||||
|
|
||||
To obtain a `refresh_token` to be able to extend session you'll need to add the `offline_access` scope. |
|
||||
|
|
||||
Additionally Zitadel include the `Project id` and the `Client Id` in the audience of the Id Token. |
|
||||
For the validation to work you will need to add the `Resource Id` as a trusted audience (`Client Id` is trusted by default). |
|
||||
You can control the trusted audience with the config `SSO_AUDIENCE_TRUSTED` |
|
||||
|
|
||||
Since [zitadel#721](https://github.com/zitadel/oidc/pull/721) PKCE should work with client secret. |
|
||||
But older versions might have to disable it (`SSO_PKCE=false`). |
|
||||
|
|
||||
Config will look like: |
|
||||
|
|
||||
- `SSO_AUTHORITY=https://${provider_host}` |
|
||||
- `SSO_SCOPES="email profile offline_access"` |
|
||||
- `SSO_CLIENT_ID` |
|
||||
- `SSO_CLIENT_SECRET` |
|
||||
- `SSO_AUDIENCE_TRUSTED='^${Project Id}$'` |
|
||||
|
|
||||
## Session lifetime |
|
||||
|
|
||||
Session lifetime is dependant on refresh token and access token returned after calling your SSO token endpoint (grant type `authorization_code`). |
|
||||
If no refresh token is returned then the session will be limited to the access token lifetime. |
|
||||
|
|
||||
Tokens are not persisted in the server but wrapped in JWT tokens and returned to the application (The `refresh_token` and `access_token` values returned by VW `identity/connect/token` endpoint). |
|
||||
Note that the server will always return a `refresh_token` for compatibility reasons with the web front and it presence does not indicate that a refresh token was returned by your SSO (But you can decode its value with <https://jwt.io> and then check if the `token` field contain anything). |
|
||||
|
|
||||
With a refresh token present, activity in the application will trigger a refresh of the access token when it's close to expiration ([5min](https://github.com/bitwarden/clients/blob/0bcb45ed5caa990abaff735553a5046e85250f24/libs/common/src/auth/services/token.service.ts#L126) in web client). |
|
||||
|
|
||||
Additionally for certain action a token check is performed, if we have a refresh token we will perform a refresh otherwise we'll call the user information endpoint to check the access token validity. |
|
||||
|
|
||||
### Disabling SSO session handling |
|
||||
|
|
||||
If you are unable to obtain a `refresh_token` or for any other reason you can disable SSO session handling and revert to the default handling. |
|
||||
You'll need to enable `SSO_AUTH_ONLY_NOT_SESSION=true` then access token will be valid for 2h and refresh token will allow for an idle time of 7 days (which can be indefinitely extended). |
|
||||
|
|
||||
### Debug information |
|
||||
|
|
||||
Running with `LOG_LEVEL=debug` you'll be able to see information on token expiration. |
|
||||
|
|
||||
## Desktop Client |
|
||||
|
|
||||
There is some issue to handle redirection from your browser (used for sso login) to the application. |
|
||||
|
|
||||
### Chrome |
|
||||
|
|
||||
Some user report having ([issues](https://github.com/bitwarden/clients/issues/12929)). |
|
||||
|
|
||||
## Firefox |
|
||||
|
|
||||
On Windows you'll be presented with a prompt the first time you log to confirm which application should be launched (But there is a bug at the moment you might end-up with an empty vault after login atm). |
|
||||
|
|
||||
|
|
||||
On Linux it's a bit more tricky. |
|
||||
First you'll need to add some config in `about:config` : |
|
||||
|
|
||||
```conf |
|
||||
network.protocol-handler.expose.bitwarden=false |
|
||||
network.protocol-handler.external.bitwarden=true |
|
||||
``` |
|
||||
|
|
||||
If you have any doubt you can check `mailto` to see how it's configured. |
|
||||
|
|
||||
The redirection will still not work since it appears that the association to an application can only be done on a link/click. You can trigger it with a dummy page such as: |
|
||||
|
|
||||
```html |
|
||||
data:text/html,<a href="bitwarden:///dummy">Click me to register Bitwarden</a> |
|
||||
``` |
|
||||
|
|
||||
From now on the redirection should now work. |
|
||||
If you need to change the application launched you can now find it in `Settings` by using the search function and entering `application`. |
|
Loading…
Reference in new issue