Custom SAML mapping provider for Synapse installations with some extensions like database logging.
Go to file
Maximilian Kratz bbe58b7547
Merge pull request #24 from maxkratz/feature/update-ci-python-version
CI: Update Python version (linting) to v3.12
2024-03-19 13:49:00 +01:00
.github/workflows CI: Update Python version (linting) to v3.12 2024-03-19 13:25:40 +01:00
matrix_synapse_saml_mapper Updates metadata: copyright year + email address 2024-03-19 13:18:58 +01:00
.gitignore Bump version to 0.2.0 + removed unused Gitlab-CI config 2021-09-21 16:10:38 +02:00
.pylintrc Ignored some pylint rules, because most interfaces are given by synapses documentation. 2020-11-19 14:14:03 +01:00
LICENSE Added Apache License v.2.0. 2020-11-18 17:56:05 +01:00 Fixes name of the complete CI pipeline 2023-01-29 13:33:45 +01:00
module_config_example.yml Updates metadata: copyright year + email address 2024-03-19 13:18:58 +01:00
requirements.txt Switches from pylint-fail-under to pylint --fail-under 2022-01-29 13:20:44 +01:00
saml-ou-trace.xml Adapted doc to new attribute names + mapper adaptions 2021-09-21 11:14:25 +02:00 Updates metadata: copyright year + email address 2024-03-19 13:18:58 +01:00

Matrix Synapse SAML Mapper

CI Pipeline

A Synapse plugin module which allows administrators to ...

  • ... concatenate and/or modify provided SAML attributes,
  • ... log registrations to a custom logfile and
  • ... log user SAML attributes to a custom PostgreSQL database at their initial login.

The main reason for creating this project was the fact that the identity provider (idp) at the Technical University (TU) Darmstadt does not provide an easy to read "displayName" as a SAML attribute. Therefore, first- and surname(s) had to be concatenated manually to populate the "displayName" fields. Some code snippets found herein refer to an identification named TU-ID which is the unique ID for all students and employees at our university. This attribute will most likely be called uid within your SAML provider.

Please note that the custom PostgreSQL database is not the same database as the one used by your Synapse installation!

Another important note: This code may break unexpectedly. Feel free to use this project as a kind of blueprint to implement your own SAML mapper. You are of course allowed to also use it in production environments, but you've been warned. :)


  • Clone this repository to your python workspace
    • e.g. $ git clone
  • Copy module_config_example.yml to /etc/matrix-synapse/saml_mapper_config.yml.
  • Adapt /etc/matrix-synapse/saml_mapper_config.yml according to your needs.
  • Install the package in your virtual environment which is used by Synapse.
    • e.g. for a Matrix/Synapse installation based on the Debian/Ubuntu package, run $ /opt/venvs/matrix-synapse/bin/python install on your console.

Logging setup

Keep in mind that this custom logging of users is just an additional feature of this module. You can always remove/comment out these parts if you will not make any use of it.

Logging setup: Database

In order to use this module to log new user registrations (from SAML) to a custom PostgreSQL database, you have to set this database up first. For this example I will use the name ou (which stands for organizational unit), but feel free to change these values to your liking.

  • Login (e.g. with $ su - root).
  • Create user: $ createuser --pwprompt ou_user
  • Login to PostgreSQL:
    • $ su - postgres
    • $ psql
  • Create database: =# CREATE DATABASE ou ENCODING 'UTF8' template=template0 OWNER ou_user;
  • Connect to database: =# \connect ou
  • Create table with following design:
    • id is a unique primary key for all entries.
    • tuid is a char with 8 symbols for the unique id at Technical University (TU) Darmstadt. Feel free to change this here and within the code according to your needs.
      • A constraint at the end to ensure that this field is always unique.
    • ou is an array of text for all departments and organizations etc.
    • givenname is a text for all first names (Technical University (TU) Darmstadt idp concatenates first names together).
    • surname is a text for all surnames (Technical University (TU) Darmstadt idp concatenates surnames together).
    • email is an array of text for all email addresses of a person.
    • edu_person_affiliation is an array of text for all groups, e.g. student and member.
    • created_at is the timestamp of the insertion into this table.
