Understanding the customization flows for OIDC SSO
The Mendix OIDC SSO module can be used to implement and use Single Sign-On (SSO) in your Mendix app using an Open ID Connect (OIDC) compliant Identity Provider (IdP). It offers runtime or deploy-time configurations for the most common scenario’s. However, certain use cases may require additional customization beyond the standard options.
For developers configuring the OIDC module, there are currently three key customization microflows available in the settings (IdPs for SSO and API security). This post will discuss each of these extension points—what they require, when to use them, and how to implement them effectively in your Mendix application:
- User Parsing
- User Provisioning (part of UserCommons)
- Token Parsing
This post is based on OIDC SSO module version 4.1. This version has some limitations and issues which have been reported.
Note: all these customization flows are part of the web callback processing in the login process. They occur after the user entered their information at the IdP for sign in. A simplified view of the “call stack” is shown in Call stack.
I want to customize…
I’ve tried to identify various use cases to help you quickly find the customization flow you need.
Nothing, tell me what the defaults are
If no customization microflows are selected, the OIDC module will still handle the parsing & provisioning. However the default behaviour is limited in what it will support.
- User Parsing: you must define a microflow for the user parsing, the default is
OIDC.OIDC_CustomUserParsing_Standard. This microflow takes all the attributes from theid_tokenand parses them toUserClaimobjects. Additionally it will store the locale (language) and timezone information on theUserInfoParam. - User Provisioning: no microflow needs to be defined, the OIDC module will still provision a user based on the settings in the “User provisioning” tab of the OIDC client configuration.
- Token Parsing: no roles will be mapped and the OIDC does a plain login, the user gets a Default Userrole configured on the “User provisioning” tab of the OIDC client configuration.
Which Mendix roles are assigned
Controlling which roles the user receives should be done in the Custom Token Parsing. This is probable the most common customization use case.
An association from the user
After sign-in the user might need to be assigned to the correct department/business unit/organization/tenant. Setting this association based on a UserClaim should be done in Custom User Provisioning.
An attribute on the user (account)
Setting application specific attributes either derived from claims or another source should be done in Custom User Provisioning.
A trigger for data sync
Assuming you need the User object to trigger the sync for the right context it should be done in Custom User Provisioning. Consider an asynchronous call to avoid slowing down the login process.
A call to an user-information endpoint
The user information is not always in the token, sometimes it is retrieved from a user information endpoint which should be done in Custom User Parsing.
Customization microflows
Custom User Parsing: OIDC_CustomUserParsing_Standard
Goal
Extract user information from either the ID token or via a GET request and make it available to the OIDC module via the entities UserInfoParam and UserInfoParam
Requirements
- Microflow name: the Custom User Parsing microflow name needs to start with
OIDC_CustomUserParsing. - Input parameters: the
OpenIDTokenJSON(string) and/or objectOIDC.Token - Output: must be an object of the type
OIDC.UserInfoParamand should have a list ofOIDC.UserClaimassociated to it, without the necessary claims it will break. Consider building your own logic and also callOIDC.OIDC_CustomUserParsing_Standardto get the standard set of claims.
Usage and limitations
You can take a look at the example microflows in the OIDC module starting with OIDC_CustomUserParsing_ as they will provide some insight in how to do a REST call to GET information about the user (e.g. when it is not provided in the ID_Token).
At this point you only have the id_token string, which is different from the access_token1 or the full OIDC.Token object. But no user object yet. This customization step is to prepare the claims for further processing. The OIDC module can either automatic mapping the UserClaims (if configured) or you can update the User(Account) in the custom user provisioning logic based on the UserInfoParam and UserClaims. There are several ways to do this: create your own UserClaim objects and associate them to the UserInfoParam or create a non-persistent helper object and associated this to the UserInfoParam.
Limitations & Issues:
- Nested JSON: the JavaAction
OIDC.CreateClaimsWithJSONwill break if theid_tokencontains nested JSON objects (reported to Mendix support), use your own import mapping and creation logic of OIDC.UserClaim when you have a nested JSON structure - Duplicate claims: the Microflow
SUB_HandlingClaimswill create duplicate UserClaim objects (reported to Mendix support)
In summary, custom user parsing is used to prepare the claims, but you cannot process them at this stage. That is where custom user provisioning comes in.
Custom User Provisioning: UC_CustomProvisioning
Goal
Customize the provisioning of user accounts before the user returns (as a signed-in user) to the Mendix application. It will allow you to map OIDC claims and update application specific properties.
Requirements
- Microflow name: the Custom User Provisioning microflow name needs to starts with
UC_CustomProvisioning - Input parameters: the
UserCommons.UserInfoParam(object) which needs to be labeledUserInfoParameterand theSystem.User(object) which needs to be labeledUser - Output: must return a
System.Userobject
Usage and limitations
The User Provisioning is actually done by the UserCommons module but is initiated by the OIDC module, when this logic is called the OIDC module already created or updated the user account and mapped the claims.
The user object is provided as the System.User object, you need to cast it to the specialization you need (e.g. Administration.Account). Make sure to commit any changes to the object within the customization microflow —the OIDC will not perform a commit on the object after performing the call to this custom microflow— and handle exceptions properly.
Typical scenario’s would be associating the user object to their correct organization/business unit/etc. or updating attributes to indicate that the user is federated / managed by SSO.
Limitations & Issues:
- Mapped claims: the claims mapping defined in the runtime does not apply correctly. Workaround: map the claims in the
UC_CustomProvisioningmicroflow again
Custom Token Parsing: CustomATP
Goal
Process the access token information and return Mendix roles. It is only for access related information.
Requirements
- Microflow name: the Custom Token Parsing microflow name needs to contain
CustomATP. - Input parameters: the
AccessTokenas a string, this still needs to be decoded - Output: must return a
OIDC.Rolelist, with the value set to the Mendix UserRole ModelGUID
Usage and limitations
The microflow should cover a number of steps:
- Decode the JWT (AccessToken string) and apply an import mapping (deserialize JSON into Mendix objects)
- Resolve the roles and map them to Mendix User Roles by creating
OIDC.Roleobjects and setting the attributeRoleNamewith theModelGUIDof theSystem.UserRole. The OIDC module will use the GUID to retrieve the roles and associate them to the user (seeOIDC.SUB_Update_OIDCUserRole). - Return the list of
OIDC.Role, leave it empty if no roles should be applied (this will return an error page to the user)
Keep this customization microflow limited to determining the correct roles the user should get based on the claims in the access token.
Limitations & Issues:
- The
OIDC.Default_OIDCProvider_TokenProcessing_CustomATPmicroflow is extracting roles from thescopeclaim in the access token, which does not align with the OIDC standard. This microflow is a bad example.
Call stack
The starting point for the OIDC user provisioning logic is an in incoming GET request on the /oauth/v2/callback endpoint. This will trigger the microflow webCallback. A simplified call stack is shown below
- webCallback
- handleAuthorizationCode
- Send request (POST) to IdP endpoint
protocol/openid-connect/token - CUSTOM_UserProvisioning
- UserProvisioning_Sample
- OIDC_CustomUserParsing_Standard (Custom User Parsing Microflow called via Java action
OIDC.CustomUserParsingMicroflow) - SUB_CreateUserClaim
- CreateOrUpdateUser
- CreateUserRecord (Java action)
- UC_CustomProvisioning (Custom User Provisioning Microflow called from within the Java action)
- CreateUserRecord (Java action)
- CreateOrUpdateUser
- OIDC_CustomUserParsing_Standard (Custom User Parsing Microflow called via Java action
- UserProvisioning_Sample
- Send request (POST) to IdP endpoint
- Default_OIDCProvider_TokenProcessing_CustomATP (Custom Token Parsing Microflow called via Java action
OIDC.CallCustomMicroflow) - SUB_ChangeRoleToUserRole
- SUB_Update_OIDCUserRole
- Redirect user to app return url (via httpHeader in httpResponse)
- handleAuthorizationCode
In summary, first User Parsing (claims), then User Provisioning (user) and finally Access Token Parsing (roles).
The
id_tokenis to provide information about the user (e.g. email, name, department), theaccess_tokencontains authorization information (e.g. roles, scopes, permissions). They often overlap a lot in terms of data they contain, but they do serve different purposes, you should look at theid_tokenwhen processing user identity claims ↩︎