Configure an upstream SSO provider
The authentication service supports using an upstream OpenID Connect provider to authenticate its users. Multiple providers can be configured, and can be used in conjunction with the local password database authentication.
Any OIDC compliant provider should work with the service as long as it supports the authorization code flow.
Note that the service does not support other SSO protocols such as SAML, and there is no plan to support them in the future. A deployment which requires SAML or LDAP-based authentication should use a service like Dex to bridge between the SAML provider and the authentication service.
General configuration
Configuration of upstream providers is done in the upstream_oauth2
section of the configuration file, which has a providers
list.
Additions and changes to this sections are synced with the database on startup.
Removals need to be applied using the mas-cli config sync --prune
command.
An exhaustive list of all the parameters is available in the configuration file reference.
The general configuration usually goes as follows:
- determine a unique
id
for the provider, which will be used as stable identifier between the configuration file and the database. Thisid
must be a ULID, and can be generated using online tools like https://www.ulidtools.com - create an OAuth 2.0/OIDC client on the provider's side, using the following parameters:
redirect_uri
:https://<auth-service-domain>/upstream/callback/<id>
response_type
:code
response_mode
:query
grant_type
:authorization_code
- fill the
upstream_oauth2
section of the configuration file with the following parameters:providers
:id
: the previously generated ULIDclient_id
: the client ID of the OAuth 2.0/OIDC client given by the providerclient_secret
: the client secret of the OAuth 2.0/OIDC client given by the providerissuer
: the issuer URL of the providerscope
: the scope to request from the provider.openid
is usually required, andprofile
andemail
are recommended to import a few user attributes.
- setup user attributes mapping to automatically fill the user profile with data from the provider. See the user attributes mapping section for more details.
User attributes mapping
The authentication service supports importing the following user attributes from the provider:
- The localpart/username (e.g.
@localpart:example.com
) - The display name
- An email address
For each of those attributes, administrators can configure a mapping using the claims provided by the upstream provider. They can also configure what should be done for each of those attributes. It can either:
ignore
: ignore the attribute, and let the user fill it manuallysuggest
: suggest the attribute to the user, but let them opt-out of importing itforce
: automatically import the attribute, but don't fail if it is not provided by the providerrequire
: automatically import the attribute, and fail if it is not provided by the provider
A Jinja2 template is used as mapping for each attribute. The template currently has one user
variable, which is an object with the claims got through the id_token
given by the provider.
The following default templates are used:
localpart
:{{ user.preferred_username }}
displayname
:{{ user.name }}
email
:{{ user.email }}
Multiple providers behaviour
Multiple authentication methods can be configured at the same time, in which case the authentication service will let the user choose which one to use.
This is true if both the local password database and an upstream provider are configured, or if multiple upstream providers are configured.
In such cases, the human_name
parameter of the provider configuration is used to display a human-readable name for the provider, and the brand_name
parameter is used to show a logo for well-known providers.
If there is only one upstream provider configured and the local password database is disabled (passwords.enabled
is set to false
), the authentication service will automatically trigger an authorization flow with this provider.
Sample configurations
This section contains sample configurations for popular OIDC providers.
Apple
Sign-in with Apple uses special non-standard for authenticating clients, which requires a special configuration.
upstream_oauth2:
providers:
- client_id: 01JAYS74TCG3BTWKADN5Q4518C
client_name: "<Service ID>" # TO BE FILLED
scope: "openid name email"
response_mode: "form_post"
token_endpoint_auth_method: "sign_in_with_apple"
sign_in_with_apple:
private_key: |
# Content of the PEM-encoded private key file, TO BE FILLED
team_id: "<Team ID>" # TO BE FILLED
key_id: "<Key ID>" # TO BE FILLED
claims_imports:
localpart:
action: ignore
displayname:
action: suggest
# SiWA passes down the user infos as query parameters in the callback
# which is available in the extra_callback_parameters variable
template: |
{%- set user = extra_callback_parameters["user"] | from_json -%}
{{- user.name.firstName }} {{ user.name.lastName -}}
email:
action: suggest
Authelia
These instructions assume that you have already enabled the OIDC provider support in Authelia.
Add a client for MAS to Authelia's configuration.yaml
(see the Authelia OIDC documentation for full details):
identity_providers:
oidc:
clients:
- client_id: "<client-id>" # TO BE FILLED
client_name: Matrix
client_secret: "<client-secret>" # TO BE FILLED
public: false
redirect_uris:
- https://<mas-fqdn>/upstream/callback/<id>
scopes:
- openid
- groups
- profile
- email
grant_types:
- 'refresh_token'
- 'authorization_code'
response_types:
- code
Authentication service configuration:
upstream_oauth2:
providers:
- id: <id>
human_name: Authelia
issuer: "https://<authelia-fqdn>" # TO BE FILLED W/O ANY TRAILING SLASHES
client_id: "<client-id>" # TO BE FILLED
client_secret: "<client-secret>" # TO BE FILLED
token_endpoint_auth_method: client_secret_basic
scope: "openid profile email"
discovery_mode: insecure
claims_imports:
localpart:
action: require
template: "{{ user.preferred_username }}"
displayname:
action: suggest
template: "{{ user.name }}"
email:
action: suggest
template: "{{ user.email }}"
set_email_verification: always
Authentik
Authentik is an open-source IdP solution.
- Create a provider in Authentik, with type OAuth2/OpenID.
- The parameters are:
- Client Type: Confidential
- Redirect URIs:
https://<auth-service-domain>/upstream/callback/<id>
- Create an application for the authentication service in Authentik and link it to the provider.
- Note the slug of your application, Client ID and Client Secret.
Authentication service configuration:
upstream_oauth2:
providers:
- id: 01HFRQFT5QFMJFGF01P7JAV2ME
human_name: Authentik
issuer: "https://<authentik-domain>/application/o/<app-slug>/" # TO BE FILLED
client_id: "<client-id>" # TO BE FILLED
client_secret: "<client-secret>" # TO BE FILLED
scope: "openid profile email"
claims_imports:
localpart:
action: require
template: "{{ user.preferred_username }}"
displayname:
action: suggest
template: "{{ user.name }}"
email:
action: suggest
template: "{{ user.email }}"
set_email_verification: always
- You will need a Facebook developer account. You can register for one here.
- On the apps page of the developer console, "Create App", and choose "Allow people to log in with their Facebook account".
- Once the app is created, add "Facebook Login" and choose "Web". You don't need to go through the whole form here.
- In the left-hand menu, open "Use cases" > "Authentication and account creation" > "Customize" > "Settings"
- Add
https://<auth-service-domain>/upstream/callback/<id>
as an OAuth Redirect URL.
- Add
- In the left-hand menu, open "App settings/Basic". Here you can copy the "App ID" and "App Secret" for use below.
Authentication service configuration:
upstream_oauth2:
providers:
- id: "01HFS3WM7KSWCEQVJTN0V9X1W6"
issuer: "https://www.facebook.com"
human_name: "Facebook"
brand_name: "facebook"
discovery_mode: disabled
pkce_method: always
authorization_endpoint: "https://facebook.com/v11.0/dialog/oauth/"
token_endpoint: "https://graph.facebook.com/v11.0/oauth/access_token"
jwks_uri: "https://www.facebook.com/.well-known/oauth/openid/jwks/"
token_endpoint_auth_method: "client_secret_post"
client_id: "<app-id>" # TO BE FILLED
client_secret: "<app-secret>" # TO BE FILLED
scope: "openid"
claims_imports:
localpart:
action: ignore
displayname:
action: suggest
template: "{{ user.name }}"
email:
action: suggest
template: "{{ user.email }}"
set_email_verification: always
GitLab
- Create a new application.
- Add the
openid
scope. Optionally add theprofile
andemail
scope if you want to import the user's name and email. - Add this Callback URL:
https://<auth-service-domain>/upstream/callback/<id>
Authentication service configuration:
upstream_oauth2:
providers:
- id: "01HFS67GJ145HCM9ZASYS9DC3J"
issuer: "https://gitlab.com"
human_name: "GitLab"
brand_name: "gitlab"
token_endpoint_auth_method: "client_secret_post"
client_id: "<client-id>" # TO BE FILLED
client_secret: "<client-secret>" # TO BE FILLED
scope: "openid profile email"
claims_imports:
displayname:
action: suggest
template: "{{ user.name }}"
localpart:
action: ignore
email:
action: suggest
template: "{{ user.email }}"
- Set up a project in the Google API Console (see documentation)
- Add an "OAuth Client ID" for a Web Application under "Credentials"
- Add the following "Authorized redirect URI":
https://<auth-service-domain>/upstream/callback/<id>
Authentication service configuration:
upstream_oauth2:
providers:
- id: 01HFS6S2SVAR7Y7QYMZJ53ZAGZ
human_name: Google
brand_name: "google"
issuer: "https://accounts.google.com"
client_id: "<client-id>" # TO BE FILLED
client_secret: "<client-secret>" # TO BE FILLED
scope: "openid profile email"
claims_imports:
localpart:
action: ignore
displayname:
action: suggest
template: "{{ user.name }}"
email:
action: suggest
template: "{{ user.email }}"
Keycloak
Follow the Getting Started Guide to install Keycloak and set up a realm.
-
Click
Clients
in the sidebar and clickCreate
-
Fill in the fields as below:
Field Value Client ID matrix-authentication-service
Client Protocol openid-connect
-
Click
Save
-
Fill in the fields as below:
Field Value Client ID matrix-authentication-service
Enabled On
Client Protocol openid-connect
Access Type confidential
Valid Redirect URIs https://<auth-service-domain>/upstream/callback/<id>
-
Click
Save
-
On the Credentials tab, update the fields:
Field Value Client Authenticator Client ID and Secret
-
Click
Regenerate Secret
-
Copy Secret
upstream_oauth2:
providers:
- id: "01H8PKNWKKRPCBW4YGH1RWV279"
issuer: "https://<keycloak>/realms/<realm>" # TO BE FILLED
token_endpoint_auth_method: client_secret_basic
client_id: "matrix-authentication-service"
client_secret: "<client-secret>" # TO BE FILLED
scope: "openid profile email"
claims_imports:
localpart:
action: require
template: "{{ user.preferred_username }}"
displayname:
action: suggest
template: "{{ user.name }}"
email:
action: suggest
template: "{{ user.email }}"
set_email_verification: always
Microsoft Azure Active Directory
Azure AD can act as an OpenID Connect Provider.
Register a new application under App registrations in the Azure AD management console.
The RedirectURI
for your application should point to your authentication service instance:
https://<auth-service-domain>/upstream/callback/<id>
where <id>
is the same as in the config file.
Go to Certificates & secrets and register a new client secret. Make note of your Directory (tenant) ID as it will be used in the Azure links.
Authentication service configuration:
upstream_oauth2:
providers:
- id: "01HFRPWGR6BG9SAGAKDTQHG2R2"
human_name: Microsoft Azure AD
issuer: "https://login.microsoftonline.com/<tenant-id>/v2.0" # TO BE FILLED
client_id: "<client-id>" # TO BE FILLED
client_secret: "<client-secret>" # TO BE FILLED
scope: "openid profile email"
claims_imports:
localpart:
action: require
template: "{{ (user.preferred_username | split('@'))[0] }}"
displayname:
action: suggest
template: "{{ user.name }}"
email:
action: suggest
template: "{{ user.email }}"
set_email_verification: always
Rauthy
-
Click
Clients
in the Rauthy Admin sidebar and clickAdd new Client
-
Fill in the fields as below:
Field Value Client ID matrix-authentication-service
Client Name matrix-authentication-service
Redirect URI https://<auth-service-domain>/upstream/callback/<id>
-
Set the client to be
Confidential
. -
Click
Save
-
Select the client you just created from the clients list.
-
Enable the
authorization_code
, andrefresh_token
grant types. -
Set the allowed scopes to
openid
,profile
, andemail
. -
Set both Access Algorithm and ID Algorithm to
RS256
. -
Set PKCE challenge method to
S256
. -
Click
Save
-
Copy the
Client ID
from theConfig
tab and theClient Secret
from theSecret
tab.
Authentication service configuration:
upstream_oauth2:
providers:
- id: "01JFFHK7HJF70YSYF753GEWVRP"
human_name: Rauthy
issuer: "https://<rauthy>/auth/v1" # TO BE FILLED
client_id: "<client-id>" # TO BE FILLED
client_secret: "<client-secret>" # TO BE FILLED
scope: "openid profile email"
claims_imports:
localpart:
action: ignore
displayname:
action: suggest
template: "{{ user.given_name }}"
email:
action: suggest
template: "{{ user.email }}"
To use a Rauthy-supported Ephemeral Client, use this JSON document:
{
"client_id": "https://path.to.this.json",
"redirect_uris": [
"https://your-app.com/callback"
],
"grant_types": [
"authorization_code",
"refresh_token"
],
"access_token_signed_response_alg": "RS256",
"id_token_signed_response_alg": "RS256"
}