Implementing Single Sign On (SSO) when using GeneXus Access Manager (GAM) is very simple, but what if any of the applications in the system isn't integrated into GAM? Is it possible to use GAM as an Identity Provider even if at least one of the client applications doesn't use GAM?
Consider a scenario where an enterprise system already has identity information about the users of all the applications involved in the system. Rather than having each client application maintain its own user database with the user credentials, it would be easier to implement a SSO mechanism. The idea is to give the user access to all the web apps by logging in only once.
Applications using GAM can easily implement SSO without any development costs. In such case, the client applications and the Identity Provider must have Enable Integrated Security property set to True.
But if you need to implement SSO in a system where some of the applications were generated with GeneXus X Evolution 3 or, in fact, it has Enable Integrated Security property = False, you can also implement SSO.
In this case, it is a manual task on the client side of the system. In other words, as the clients aren't integrated into GAM, you have to program the necessary calls to the GAM Identity Provider emulating the real calls done when GAM is enabled on the client.
The calls follow the OAuth 2.0 protocol for solving the SSO problem.
Before you go on reading, make sure you understand the Behind the scenes of GAM SSO implementation.
Below is an example of an application generated using GeneXus X Evolution 2 (download the sample below).
1. The client application in this scenario isn't integrated into GAM. Consider a fictitious client application and generate GUIDs for the application credentials: Client Id and Client Secret information. There are online GUID generators you can use.
Afterwards, define the application in the Identity provider using the previous credentials. You can run the GAM Backoffice and create a GAM Application:
Here it's important to save de Application Credentials: Client ID and Client Secret, and check "Allow WEB authentication" property.
In this case, it's important to detail the Local Login URL, and what user scopes you want to obtain.
To learn more about User Scopes, see the following link: GAM - OAuth User Scopes
2. Define a Transaction in the client Knowledge Base which is used to store the parameters needed to establish the connection to the Identity Provider. In the example, the Transaction is the following:
The table associated with the Transaction stores the following parameters:
- Identity Provider Host, PORT, Secure (HTTP or HTTPS)
- Identity Provider Base URL
- Callback URL (Redirect URL). The Redirect URL must always be ../oauth/gam/callback. Afterwards, you define a redirection to a service associated with that end point.
The Callback URL parameter configuration is needed for Web Applications only.
- Client Id and Client Secret of the client application (the same specified in step 1)
3. The login Web Panel in the client calls a Procedure —called "login" in the example xpz— which does the following:
- Generate a random state string; for this example, the state is stored in the &state variable using the fixed value "GRESTD" plus a GUID value to make sure it is unique.
- Redirect to ../oauth/gam/signin on the server, passing the client_id information, the redirect_uri, the scope and the state.
The code looks as follows:
&GXGUID = GUID.NewGuid()
&state = "GRESTD" + &GXGUID.ToString()
&WebSession.Set(!"State", &state)
&url = GetBaseUrl(&IdentityProvider) + !"/oauth/gam/signin"
¶ms= !"oauth=auth&client_id=" + &ClienteId.Trim() +
!"&redirect_uri=" + &IdentityProvider.IdentityProviderRedirUrl +
!"&scope=gam_user_data+gam_user_roles+gam_user_additional_data+&state=" + &state.Trim()
Link(&url, ¶ms)
The execution of the URL (&url) checks in the Identity Provider GAM if there is a valid session. If not, the Local Login URL specified in step 1 (GAMRemoteLogin object) in the server is executed, so that the user can enter his credentials. Afterwards, the URL specified in the redirect_uri parameter is executed by a GET HTTP.
Remember that the redirect_uri is ../oauth/gam/callback.
4. The redirect to the redirect_uri specified in the previous step forces the execution of a Procedure, called loginresponse in the example xpz, that executes what it is detail below:
- Check the validity of the HTTP response.
- Web Applications: Generate a POST execution to oauth/gam/access_token on the server, to get the Access Token for the session.
- Other Applications: Generate a POST execution to oauth/access_token on the server, to get the Access Token for the session.
- Generate a POST execution to oauth/gam/userinfo on the server, to get user information.
- You may implement a way to save the session locally to the application, so it's valid for a period of time.
- Take into account that a complete logout occurs when the logout is done on the server. Check this article for more detail.
- See Some tips to debug SSO solution without using GAM.
If the client application is generated with C#, you need to add the following lines to the web.config file under the rules section:
<!--GXIGNORE_START-->
<rule name="GXGamCallback" stopProcessing="true">
<match url="^oauth/gam/callback$" />
<action type="Rewrite" url="aloginresponse.aspx" />
</rule>
<!--GXIGNORE_END-->
where loginresponse references the GeneXus Procedure in charge of the processing.
If the client application is generated with Java, you need to add the following lines to the web.xml file in the WEB-INF directory of the client Web app:
<servlet>
<servlet-name>GAMOAuthCallback</servlet-name>
<servlet-class>aloginresponse</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>GAMOAuthCallback</servlet-name>
<url-pattern>/oauth/gam/callback</url-pattern>
</servlet-mapping>
If the server application is generated with Java, it may be necessary to add a Filter in the Identity Provider web app, as shown below:
<filter>
<filter-name>FiltroJSON</filter-name>
<filter-class>sso.FiltroJson</filter-class>
</filter>
<filter-mapping>
<filter-name>FiltroJSON</filter-name>
<url-pattern>/oauth/gam/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
The filter should be added to the web.xml file in the WEB-INF directory of the Identity Provider web app.
Note that a package (optional) called SSO is used. So, in this case, the class is copied with its package under the WEB-INF\classes directory of the Identity Provider web app.
The following is the source of the class. You should compile the class and copy it to the server.
package sso;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class FiltroJson implements Filter {
private FilterConfig config;
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
public void destroy() {
config = null;
}
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
response.setContentType("text/json");
chain.doFilter(request, response);
}
}
ssosampleClientWithoutGAM
Single Sign On in applications using GAM
GAM - GAMRemote Authentication Type
HowTo: Signout from a SSO applications not using GAM