=# CREATE TABLE user_external_saml (
  tuid char(8) NOT NULL,
  givenname TEXT NOT NULL,
  surname TEXT NOT NULL,
  email TEXT[] NOT NULL,
  edu_person_affiliation TEXT[] NOT NULL,
  created_at TIMESTAMP NOT NULL,
  CONSTRAINT tuid_unique UNIQUE (tuid)
  • Change table owner to ou_user: =# ALTER TABLE user_external_saml OWNER TO ou_user;
  • Add user ou_user to your PostgreSQL database. In my case, this was done by extending /etc/postgresql/12/main/pg_hba.con:
hostssl ou              ou_user         <synapse-server-ip>/32       md5
  • Restart PostgreSQL: $ service postgresql restart

Logging setup: Actual logging

The logging capability of this module is more of a 'conceptual proof'. If properly set up, the module logs all unique ids together with the timestamp of their first login (registration) to a log file onto disk.

  • $ mkdir -p /var/log/custom-scripts
  • $ chown -R matrix-synapse /var/log/custom-scripts/
  • $ chgrp -R nogroup /var/log/custom-scripts/

Logs can be found in /var/log/custom-scripts/dummy_logger.log (default path).


Configuration of this module is completely done inside file module_config.yml.


  • db: database: "ou" Module uses a custom PostgreSQL database with name ou.
  • db: user: "ou_user" Module uses a custom PostgreSQL database user named ou_user.
  • db: password: "secret" Module uses a custom PostgreSQL database password secret.
  • db: host: "db-host" Module uses a custom PostgreSQL database at host db-host. (You may also specify an ip address here.)
  • db: port: "5432" Module uses the given port to connect to custom PostgreSQL database.


  • log: path: "/var/log/custom-scripts/dummy_logger.log" Module uses the specified path to log new registrations (user creations at first login) of SAML to specified file.


In order to use the custom module, you have configure Synapse to do so. For this example let's assume the following attributes provided by the identity provider (idp):

  • cn or urn:oid: This is the unique ID, in most systems named uid.
  • mail or urn:oid:0.9.2342.19200300.100.1.3: Mail address of the user.
  • surname or urn:oid: Surname(s) of the user.
  • givenName or urn:oid: Given name(s) of the user.
  • ou or urn:oid: Organizational unit of the user.
  • eduPersonAffiliation or urn:oid: Affiliation of the user like student.
  • eduPersonScopedAffiliation or urn:oid: Scoped affiliation of the user like

Change the SAML2 attribute map in /etc/matrix-synapse/saml2-attribute-maps/

MAP = {
    "identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
    "fro": {
        'urn:oid:': 'uid',
        'urn:oid:0.9.2342.19200300.100.1.3': 'email',
        'urn:oid:': 'surname',
        'urn:oid:': 'givenName',
        'urn:oid:': 'ou',
        'urn:oid:': 'eduPersonAffiliation',
        'urn:oid:': 'eduPersonScopedAffiliation'
    "to": {
        'uid': 'urn:oid:',
        'email': 'urn:oid:0.9.2342.19200300.100.1.3',
        'surname': 'urn:oid:',
        'givenName': 'urn:oid:',
        'ou': 'urn:oid:',
        'eduPersonAffiliation': 'urn:oid:',
        'eduPersonScopedAffiliation' : 'urn:oid:',

Please keep in mind that this module expects this seven values (after mapping):

  • uid
  • surname
  • givenName
  • email
  • ou
  • eduPersonAffiliation
  • eduPersonScopedAffiliation (optional)

Edit the following values in your homeserver.yml file:

    # [...]
    attribute_map_dir: /etc/matrix-synapse/saml2-attribute-maps
    # [...]

    module: "matrix_synapse_saml_mapper.SamlMappingProvider"
      mxid_source_attribute: uid

Restart your Synapse server after all configuration changes.

Please keep in mind that all required or optional SAML attributes are handled by this mapper, i.e., you can not specify more or other attributes in your homeserver.yaml file. To prevent misunderstandings, this will not work while using this mapper:

         required_attributes: ["attribute-1"]
         optional_attributes: ["attribute-2"]


Code is linted with pylint using PEP 8 style. You may check the style using this command:

pylint-fail-under --fail_under 9.0 -d pep8 matrix_synapse_saml_mapper/*.py


This code is heavily based on:

Please check out the following plugin if you want to trace SAML logins (on the client side). At least for me it was quite helpful during debugging.