Synapse Token Authenticator is a synapse auth provider which allows for token authentication (and optional registration) using JWTs (Json Web Tokens) and OIDC.
Table of Contents
pip install synapse-token-authenticator
Here are the available configuration options:
jwt:
# provide only one of secret, keyfile
secret: symetrical secret
keyfile: path to asymetrical keyfile
# Algorithm of the tokens, defaults to HS512 (optional)
algorithm: HS512
# Allow registration of new users, defaults to false (optional)
allow_registration: false
# Require tokens to have an expiry set, defaults to true (optional)
require_expiry: true
oidc:
# Include trailing slash
issuer: "https://idp.example.com/"
client_id: "<IDP client id>"
client_secret: "<IDP client secret>"
# Zitadel Organization ID, used for masking. (Optional)
organization_id: 1234
# Zitadel Project ID, used for validating the audience of the returned token.
project_id: 5678
# Limits access to specified clients. Allows any client if not set (optional)
allowed_client_ids: ['2897827328738@project_name']
# Allow registration of new users, defaults to false (optional)
allow_registration: false
custom_flow:
# provide only one of secret, keyfile
secret: symetrical secret
keyfile: path to asymetrical keyfile
# Algorithm of the tokens, defaults to RS256 (optional)
algorithm: RS256
# Require tokens to have an expiry set, defaults to true (optional)
require_expiry: true
# This endpoint will be called when new user is registered
# with `{"token": <token>}` as its request body
notify_on_registration_uri: http://example.com/notify
# Bearer auth token for `notify_on_registration_uri` call (optional)
notification_access_token: 'my$3cr37'
It is recommended to have require_expiry
set to true
(default). As for allow_registration
, it depends on usecase: If you only want to be able to log in existing users, leave it at false
(default). If nonexistant users should be simply registered upon hitting the login endpoint, set it to true
.
First you have to generate a JWT with the correct claims. The sub
claim is the localpart or full mxid of the user you want to log in as. Be sure that the algorithm and secret match those of the configuration. An example of the claims is as follows:
{
"sub": "alice",
"exp": 1516239022
}
Next you need to post this token to the /login
endpoint of synapse. Be sure that the type
is com.famedly.login.token
and that identifier.user
is, again, either the localpart or the full mxid. For example the post body could look as following:
{
"type": "com.famedly.login.token",
"identifier": {
"type": "m.id.user",
"user": "alice"
},
"token": "<jwt here>"
}
First, the user needs to obtain an Access token and an ID token from the IDP:
POST https://idp.example.org/oauth/v2/token
Next, the client needs to use these tokens and construct a payload to the login endpoint:
{
"type": "com.famedly.login.token.oidc",
"identifier": {
"type": "m.id.user",
"user": "alice" // The user's localpart, extracted from the localpart in the ID token returned by the IDP
},
"token": "<opaque access here>" // The access token returned by the IDP
}
This is similar to jwt flow except few additinal claims are checked:
name
claim must be presenturn:messaging:matrix:localpart
claim must be equal to user nameurn:messaging:matrix:mxid
claim must be valid mxid with localpart matchingurn:messaging:matrix:localpart
claim and domain name matching this homeserver domain
{
"type": "com.famedly.login.token.custom",
"identifier": {
"type": "m.id.user",
"user": "d2773fdb-91b5-4e77-9367-d4bd121afc48" // localpart, same as `urn:messaging:matrix:localpart` in JWT
},
"token": "<jwt IDToken here>"
}
An example of a JWT payload:
{
"iss": "https://auth.example.com",
"sub": "8fd1ec9b-c054-4de0-bbd0-90d40ce9200e",
"exp": 1701432906,
"urn:messaging:matrix:mxid": "@d2773fdb-91b5-4e77-9367-d4bd121afc48:homserver.matrix.de",
"urn:messaging:matrix:localpart": "d2773fdb-91b5-4e77-9367-d4bd121afc48",
"name": "Alice Bob"
}
Additionally, when a new user is registered, a POST json request is made with {"token": <token>}
as its request body. The handler of the request must return any json due to some implementation details (synapse's BaseHttpClient
poor interface)
The tests uses twisted's testing framework trial, with the development enviroment managed by hatch. Running the tests and generating a coverage report can be done like this:
hatch run cov
After tagging a new version, manually create a Github release based on the tag. This will publish the package on PyPI.
synapse-token-authenticator
is distributed under the terms of the
AGPL-3.0 license.