anonymousland-synapse/docs/openid.md
Sean Quah 158d73ebdd Revert accidental fast-forward merge from v1.49.0rc1
Revert "Sort internal changes in changelog"
Revert "Update CHANGES.md"
Revert "1.49.0rc1"
Revert "Revert "Move `glob_to_regex` and `re_word_boundary` to `matrix-python-common` (#11505) (#11527)"
Revert "Refactors in `_generate_sync_entry_for_rooms` (#11515)"
Revert "Correctly register shutdown handler for presence workers (#11518)"
Revert "Fix `ModuleApi.looping_background_call` for non-async functions (#11524)"
Revert "Fix 'delete room' admin api to work on incomplete rooms (#11523)"
Revert "Correctly ignore invites from ignored users (#11511)"
Revert "Fix the test breakage introduced by #11435 as a result of concurrent PRs (#11522)"
Revert "Stabilise support for MSC2918 refresh tokens as they have now been merged into the Matrix specification. (#11435)"
Revert "Save the OIDC session ID (sid) with the device on login (#11482)"
Revert "Add admin API to get some information about federation status (#11407)"
Revert "Include bundled aggregations in /sync and related fixes (#11478)"
Revert "Move `glob_to_regex` and `re_word_boundary` to `matrix-python-common` (#11505)"
Revert "Update backward extremity docs to make it clear that it does not indicate whether we have fetched an events' `prev_events` (#11469)"
Revert "Support configuring the lifetime of non-refreshable access tokens separately to refreshable access tokens. (#11445)"
Revert "Add type hints to `synapse/tests/rest/admin` (#11501)"
Revert "Revert accidental commits to develop."
Revert "Newsfile"
Revert "Give `tests.server.setup_test_homeserver` (nominally!) the same behaviour"
Revert "Move `tests.utils.setup_test_homeserver` to `tests.server`"
Revert "Convert one of the `setup_test_homeserver`s to `make_test_homeserver_synchronous`"
Revert "Disambiguate queries on `state_key` (#11497)"
Revert "Comments on the /sync tentacles (#11494)"
Revert "Clean up tests.storage.test_appservice (#11492)"
Revert "Clean up `tests.storage.test_main` to remove use of legacy code. (#11493)"
Revert "Clean up `tests.test_visibility` to remove legacy code. (#11495)"
Revert "Minor cleanup on recently ported doc pages  (#11466)"
Revert "Add most of the missing type hints to `synapse.federation`. (#11483)"
Revert "Avoid waiting for zombie processes in `synctl stop` (#11490)"
Revert "Fix media repository failing when media store path contains symlinks (#11446)"
Revert "Add type annotations to `tests.storage.test_appservice`. (#11488)"
Revert "`scripts-dev/sign_json`: support for signing events (#11486)"
Revert "Add MSC3030 experimental client and federation API endpoints to get the closest event to a given timestamp (#9445)"
Revert "Port wiki pages to documentation website (#11402)"
Revert "Add a license header and comment. (#11479)"
Revert "Clean-up get_version_string (#11468)"
Revert "Link background update controller docs to summary (#11475)"
Revert "Additional type hints for config module. (#11465)"
Revert "Register the login redirect endpoint for v3. (#11451)"
Revert "Update openid.md"
Revert "Remove mention of OIDC certification from Dex (#11470)"
Revert "Add a note about huge pages to our Postgres doc (#11467)"
Revert "Don't start Synapse master process if `worker_app` is set (#11416)"
Revert "Expose worker & homeserver as entrypoints in `setup.py` (#11449)"
Revert "Bundle relations of relations into the `/relations` result. (#11284)"
Revert "Fix `LruCache` corruption bug with a `size_callback` that can return 0 (#11454)"
Revert "Eliminate a few `Any`s in `LruCache` type hints (#11453)"
Revert "Remove unnecessary `json.dumps` from `tests.rest.admin` (#11461)"
Revert "Merge branch 'master' into develop"

This reverts commit 26b5d2320f.
This reverts commit bce4220f38.
This reverts commit 966b5d0fa0.
This reverts commit 088d748f2c.
This reverts commit 14d593f72d.
This reverts commit 2a3ec6facf.
This reverts commit eccc49d755.
This reverts commit b1ecd19c5d.
This reverts commit 9c55dedc8c.
This reverts commit 2d42e586a8.
This reverts commit 2f053f3f82.
This reverts commit a15a893df8.
This reverts commit 8b4b153c9e.
This reverts commit 494ebd7347.
This reverts commit a77c369897.
This reverts commit 4eb77965cd.
This reverts commit 637df95de6.
This reverts commit e5f426cd54.
This reverts commit 8cd68b8102.
This reverts commit 6cae125e20.
This reverts commit 7be88fbf48.
This reverts commit b3fd99b74a.
This reverts commit f7ec6e7d9e.
This reverts commit 5640992d17.
This reverts commit d26808dd85.
This reverts commit f91624a595.
This reverts commit 16d39a5490.
This reverts commit 8a4c296987.
This reverts commit 49e1356ee3.
This reverts commit d2279f471b.
This reverts commit b50e39df57.
This reverts commit 858d80bf0f.
This reverts commit 435f044807.
This reverts commit f61462e1be.
This reverts commit a6f1a3abec.
This reverts commit 84dc50e160.
This reverts commit ed635d3285.
This reverts commit 7b62791e00.
This reverts commit 153194c771.
This reverts commit f44d729d4c.
This reverts commit a265fbd397.
This reverts commit b9fef1a7cd.
This reverts commit b0eb64ff7b.
This reverts commit f1795463bf.
This reverts commit 70cbb1a5e3.
This reverts commit 42bf020463.
This reverts commit 379f2650cf.
This reverts commit 7ff22d6da4.
This reverts commit 5a0b652d36.
This reverts commit 432a174bc1.
This reverts commit b14f8a1baf, reversing
changes made to e713855dca.
2021-12-07 16:47:31 +00:00

20 KiB

Configuring Synapse to authenticate against an OpenID Connect provider

Synapse can be configured to use an OpenID Connect Provider (OP) for authentication, instead of its own local password database.

Any OP should work with Synapse, as long as it supports the authorization code flow. There are a few options for that:

  • start a local OP. Synapse has been tested with Hydra and Dex. Note that for an OP to work, it should be served under a secure (HTTPS) origin. A certificate signed with a self-signed, locally trusted CA should work. In that case, start Synapse with a SSL_CERT_FILE environment variable set to the path of the CA.

  • set up a SaaS OP, like Google, Auth0 or Okta. Synapse has been tested with Auth0 and Google.

It may also be possible to use other OAuth2 providers which provide the authorization code grant type, such as Github.

Preparing Synapse

The OpenID integration in Synapse uses the authlib library, which must be installed as follows:

  • The relevant libraries are included in the Docker images and Debian packages provided by matrix.org so no further action is needed.

  • If you installed Synapse into a virtualenv, run /path/to/env/bin/pip install matrix-synapse[oidc] to install the necessary dependencies.

  • For other installation mechanisms, see the documentation provided by the maintainer.

To enable the OpenID integration, you should then add a section to the oidc_providers setting in your configuration file (or uncomment one of the existing examples). See sample_config.yaml for some sample settings, as well as the text below for example configurations for specific providers.

Sample configs

Here are a few configs for providers that should work with Synapse.

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 matrix server: [synapse public baseurl]/_synapse/client/oidc/callback

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. Edit your Synapse config file and change the oidc_config section:

oidc_providers:
  - idp_id: microsoft
    idp_name: Microsoft
    issuer: "https://login.microsoftonline.com/<tenant id>/v2.0"
    client_id: "<client id>"
    client_secret: "<client secret>"
    scopes: ["openid", "profile"]
    authorization_endpoint: "https://login.microsoftonline.com/<tenant id>/oauth2/v2.0/authorize"
    token_endpoint: "https://login.microsoftonline.com/<tenant id>/oauth2/v2.0/token"
    userinfo_endpoint: "https://graph.microsoft.com/oidc/userinfo"

    user_mapping_provider:
      config:
        localpart_template: "{{ user.preferred_username.split('@')[0] }}"
        display_name_template: "{{ user.name }}"

Dex

Dex is a simple, open-source, certified OpenID Connect Provider. Although it is designed to help building a full-blown provider with an external database, it can be configured with static passwords in a config file.

Follow the Getting Started guide to install Dex.

Edit examples/config-dev.yaml config file from the Dex repo to add a client:

staticClients:
- id: synapse
  secret: secret
  redirectURIs:
  - '[synapse public baseurl]/_synapse/client/oidc/callback'
  name: 'Synapse'

Run with dex serve examples/config-dev.yaml.

Synapse config:

oidc_providers:
  - idp_id: dex
    idp_name: "My Dex server"
    skip_verification: true # This is needed as Dex is served on an insecure endpoint
    issuer: "http://127.0.0.1:5556/dex"
    client_id: "synapse"
    client_secret: "secret"
    scopes: ["openid", "profile"]
    user_mapping_provider:
      config:
        localpart_template: "{{ user.name }}"
        display_name_template: "{{ user.name|capitalize }}"

Keycloak

Keycloak is an opensource IdP maintained by Red Hat.

Follow the Getting Started Guide to install Keycloak and set up a realm.

  1. Click Clients in the sidebar and click Create

  2. Fill in the fields as below:

Field Value
Client ID synapse
Client Protocol openid-connect
  1. Click Save
  2. Fill in the fields as below:
Field Value
Client ID synapse
Enabled On
Client Protocol openid-connect
Access Type confidential
Valid Redirect URIs [synapse public baseurl]/_synapse/client/oidc/callback
  1. Click Save
  2. On the Credentials tab, update the fields:
Field Value
Client Authenticator Client ID and Secret
  1. Click Regenerate Secret
  2. Copy Secret
oidc_providers:
  - idp_id: keycloak
    idp_name: "My KeyCloak server"
    issuer: "https://127.0.0.1:8443/auth/realms/{realm_name}"
    client_id: "synapse"
    client_secret: "copy secret generated from above"
    scopes: ["openid", "profile"]
    user_mapping_provider:
      config:
        localpart_template: "{{ user.preferred_username }}"
        display_name_template: "{{ user.name }}"

Auth0

Auth0 is a hosted SaaS IdP solution.

  1. Create a regular web application for Synapse

  2. Set the Allowed Callback URLs to [synapse public baseurl]/_synapse/client/oidc/callback

  3. Add a rule to add the preferred_username claim.

    Code sample
    function addPersistenceAttribute(user, context, callback) {
      user.user_metadata = user.user_metadata || {};
      user.user_metadata.preferred_username = user.user_metadata.preferred_username || user.user_id;
      context.idToken.preferred_username = user.user_metadata.preferred_username;
    
      auth0.users.updateUserMetadata(user.user_id, user.user_metadata)
        .then(function(){
            callback(null, user, context);
        })
        .catch(function(err){
            callback(err);
        });
    }
    

Synapse config:

oidc_providers:
  - idp_id: auth0
    idp_name: Auth0
    issuer: "https://your-tier.eu.auth0.com/" # TO BE FILLED
    client_id: "your-client-id" # TO BE FILLED
    client_secret: "your-client-secret" # TO BE FILLED
    scopes: ["openid", "profile"]
    user_mapping_provider:
      config:
        localpart_template: "{{ user.preferred_username }}"
        display_name_template: "{{ user.name }}"

Authentik

Authentik is an open-source IdP solution.

  1. Create a provider in Authentik, with type OAuth2/OpenID.
  2. The parameters are:
  • Client Type: Confidential
  • JWT Algorithm: RS256
  • Scopes: OpenID, Email and Profile
  • RSA Key: Select any available key
  • Redirect URIs: [synapse public baseurl]/_synapse/client/oidc/callback
  1. Create an application for synapse in Authentik and link it to the provider.
  2. Note the slug of your application, Client ID and Client Secret.

Synapse config:

oidc_providers:
  - idp_id: authentik
    idp_name: authentik
    discover: true
    issuer: "https://your.authentik.example.org/application/o/your-app-slug/" # TO BE FILLED: domain and slug
    client_id: "your client id" # TO BE FILLED
    client_secret: "your client secret" # TO BE FILLED
    scopes:
      - "openid"
      - "profile"
      - "email"
    user_mapping_provider:
      config:
        localpart_template: "{{ user.preferred_username }}}"
        display_name_template: "{{ user.preferred_username|capitalize }}" # TO BE FILLED: If your users have names in Authentik and you want those in Synapse, this should be replaced with user.name|capitalize.

LemonLDAP

LemonLDAP::NG is an open-source IdP solution.

  1. Create an OpenID Connect Relying Parties in LemonLDAP::NG
  2. The parameters are:
  • Client ID under the basic menu of the new Relying Parties (Options > Basic > Client ID)
  • Client secret (Options > Basic > Client secret)
  • JWT Algorithm: RS256 within the security menu of the new Relying Parties (Options > Security > ID Token signature algorithm and Options > Security > Access Token signature algorithm)
  • Scopes: OpenID, Email and Profile
  • Allowed redirection addresses for login (Options > Basic > Allowed redirection addresses for login ) : [synapse public baseurl]/_synapse/client/oidc/callback

Synapse config:

oidc_providers:
  - idp_id: lemonldap
    idp_name: lemonldap
    discover: true
    issuer: "https://auth.example.org/" # TO BE FILLED: replace with your domain
    client_id: "your client id" # TO BE FILLED
    client_secret: "your client secret" # TO BE FILLED
    scopes:
      - "openid"
      - "profile"
      - "email"
    user_mapping_provider:
      config:
        localpart_template: "{{ user.preferred_username }}}"
        # TO BE FILLED: If your users have names in LemonLDAP::NG and you want those in Synapse, this should be replaced with user.name|capitalize or any valid filter.
        display_name_template: "{{ user.preferred_username|capitalize }}"

GitHub

GitHub is a bit special as it is not an OpenID Connect compliant provider, but just a regular OAuth2 provider.

The /user API endpoint can be used to retrieve information on the authenticated user. As the Synapse login mechanism needs an attribute to uniquely identify users, and that endpoint does not return a sub property, an alternative subject_claim has to be set.

  1. Create a new OAuth application: https://github.com/settings/applications/new.
  2. Set the callback URL to [synapse public baseurl]/_synapse/client/oidc/callback.

Synapse config:

oidc_providers:
  - idp_id: github
    idp_name: Github
    idp_brand: "github"  # optional: styling hint for clients
    discover: false
    issuer: "https://github.com/"
    client_id: "your-client-id" # TO BE FILLED
    client_secret: "your-client-secret" # TO BE FILLED
    authorization_endpoint: "https://github.com/login/oauth/authorize"
    token_endpoint: "https://github.com/login/oauth/access_token"
    userinfo_endpoint: "https://api.github.com/user"
    scopes: ["read:user"]
    user_mapping_provider:
      config:
        subject_claim: "id"
        localpart_template: "{{ user.login }}"
        display_name_template: "{{ user.name }}"

Google

Google is an OpenID certified authentication and authorisation provider.

  1. Set up a project in the Google API Console (see https://developers.google.com/identity/protocols/oauth2/openid-connect#appsetup).
  2. Add an "OAuth Client ID" for a Web Application under "Credentials".
  3. Copy the Client ID and Client Secret, and add the following to your synapse config:
    oidc_providers:
      - idp_id: google
        idp_name: Google
        idp_brand: "google"  # optional: styling hint for clients
        issuer: "https://accounts.google.com/"
        client_id: "your-client-id" # TO BE FILLED
        client_secret: "your-client-secret" # TO BE FILLED
        scopes: ["openid", "profile"]
        user_mapping_provider:
          config:
            localpart_template: "{{ user.given_name|lower }}"
            display_name_template: "{{ user.name }}"
    
  4. Back in the Google console, add this Authorized redirect URI: [synapse public baseurl]/_synapse/client/oidc/callback.

Twitch

  1. Setup a developer account on Twitch
  2. Obtain the OAuth 2.0 credentials by creating an app
  3. Add this OAuth Redirect URL: [synapse public baseurl]/_synapse/client/oidc/callback

Synapse config:

oidc_providers:
  - idp_id: twitch
    idp_name: Twitch
    issuer: "https://id.twitch.tv/oauth2/"
    client_id: "your-client-id" # TO BE FILLED
    client_secret: "your-client-secret" # TO BE FILLED
    client_auth_method: "client_secret_post"
    user_mapping_provider:
      config:
        localpart_template: "{{ user.preferred_username }}"
        display_name_template: "{{ user.name }}"

GitLab

  1. Create a new application.
  2. Add the read_user and openid scopes.
  3. Add this Callback URL: [synapse public baseurl]/_synapse/client/oidc/callback

Synapse config:

oidc_providers:
  - idp_id: gitlab
    idp_name: Gitlab
    idp_brand: "gitlab"  # optional: styling hint for clients
    issuer: "https://gitlab.com/"
    client_id: "your-client-id" # TO BE FILLED
    client_secret: "your-client-secret" # TO BE FILLED
    client_auth_method: "client_secret_post"
    scopes: ["openid", "read_user"]
    user_profile_method: "userinfo_endpoint"
    user_mapping_provider:
      config:
        localpart_template: '{{ user.nickname }}'
        display_name_template: '{{ user.name }}'

Facebook

Like Github, Facebook provide a custom OAuth2 API rather than an OIDC-compliant one so requires a little more configuration.

  1. You will need a Facebook developer account. You can register for one here.
  2. On the apps page of the developer console, "Create App", and choose "Build Connected Experiences".
  3. Once the app is created, add "Facebook Login" and choose "Web". You don't need to go through the whole form here.
  4. In the left-hand menu, open "Products"/"Facebook Login"/"Settings".
    • Add [synapse public baseurl]/_synapse/client/oidc/callback as an OAuth Redirect URL.
  5. In the left-hand menu, open "Settings/Basic". Here you can copy the "App ID" and "App Secret" for use below.

Synapse config:

  - idp_id: facebook
    idp_name: Facebook
    idp_brand: "facebook"  # optional: styling hint for clients
    discover: false
    issuer: "https://facebook.com"
    client_id: "your-client-id" # TO BE FILLED
    client_secret: "your-client-secret" # TO BE FILLED
    scopes: ["openid", "email"]
    authorization_endpoint: https://facebook.com/dialog/oauth
    token_endpoint: https://graph.facebook.com/v9.0/oauth/access_token
    user_profile_method: "userinfo_endpoint"
    userinfo_endpoint: "https://graph.facebook.com/v9.0/me?fields=id,name,email,picture"
    user_mapping_provider:
      config:
        subject_claim: "id"
        display_name_template: "{{ user.name }}"

Relevant documents:

Gitea

Gitea is, like Github, not an OpenID provider, but just an OAuth2 provider.

The /user API endpoint can be used to retrieve information on the authenticated user. As the Synapse login mechanism needs an attribute to uniquely identify users, and that endpoint does not return a sub property, an alternative subject_claim has to be set.

  1. Create a new application.
  2. Add this Callback URL: [synapse public baseurl]/_synapse/client/oidc/callback

Synapse config:

oidc_providers:
  - idp_id: gitea
    idp_name: Gitea
    discover: false
    issuer: "https://your-gitea.com/"
    client_id: "your-client-id" # TO BE FILLED
    client_secret: "your-client-secret" # TO BE FILLED
    client_auth_method: client_secret_post
    scopes: [] # Gitea doesn't support Scopes
    authorization_endpoint: "https://your-gitea.com/login/oauth/authorize"
    token_endpoint: "https://your-gitea.com/login/oauth/access_token"
    userinfo_endpoint: "https://your-gitea.com/api/v1/user"
    user_mapping_provider:
      config:
        subject_claim: "id"
        localpart_template: "{{ user.login }}"
        display_name_template: "{{ user.full_name }}"

XWiki

Install OpenID Connect Provider extension in your XWiki instance.

Synapse config:

oidc_providers:
  - idp_id: xwiki
    idp_name: "XWiki"
    issuer: "https://myxwikihost/xwiki/oidc/"
    client_id: "your-client-id" # TO BE FILLED
    client_auth_method: none
    scopes: ["openid", "profile"]
    user_profile_method: "userinfo_endpoint"
    user_mapping_provider:
      config:
        localpart_template: "{{ user.preferred_username }}"
        display_name_template: "{{ user.name }}"

Apple

Configuring "Sign in with Apple" (SiWA) requires an Apple Developer account.

You will need to create a new "Services ID" for SiWA, and create and download a private key with "SiWA" enabled.

As well as the private key file, you will need:

  • Client ID: the "identifier" you gave the "Services ID"
  • Team ID: a 10-character ID associated with your developer account.
  • Key ID: the 10-character identifier for the key.

https://help.apple.com/developer-account/?lang=en#/dev77c875b7e has more documentation on setting up SiWA.

The synapse config will look like this:

  - idp_id: apple
    idp_name: Apple
    issuer: "https://appleid.apple.com"
    client_id: "your-client-id" # Set to the "identifier" for your "ServicesID"
    client_auth_method: "client_secret_post"
    client_secret_jwt_key:
      key_file: "/path/to/AuthKey_KEYIDCODE.p8"  # point to your key file
      jwt_header:
        alg: ES256
        kid: "KEYIDCODE"   # Set to the 10-char Key ID
      jwt_payload:
        iss: TEAMIDCODE    # Set to the 10-char Team ID
    scopes: ["name", "email", "openid"]
    authorization_endpoint: https://appleid.apple.com/auth/authorize?response_mode=form_post
    user_mapping_provider:
      config:
        email_template: "{{ user.email }}"

Django OAuth Toolkit

django-oauth-toolkit is a Django application providing out of the box all the endpoints, data and logic needed to add OAuth2 capabilities to your Django projects. It supports OpenID Connect too.

Configuration on Django's side:

  1. Add an application: https://example.com/admin/oauth2_provider/application/add/ and choose parameters like this:
  1. You can customize the claims Django gives to synapse (optional):

    Code sample
    class CustomOAuth2Validator(OAuth2Validator):
    
        def get_additional_claims(self, request):
            return {
                "sub": request.user.email,
                "email": request.user.email,
                "first_name": request.user.first_name,
                "last_name": request.user.last_name,
            }
    

Your synapse config is then:

oidc_providers:
  - idp_id: django_example
    idp_name: "Django Example"
    issuer: "https://example.com/o/"
    client_id: "your-client-id"  # CHANGE ME
    client_secret: "your-client-secret"  # CHANGE ME
    scopes: ["openid"]
    user_profile_method: "userinfo_endpoint"  # needed because oauth-toolkit does not include user information in the authorization response
    user_mapping_provider:
      config:
        localpart_template: "{{ user.email.split('@')[0] }}"
        display_name_template: "{{ user.first_name }} {{ user.last_name }}"
        email_template: "{{ user.email }}"