NF11 Single SignOn

From iDempiere en

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 make 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. 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.

Not implemented yet

The current version of SSO does not implement changes on: - SOAP and REST 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

How to Enable SSO

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

How to implement SSO

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
  1. org.adempiere.base
  2. org.eclipse.jetty.servlet-api
  3. org.adempiere.plugin.utils

Implementing 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 (ISSOPrinciple) implementor should be returned. Mostly we used SSO Provider configured on sso configuration screen.

 1import org.adempiere.base.sso.ISSOPrinciple;
 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 ISSOPrinciple 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 ISSOPrinciple interface.

ISSOPrinciple 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.
isAccessTokenExpired
  • Method has request (HttpServletRequest) , response (HttpServletResponse) as a parameter.
  • Method will return true, if auth token is expired, other wise false.
refreshToken
  • Method has request (HttpServletRequest) , response (HttpServletResponse) and redirectMode (String) as a parameter.
  • Method will send request to SSO Provider to refresh auth token & update token in session.
removePrincipleFromSession
  • 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.
    1. SSOUtils.SSO_MODE_OSGI - Apache Felix Web Console Bundles
    2. SSOUtils.SSO_MODE_MONITOR - iDempiere Monitor
    3. 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.ISSOPrinciple;
  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 ISSOPrinciple
 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	 * Check the token stored in a session attribute 
 84	 * {@link #ISSOPrinciple.SSO_PRINCIPLE_SESSION_NAME} is expired?
 85	 */
 86	@Override
 87	public boolean isAccessTokenExpired(HttpServletRequest request, HttpServletResponse response)
 88	{
 89		// check token expired date/time.
 90		return false;
 91	}
 92
 93	/**
 94	 * {@code #isAccessTokenExpired(HttpServletRequest, HttpServletResponse)} return true, then 
 95	 * sent a request to SSO Provider to refresh the current token
 96	 */
 97	@Override
 98	public void refreshToken(HttpServletRequest request, HttpServletResponse response, String redirectMode) throws Throwable
 99	{
100		// Code for a token refresh 
101	}
102
103	/**
104	 * If there is any error SSO login/authenticating we remove the token and other save data from the
105	 * request session.
106	 */
107	@Override
108	public void removePrincipleFromSession(HttpServletRequest httpRequest)
109	{
110		// code to remove a token from the session or other info saved during SSO login.
111	}
112
113	/**
114	 * return authenticate user name/email from token to validate in iDempiere
115	 */
116	@Override
117	public String getUserName(Object result) throws ParseException
118	{
119		// check if IDempiere login validate by email/user name
120		boolean isEmailLogin = MSysConfig.getBooleanValue(MSysConfig.USE_EMAIL_FOR_LOGIN, false);
121		// Return the login user name/email.
122		return null;
123	}
124
125	/**
126	 * return {@link #Language} for login user, can be changed from Role panel
127	 */
128	@Override
129	public Language getLanguage(Object result) throws ParseException
130	{
131		// Return login language/user IDempiere default method Language.getBaseLanguage();
132		return null;
133	}
134
135	public I_SSO_PrincipleConfig getConfig()
136	{
137		return config;
138	}
139
140	public void setConfig(I_SSO_PrincipleConfig config)
141	{
142		this.config = config;
143	}
144}

Create Component Definition

  • Create OSGI-INF Folder in plugin if does not exist
  • Right click in OSGI-INF > New > Other..
OSGI-INF Folder.png

Open Wizard.png

  • Search Component Definition
  • Click Next
  • Enter Class, Name and File name as shown in image below
  • Click Finish
Component Definition Wizard.png

NewComponentDefinition.png

  • Click Add Property with below details
    • Name: service.ranking
    • Type: Integer
    • Values: 10
Service.ranking.png

Service.ranking img.png

  • Select Services section
  • Provide Services > click Add
  • Search ISSOPrincipleFactory
  • Click OK
Service ISSOPrincipleFactory.png

Services ISSOPrincipleFactory Img.png

  • 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>


Technical Info: IDEMPIERE-5346

Cookies help us deliver our services. By using our services, you agree to our use of cookies.