Feature: Single Sign-On
Goal: Security
Developer: Logilite Technologies
Goal
iDempiere 11 has added support for single sign-On. Single sign-on on help to get authenticated with third party identity provider like Azure MSAL or AWS cognito server. This remove requirement to store credentials of employee in database and make more secure against threat exposing user data.
iDempiere provides services to implement plugin for any SSO providers. The goal of this How-To is to show how to develop plug-in project to support new SSO provider. For creating blank plugin project, refer guide as Get Your Plugins Running
logilite has developed and made available plugins for MSAL (Azure) and AWS Cognito.
iDempiere has also included build in SSO support for OIDC compliance identity provider.
Not implemented yet
The current version of SSO does not implement changes on: - SOAP web services, these keep working with AD_User authentication - SSO provider per tenant is not implemented, this would require a mechanism to identify the tenant on the URL
SSO Provider plugin
Deepak Pansheriya has published plugins for Azure and Cognito, you can start using it with these plugins:
You can find instructions about how to configure AWS Cognito and the plugin within iDempiere in the provided file:
AmazonCognitoConfiguration.docx
Build in support for OIDC Identity Provider
iDempiere has build in SSO support for OIDC compliance identity provider.
We have tested SSO authentication with the following OIDC identity providers:
- Google Cloud OIDC Configuration
- Microsoft Azure OIDC Configuration
- Amazon Cognito OIDC Configuration
- Keycloak OIDC Configuration
How to implement SSO Provider Plugin
Adding new item in SSO Provider drop down
On SSO configuaraion screen has SSO Provider field which list all SSO provider implementations available as plugin. first step is to add your SSO provider name in this drop down. For same follow below steps.
- If you are adding new identity provider support, first step is to add SSO (identity) Provider Name in SSO Provider List on Reference.
- For same login as System user in system tenant.
- Open Reference window and locate record with name "SSO Provider List"
- Next go to tab list validation and create new entry
- Provide unique search key and name. Search key is used in your factory to identify configured SSO for your implementation
Adding dependencies in your plugin
- open MANIFEST.MF of your plugin project and on dependency tab under required plugins, add following plugin dependencies as shown below
- org.adempiere.base
- org.eclipse.jetty.servlet-api
- org.adempiere.plugin.utils
Implement SSO Factory
For implementing SSO factory, add new class implementing ISSOPrincipleFactory as below. you should add condition in which case the object of your SSO provider (ISSOPrincipalService) implementor should be returned. Mostly we used SSO Provider configured on sso configuration screen.
1import org.adempiere.base.sso.ISSOPrincipalService;
2import org.adempiere.base.sso.ISSOPrincipleFactory;
3import org.compiere.model.I_SSO_PrincipleConfig;
4
5import com.logilite.sso.test.principle.MSSOPrinciple;
6
7public class MySSOFactory implements ISSOPrincipleFactory
8{
9 @Override
10 public ISSOPrincipalService getSSOPrincipleService(I_SSO_PrincipleConfig config)
11 {
12 if ("MYSSOPROVIDER".equalsIgnoreCase(config.getSSO_Provider()))
13 return new MSSOPrinciple(config);
14 return null;
15 }
16}
Implement SSO Provider
SSO Provider class implements ISSOPrincipalService interface.
ISSOPrincipalService Method:
hasAuthenticationCode
- Method has request (HttpServletRequest) , response (HttpServletResponse) as a parameter.
- Method will return true, if a request has an authentication code from SSO provider, other wise false.
getAuthenticationToken
- Method has request (HttpServletRequest) , response (HttpServletResponse) and redirectMode (String) as a parameter.
- Method will send a request to SSO provider with the code & redirect URL to get the auth token & and save token in session.
- Token is save in session with attribute name SSOPrinciple.SSO_PRINCIPLE_SESSION_NAME.
isAuthenticated
- Method has request (HttpServletRequest) , response (HttpServletResponse) as a parameter.
- Method will return true, if session has auth token, other wise false.
redirectForAuthentication
- Method has request (HttpServletRequest) , response (HttpServletResponse) and redirectMode (String) as a parameter.
- Method will redirect request to the SSO provider for authentication.
removePrincipalFromSession
- Method has request (HttpServletRequest) , response (HttpServletResponse) as a parameter.
- Method will remove auth token from session.
getUserName
- Method has result (Object) as a parameter, result is token save in session.
- Method will return user name/email from token, as per System Configurator USE_EMAIL_FOR_LOGIN.
getLanguage
- Method has result (Object) as a parameter, result is token save in session.
- Method will return Login Language from token.
SSOUtils Method:
getRedirectedURL
- Method take redirectMode (String) , config (I_SSO_PrincipleConfig) as a parameter.
- Method will return Redirect URIs (String) from SSO Provider configured (config) as per redirectMode .
- We have Redirect Mode as below.
- SSOUtils.SSO_MODE_OSGI - Apache Felix Web Console Bundles
- SSOUtils.SSO_MODE_MONITOR - iDempiere Monitor
- SSOUtils.SSO_MODE_WEBUI - Web login
1import java.io.IOException;
2import java.text.ParseException;
3
4import javax.servlet.http.HttpServletRequest;
5import javax.servlet.http.HttpServletResponse;
6
7import org.adempiere.base.sso.ISSOPrincipalService;
8import org.adempiere.base.sso.SSOUtils;
9import org.compiere.model.I_SSO_PrincipleConfig;
10import org.compiere.model.MSysConfig;
11import org.compiere.util.Language;
12
13/**
14 * in core {@link #BridgeFilter}, {@link #AdempiereMonitorFilter}, {@link #SSOWebuiFilter} filter
15 * use ISSOPrinciple methods to log-in/authenticated users using the SSO provider.
16 */
17public class MSSOPrinciple implements ISSOPrincipalService
18{
19
20 private I_SSO_PrincipleConfig config;
21
22 public MSSOPrinciple(I_SSO_PrincipleConfig ssoConfig)
23 {
24 setConfig(ssoConfig);
25 }
26
27 /**
28 * When a request comes to any filter class, a request is checked for
29 * authentication code from the SSO provider.
30 */
31 @Override
32 public boolean hasAuthenticationCode(HttpServletRequest request, HttpServletResponse response)
33 {
34 // Check request has authentication code
35 return false;
36 }
37
38 /**
39 * if {@code #hasAuthenticationCode(HttpServletRequest, HttpServletResponse)} returns true, then
40 * request is sent to the SSO provider with the code & response redirect URL to get the
41 * authenticated user token & and saved token in the session attribute
42 * {@link #ISSOPrinciple.SSO_PRINCIPLE_SESSION_NAME}
43 */
44 @Override
45 public void getAuthenticationToken(HttpServletRequest request, HttpServletResponse response, String redirectMode) throws Throwable
46 {
47 /**
48 * we have three redirectMode
49 * 1. SSOUtils.SSO_MODE_OSGI - Apache Felix Web Console Bundles
50 * 2. SSOUtils.SSO_MODE_MONITOR - iDempiere Monitor
51 * 3. SSOUtils.SSO_MODE_WEBUI - Web login
52 */
53 // get redirect URL for SSO.
54 String sso_Redirect_URL = SSOUtils.getRedirectedURL(redirectMode, getConfig());
55 // SSO authentication token code
56
57 // request.getSession().setAttribute(ISSOPrinciple.SSO_PRINCIPLE_SESSION_NAME, token);
58 }
59
60 /**
61 * if {@code #hasAuthenticationCode(HttpServletRequest, HttpServletResponse)} returns false,
62 * then current session attribute {@link #ISSOPrinciple.SSO_PRINCIPLE_SESSION_NAME}
63 * have a token or not.
64 */
65 @Override
66 public boolean isAuthenticated(HttpServletRequest request, HttpServletResponse response)
67 {
68 // Check request session is authenticated
69 return false;
70 }
71
72 /**
73 * if {@code #isAuthenticated(HttpServletRequest, HttpServletResponse)} returns false,
74 * then the current request is redirected to the SSO provider for login/authentication.
75 */
76 @Override
77 public void redirectForAuthentication(HttpServletRequest request, HttpServletResponse response, String redirectMode) throws IOException
78 {
79 // Code for redirect to SSO provider
80 }
81
82 /**
83 * If there is any error SSO login/authenticating we remove the token and other save data from the
84 * request session.
85 */
86 @Override
87 public void removePrincipalFromSession(HttpServletRequest httpRequest)
88 {
89 // code to remove a token from the session or other info saved during SSO login.
90 }
91
92 /**
93 * return authenticate user name/email from token to validate in iDempiere
94 */
95 @Override
96 public String getUserName(Object result) throws ParseException
97 {
98 // check if IDempiere login validate by email/user name
99 boolean isEmailLogin = MSysConfig.getBooleanValue(MSysConfig.USE_EMAIL_FOR_LOGIN, false);
100 // Return the login user name/email.
101 return null;
102 }
103
104 /**
105 * return {@link #Language} for login user, can be changed from Role panel
106 */
107 @Override
108 public Language getLanguage(Object result) throws ParseException
109 {
110 // Return login language/user IDempiere default method Language.getBaseLanguage();
111 return null;
112 }
113
114 public I_SSO_PrincipleConfig getConfig()
115 {
116 return config;
117 }
118
119 public void setConfig(I_SSO_PrincipleConfig config)
120 {
121 this.config = config;
122 }
123}
Create OSGi Service Component Definition
- Create OSGI-INF Folder in plugin if does not exist
- Right click in OSGI-INF > New > Other..
- Search Component Definition
- Click Next
- Enter Class, Name and File name as shown in image below
- Click Finish
- Click Add Property with below details
- Name: service.ranking
- Type: Integer
- Values: 10
- Select Services section
- Provide Services > click Add
- Search ISSOPrincipleFactory
- Click OK
- Component Definition myssofactory.xml look as below
1<?xml version="1.0" encoding="UTF-8"?>
2<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.logilite.sso.test.factory.MySSOFactory">
3 <implementation class="com.logilite.sso.test.factory.MySSOFactory"/>
4 <property name="service.ranking" type="Integer" value="10"/>
5 <service>
6 <provide interface="org.adempiere.base.sso.ISSOPrincipleFactory"/>
7 </service>
8</scr:component>
Configuring Single Sign-On (SSO) in iDempiere
1. Log in as System User
- Access to system-wide configurations is typically limited to users with System-level privileges.
2. SSO Configuration
- This step involves setting up the connection between iDempiere and the chosen SSO provider.
- The configuration details include essential information like Tenant ID, Application Client ID, and Secret Key. These details allow iDempiere to communicate securely with the SSO provider.
- Redirect URIs are specified to ensure that the authentication process is seamless and secure.
- Marking a configuration as "Default" designates it as the primary SSO configuration, ensuring that it is used for SSO logins.
3. Enabling SSO for WebUI
- Once the SSO configurations are set up, the system-wide switch for enabling or disabling SSO for WebUI is controlled through the `ENABLE_SSO` System Configurator parameter.
- Setting `Configured Value` to Y activates SSO login functionality for WebUI, while N reverts to the standard iDempiere login page.
4. Enabling SSO for iDempiere Monitor
- A specific System Configurator parameter, `ENABLE_SSO_IDEMPIERE_MONITOR`, allows enabling or disabling SSO specifically for iDempiere Monitor.
5. Enabling SSO for Felix Web Console
- The `ENABLE_SSO_OSGI_CONSOLE` System Configurator parameter serves the same purpose but for Felix Web Console.
6. Using Email for Login Validation (Optional)
- This configuration, controlled by the `USE_EMAIL_FOR_LOGIN` parameter, provides flexibility in the login process. When enabled, users can log in using their email addresses instead of display names.
7. Role Selection when Logging in with SSO (Optional)
- The `SSO_SELECT_ROLE` parameter influences the login experience further. When enabled, users logging in through SSO are presented with a Role Panel, allowing them to select a specific role if applicable.
Technical Info: IDEMPIERE-5346