Synapse authentication module which allows for authenticating and registering using JWTs
Project description
Synapse Token Authenticator
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
Installation
pip install synapse-token-authenticator
Configuration
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
oauth:
# see OAuthConfig section
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
.
OAuthConfig
Parameter | Type |
---|---|
jwt_validation |
JwtValidationConfig (optional) |
introspection_validation |
IntrospectionValidationConfig (optional) |
username_type |
One of 'fq_uid' , 'localpart' , 'user_id' (optional) |
notify_on_registration |
NotifyOnRegistration (optional) |
expose_metadata_resource |
Any (optional) |
registration_enabled |
Bool (defaults to false ) |
At least one of jwt_validation
or introspection_validation
must be defined.
username_type
specifies the role of identifier.user
:
'fq_uid'
— must be fully qualified username, e.g.@alice:example.test
'localpart'
— must be localpart, e.g.alice
'user_id'
— could be localpart or fully qualified usernamenull
— the username is ignored, it will be source from the token or introspection response
If notify_on_registration
is set then notify_on_registration.url
will be called when a new user is registered with this body:
{
"localpart": "alice",
"fully_qualified_uid": "@alice:example.test",
"displayname": "Alice",
},
expose_metadata_resource
must be an object with name
field. The object will be exposed at /_famedly/login/{expose_metadata_resource.name}
.
jwt_validation
and introspection_validation
contain a bunch of *_path
optional fields. Each of these, if specified will be used to source either localpart, user id, or fully qualified user id from jwt claims and introspection response. They values are going to be compared for equality, if they differ, authentication would fail. Be careful with these, as it is possible to configure in such a way that authentication would always fail, or, if username_type
is null
, no user id data can be sourced, thus also leading to failure.
JwtValidationConfig
RFC 7519 - JSON Web Token (JWT)
Parameter | Type |
---|---|
validator |
Validator (defaults to Exist ) |
require_expiry |
Bool (defaults to false ) |
localpart_path |
Path (optional) |
user_id_path |
Path (optional) |
fq_uid_path |
Path (optional) |
displayname_path |
Path (optional) |
required_scopes |
Space separated string or a list of strings (optional) |
jwk_set |
JWKSet or JWK (optional) |
jwk_file |
String (optional) |
Either jwk_set
or jwk_file
must be specified.
IntrospectionValidationConfig
RFC 7662 - OAuth 2.0 Token Introspection
Parameter | Type |
---|---|
endpoint |
String |
validator |
Validator (defaults to Exist ) |
auth |
HttpAuth (optional) |
localpart_path |
Path (optional) |
user_id_path |
Path (optional) |
fq_uid_path |
Path (optional) |
displayname_path |
Path (optional) |
required_scopes |
Space separated string or a list of strings (optional) |
Keep in mind, that default validator will always pass. According to the spec, you probably want at least
type: in
path: 'active'
validator:
type: equal
value: true
or
['in', 'active', ['equal', true]]
NotifyOnRegistration:
Parameter | Type |
---|---|
url |
String |
auth |
HttpAuth (optional) |
interrupt_on_error |
Bool (defaults to true ) |
Path
A path is either a string or a list of strings. A path is used to get a value inside a nested dictionary/object.
Examples
'foo'
is an existing path in{'foo': 3}
, resulting in value3
['foo']
is an existing path in{'foo': 3}
, resulting in value3
['foo', 'bar']
is an existing path in{'foo': {'bar': 3}}
, resulting in value3
BasicAuth
Parameter | Type |
---|---|
username |
String |
password |
String |
BearerAuth
Parameter | Type |
---|---|
token |
String |
HttpAuth
Authentication options, always optional
Parameter | Type |
---|---|
type |
'basic' | 'bearer' |
Possible options: BasicAuth
, BearerAuth
,
Validator
A validator is any of these types:
Exist
,
Not
,
Equal
,
MatchesRegex
,
AnyOf
,
AllOf
,
In
,
ListAnyOf
,
ListAllOf
Each validator has type
field
Exist
Validator that always returns true.
Examples
{'type': 'exist'}
or
['exist']
Not
Validator that inverses the result of the inner validator.
Parameter | Type |
---|---|
validator |
Validator |
Examples
{'type': 'not', 'validator': 'exist'}
or
['not', 'exist']
Equal
Validator that checks for equality with the specified constant.
Parameter | Type |
---|---|
value |
Any |
Examples
{'type': 'equal', 'value': 3}
or
['equal', 3]
MatchesRegex
Validator that checks if a value is a string and matches the specified regex.
Parameter | Type | Description |
---|---|---|
regex |
str |
Python regex syntax |
full_match (optional, true by default) |
bool |
Full match or partial match |
Examples
{'type': 'regex', 'regex': 'hello.'}
or
['regex', 'hello.', false]
AnyOf
Validator that checks if any of the inner validators pass.
Parameter | Type |
---|---|
validators |
List of Validator |
Examples
type: any_of
validators:
- ['in', 'foo', ['equal', 3]]
- ['in', 'bar' ['exist']]
or
['any_of', [['in', 'bar' ['exist']], ['in', 'foo', ['equal', 3]]]]
AllOf
Validator that checks if all of the inner validators pass.
Parameter | Type |
---|---|
validators |
List of Validator |
Examples
type: all_of
validators:
- ['exist']
- ['in', 'foo', ['equal', 3]]
or
['all_of', [['exist'], ['in', 'foo', ['equal', 3]]]]
In
Validator that modifies the context for the inner validator, going inside a dict key.
If the validated object is not a dict, or doesn't have specified path
, validation fails.
Parameter | Type |
---|---|
path |
Path |
validator |
Validator (optional, defaults to Exist ) |
Examples
['in', ['foo', 'bar'], ['equal', 3]]
ListAllOf
Validator that checks if the value is a list and all of its elements satisfy the specified validator.
Parameter | Type |
---|---|
validator |
Validator |
Examples
type: list_all_of
validator:
type: regex
regex: 'ab..'
or
['list_all_of', ['regex', 'ab..']]
ListAnyOf
Validator that checks if the value is a list and if any of its elements satisfy the specified validator.
Parameter | Type |
---|---|
validator |
Validator |
Examples
type: list_all_of
validator:
type: equal
value: 3
or
['list_any_of', ['equal', 3]]
Usage
JWT Authentication
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>"
}
OIDC Authentication
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
}
Testing
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
Releasing
After tagging a new version, manually create a Github release based on the tag. This will publish the package on PyPI.
License
synapse-token-authenticator
is distributed under the terms of the
AGPL-3.0 license.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for synapse_token_authenticator-0.6.0.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8528a60100428ef4b36a29f2a969e72d420179fb470ae4650dcbba7bfc58a752 |
|
MD5 | 0f1a7c15d572f882088ff7d8c9cc703a |
|
BLAKE2b-256 | aa0c04b63ea2ad10afeb288dd1a8fed29f9cfb148f39a5074108e86168e4ae59 |
Hashes for synapse_token_authenticator-0.6.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 14e1aef9c966642b577e0f1b32df3b377a7d3237b48ee9c8bfa02670052b22fe |
|
MD5 | cf73e6f5dbcaf89449ea5d5c2595fe10 |
|
BLAKE2b-256 | 943c80ae5cca184a452fee902503dcee13fbec981bd57b220b719f8c8f15d723 |