Some companies are using SAML to authenticate the users using Tryton. Quite often in this setup the users and groups are managed in some way from the Identity Provider. The synchronisation between the Identity Provider and Tryton in this regard hasn’t been defined yet.
Hopefully there is a standard that has been devised to allow for the automatic exchange of identity information between IT systems: the System for Cross-domain Identity Management.
In this system, Tryton would provide the routes defined in the protocol RFC which defines a REST API enabling the exchange of ressources defined in the schema RFC. Those resources are users and groups, there is also some room for extensions.
When the Identity Provider creates / modify / delete a user / group, it will be the client consuming this REST in order to reflect those changes in Tryton (and probably other services).
Proposal
Creation of a new module crossdomain_identity_managment. This module will expose the required routes to implement the protocol:
/Users
/Groups
The schemas share those common attributes:
SCIM Attribute Name
Description
Remarks
id
A unique identifier of the resource.
We’re responsible for providing this. We should probably use uuids for this
externalId
A string that is an identifier used by the provisioning client.
The RFC states that it’s set by the client and that the Service Provider (Tryton) MUST NOT set it. It’s an optional field
meta
A complex field containing metadata (creation date, location, etc) about the resource
Both id and meta are required.
Users
SCIM Attribute Name
Description
Required
Remarks
userName
Tryton’s unique identifier of the user
login field
name
The user name. A complex attribute containing the components of the user name
name field that we will import for the formatted sub-attribute
displayName
The name of the user as it is usualy displayed
name field again. This one should probably take precendence from the one received from name
nickName
Casual way to address the user
We can ignore that
profileURL
An URI pointing to the user’s profile
We can ignore that
title
The user’s title
We can ignore that
userType
Identify the relation between the user and the organization
We can ignore that
preferredLanguage
The user preferred language in the format of the Accept-Language HTTP header
language field
locale
The locale of the user for the purpose of localizing dates, numerical representation
We can ignore that as it’s already encoded in the language in Tryton
timezone
The user’s timezone
We can ignore that
active
An indication of the user administrative status
active field
password
A write only attribute allowing to set / compare the user’s password
password field. If password authentication is allowed.
emails
A list of email addresses as complex attributes
email field. As we support only one email per user, we should probably use the one marked as primary or the first one with the type work or the first one
phoneNumbers
A list of phone numbers
We can ignore that
ims
A list of instant messaging address
We can ignore that
photos
A list of URIs pointing to the user’s image
avatars field
addresses
A list of physical addresses which are a complex type
We can ignore that
groups
A readonly list of groups the user belongs to
group membership changes are applied through the /Groups endpoint
entitlements
A list of entitlements. An entitlement may be an additional right to a thing / object / service
We can ignore that
roles
A list of roles for the user
We might use that with user_role but I think we can ignore that for now
x509Certificates
A list of certificates associated with the user
We can ignore that
Groups
SCIM Attribute Name
Description
Required
Remarks
displayName
Human readable name of the Group
name field
members
A list of members of the Group. Members are complex type with sub attributes value which is the id of a resource and $ref which is the URI of the resource. Resources can be User or Group
users field
An SCIMGroup Model should be created with the purpose of allowing a mapping between the groups in the Identity Provider and the res.group from Tryton. This model will contain
name field: a Char field containing the displayName
members field: a Many2Many field containing the res.user from the members definition
An scim_group field on res.group will point to the SCIMGroup in order to add the users defined in the members attribute in each group pointing to the SCIMGroup being created / modified.
A button on the SCIMGroup model will synchronise the data between the SCIMGroup and the res.group.
I propose this slightly complicated process because I expect that on the first synchronisation no SCIMGroup will exist and thus their link to the Tryton group won’t exist either. After the first synchronisation, using the button won’t be necessary as it will be call after each call on the REST API. The button is still useful though if an admin wants to force the synchronisation.
I think we should distinct that this is about implementing the receiver of the identities and not the management (which could be implemented in the future by another module where Tryton is the source of the identities).
To follow the naming convention (ex: user_role) and reduce length, I think it should be named: user_scim_server.
I think it should be used only on creation. Later the user can change it from the preferences without it being override by the SCIM.
Idem only on creation.
I think it will be better to ignore it.
Are you sure about that? If a user is added to a group, the SCIM client will only make a /Groups request? That seems odd.
In the same logic, would it not be better to store the SCIM id of the users as the user may not exist yet in Tryton when the group is created.
I think it’s clear enough that if we implement the routes then we are the receiving party (but we still have to reply to some GET queries anyway).
This is how standard diverges from one implementation to another .
If the user has a tool to change his prefered language in all the applications of the company why would we prevent him to change that and force him to go to Tryton to change his language ?
My fear is that a default value is set on the identity manager and the user change the value in Tryton. Then a change on the user (not related to the language) is made (like adding to a new group). Tryton will receive a POST to update the user but it will contain the original language which will reset the choice of the user.
Also the user may not have any way to modify such preference in the identity manager.
I may be wrong on this, but when configuring the connexion on the IdP side, we should be able to decide which information we want to send. In that case, we can choose whether the Language is sent or not. If it is, we should assume that it is the source of truth, otherwise we have nothing to do
I need to dig deeper in the protocol but there is indeed a route allowing to configure the schema supported by the service provider. So we might be able to state that some fields are readonly on our side.
I’ve found this discussion searching for hints to implement tryton and saml with keycloak. While I totally agree that SCIM is the “gold standard” for enterprise environments, I feel like we are overlooking a very pragmatic middle ground that I frequently implement for smaller setups: Just-In-Time (JIT) synchronization.
I often use this in Shibboleth contexts (e.g., connecting Moodle with an IdM), and in many cases, it is more than enough. I’m currently using Keycloak, and here is my perspective on why we should consider JIT alongside SCIM:
The Case for SCIM: I’m 100% with you that SCIM is the right way to go for proper lifecycle management. Being able to deactivate a user or prepare their account before they even log in is a must-have for larger setups. If we want Tryton to play in the “big leagues” of Identity Management, SCIM is the correct architectural answer.
The Reality of JIT: However, for many users, SCIM can be “overkill” or simply locked behind expensive enterprise licenses on the IdP side (many providers charge extra for SCIM provisioning). A simple group sync during the SAML/OIDC login (JIT) is often 95% of what is needed. It’s easy to set up, requires no extra infrastructure, and could work with a small module.
Addressing the “Deletion” concern: I understand the concern that JIT doesn’t “clean up” properly. But if we implement logic where missing groups in the SAML token lead to revoking those memberships in Tryton, we solve the majority of “permission creep” issues. And let’s be honest: if a user is fired and disabled in the IdP, they can’t log in anyway. For many SMEs, that is a perfectly acceptable security level.
Beside the “perfect” (SCIM), can we also have a “mostly good” (JIT) option for simpler setups? Supporting a basic group-to-role mapping during login would make Tryton way more accessible for people who just want to connect Keycloak, Authelia, or Authentik without a week of configuration.
Auto-creates users, syncs groups via external_id, creates employee records on first SAML login.
Note: Currently monkey-patches the authentication_saml ACS route—I’d prefer a cleaner approach (hooks? separate module? event-based?). Suggestions welcome.
We"re indeed thinking about making the routes in Tryton objects living in the pool.
In part for this development but also for others around SAML and what not.
I do not like the “JIT” in standard module (because it is not very standard and implementation always felt ad-hoc). We already removed such feature from the LDAP module.
But indeed it is annoying that the route must be replaced but we have a coming proposal to allow to extend routes in a more Tryton way.