Firstly, what is MAS and why might you want it? Well, to sum it up briefly, it provides harder, better, faster, authentication for your matrix communication stack.
More tangibly it provides an OAuth 2.0 and OpenID Connect based authentication layer, with improvements such as system browser based authentication and a myriad of resulting security benefits (see MSC3861 ).
If you’re wondering what the hell Matrix even is, check out my previous article for context: Connecting Differently: The Matrix Approach to Communication . I also wrote a guide on how to install Synapse.
Assumptions before we get going
You want to install MAS via Docker Compose. I’ll provide an example docker-compose.yaml.
You are using an OIDC compatible client like Element X or Element Web. If in doubt, check first.
You already have Synapse installed. MAS is a standalone component and works with other homeserver implementations however this guide focuses on its integration with Synapse.
You already have users. Users that you’d like to migrate over to the new MAS setup, who will login with their same username & passwords.
You don’t already use an upstream IDP. You can absolutely follow this guide to install MAS, but I won’t cover the additional configuration to setup MAS with an upstream IDP, which is covered well in the official documentation .
You’re aware some bridges and tools haven’t added support yet. It is possible to use MAS even with E2BE (end to bridge encryption), though not all bridges support this yet. I provide example config using the popular Mautrix bridges at the bottom of this doc.
Run the migration advisor first
Thankfully the tool that we will later use to handle the migration of users from your current deployment into MAS also has an advisor command.
We’ll use this to check if there are any issues with your current Synapse setup, which would be good to know upfront.
The tool can be installed with npm, if you haven’t already got it:
sudo apt install nodejs
sudo apt install npm
Install the syn2mas migration tool:
npm install -g @vector-im/syn2mas
Run the tool in advisor mode, pointing to your Synapse homerserver configuration file:
syn2mas --command=advisor --synapseConfigFile=homeserver.yaml
For the remainder of the tutorial I’ll presume that no issues were flagged by this command.
Set up the networking
MAS is a stand-alone application that needs to be reachable on a public domain.
This is typically achieved by setting up a domain like auth, mas, or account.example.com that points to your reverse proxy (if you’re using one) configured to direct the appropriate traffic to MAS, official guidance on that here .
Configure all the things
Using my example docker-compose.yaml, update the external ports as needed, and place it in a new directory of your choosing:
services:
matrix-auth-service:
image: ghcr.io/element-hq/matrix-authentication-service:latest
container_name: matrix-auth-service
environment:
- MAS_CONFIG=/app/config/config.yaml
ports:
- "8080:8080"
- "8081:8081" # health endpoint
volumes:
- ./config.yaml:/app/config/config.yaml:ro
depends_on:
- db
restart: unless-stopped
db:
image: postgres:latest
container_name: mas-db
# ports: - “5432:5432” # Only needed for the initial migration step later
environment:
POSTGRES_USER: mas_user
POSTGRES_PASSWORD: mas_password # Set a secure password
POSTGRES_DB: mas
volumes:
- ./db_data:/var/lib/postgresql/data
restart: unless-stopped
Generate a config.yaml file in the same directory:
docker run ghcr.io/element-hq/matrix-authentication-service config generate > config.yaml
Make changes to config.yaml as needed
Update the public_base and issuer to reflect your MAS domain name:
public_base: https://account.example.com/ #Update as appropriate
issuer: https://account.example.com/ # Update as appropriate
Change the database uri:
uri: postgres://mas_user:mas_password@db:5432/mas #following the configuration in your docker-compose.yaml file
Update the passwords section to allow for compatibility with your existing user’s passwords, which have a different algorithm.
passwords:
enabled: true
schemes:
- version: 1
algorithm: bcrypt
- version: 2
algorithm: argon2id
minimum_complexity: 3
Edit the Matrix section so it reflects your homeserver domain name. The endpoint can be a local address for Synapse if preferable. Generate a secret with high entropy: openssl rand -hex 96
.
matrix:
homeserver: example.com
secret: "SECRET"
endpoint: https//example.com/ # or local endpoint to Synapse
Add a new client section which will represent the client config for Synapse to connect to MAS. Again, generate a secret: openssl rand -hex 96
.
clients:
- client_id: 0000000000000000000SYNAPSE
client_auth_method: client_secret_basic
client_secret: "SECRET"
Check the configuration looks good by running the MAS cli config check
command:
docker run -v /path/to/directory/containing/mas/config:/config \
ghcr.io/element-hq/matrix-authentication-service --config /config/config.yaml config check
You should see Configuration file looks good
in the output, else address the issues.
Now we need to update the Synapse homserver.yaml configuration
Add the following config to the bottom of your synapse configuration file and update the issuer to reflect your MAS public domain name.
experimental_features:
msc3861:
enabled: true
# Synapse will call `{issuer}/.well-known/openid-configuration` to get the OIDC configuration
issuer: https://account.example.com/
client_id: 0000000000000000000SYNAPSE
client_auth_method: client_secret_basic
client_secret: "SECRET"
admin_token: "SECRET"
# URL which Synapse will use to introspect access tokens
# Defaults to the URL advertised by MAS, e.g. `https://{public_mas_domain}/oauth2/introspect`
# This is useful to override if Synapse has a way to call the auth service's
# introspection endpoint directly, skipping intermediate reverse proxies
introspection_endpoint: "http://[local MAS address]/oauth2/introspect"
client_secret
must match the first secret you generated; ie the client_secret
part of the clients
section in the MAS config above.
admin_token
must match the second token you generated; ie the secret
part of the matrix
section in the MAS config above.
The introspection endpoint is an optional config to allow Synapse to talk to MAS locally for introspections (for speed), rather than going through the public MAS domain name and any reverse proxies.
Update the .well-known client file
Since you’re running your own synapse deployment already, you’ll know that a json file needs to be served at https://example.com/.well-known/matrix/client , add another section here to instruct clients where your MAS instance is located. An example could look like this:
{
"m.server": {
"base_url": "https://example.com"
},
"m.homeserver": {
"base_url": "https://example.com"
},
"org.matrix.msc2965.authentication": {
"issuer": "https://account.example.com/",
"account": "https://account.example.com"
}
}
Migrate users to MAS
At this point, if you haven’t already, start up your MAS docker stack with docker compose up -d
so we can begin the migration steps below. You can check for any obvious startup errors at this point with docker compose logs -f
.
You should also stop synapse before proceeding.
The issue we’ll face next running the migration tool, if you’ve followed my instructions so far, is that syn2mas runs outside of the docker stack that MAS is deployed in and it can’t reach its postgres DB.
So temporarily we’ll need to do two things, which you should reverse after completing the migration (there is probably a better way of doing this!).
- Expose the db ports outside of the stack by uncommenting the
ports:
line in the docker-compose.yaml file provided earlier.
Restart the service at this point with
docker compose down
docker compose up -d
- Update the MAS db uri in config.yaml, so that it uses the ip address of the host machine, which syn2mas can now reach:
uri: postgres://mas_user:mas_password@LOCAL_IP_ADDRESS:5432/mas
Do not restart MAS again, and carry on with the migration in test mode first:
syn2mas --command migrate --synapseConfigFile /path/to/synapse/config/homeserver.yaml --masConfigFile config.yaml --dryRun
If everything runs well, and no errors are shown, you can go ahead with the actual migration by removing the dryRun flag:
syn2mas --command migrate --synapseConfigFile /path/to/synapse/config/homeserver.yaml --masConfigFile config.yaml
Finally reverse the two changes we made above for syn2mas to run, and restart both MAS and Synapse.
We should now be up and running
If all went to plan you’ll now have MAS running, Synapse configured correctly, and all your users migrated over.
The MAS cli has a doctor
command which I’d recommend running to make sure everything is ok:
docker compose exec matrix-auth-service mas-cli doctor
🚀 Hopefully at this point you can login! Try it on a compatible oidc native client (like ElementX or Element Web), or directly at account.example.com.
Helpful things to know
👥 Creating a user - either open up registration through a config change , so users can register themselves, or go via the command line:
docker compose exec matrix-auth-service mas-cli manage register-user
🔒 Lock and deactivate a user
docker compose exec matrix-auth-service mas-cli manage lock-user --deactivate username
🧑💻 QR code login - now you have MAS you can setup QR code login on supported clients. QR code login is great because it transfers your crypto identity and there is no need to enter a password or passphrase. Make sure you have Synapse running on at least version 1.106 and that you have MSC4108 enabled in your synapse config:
experimental_features:
msc4108_enabled: true
You may also need to adjust your reverse proxy to ensure the right headers are passed through, as documented here .
🌉 Encrypted Bridges - not all bridges support the new authentication APIs and end to bridge encryption, additional steps are required. This example shows how to get it working with the Mautrix bridges, which added support late 2024, again you’ll need a recent version of Synapse:
In your Synapse config enable the following MSCs:
experimental_features:
msc4190_enabled: true
msc3202_device_masquerading: true
In your mautrix bridge config under encryption:
add:
msc4190: true
In your bridge registration yaml file add:
io.element.msc4190: true
Then restart Synapse.
➕ Finally check out the extensive official documentation for even more things to tweak!