As today’s information systems are going through a rapid wave of security hardening, WSO2 Multi Factor Authentication is coming into the spotlight to ensure the identity of a user in more context-aware mechanisms. The ability to adapt and switch based on multiple different scenarios a user may access your system, is imperative in this context.
For example, let’s say your system handles user login through 3 factors — Basic credentials, face detection and a One-time-Password sent to the email. Now, if user A is accessing your system from the Company intranet, you may want to make the login process easier by skipping the One-time-Password. This pattern for the Administrator to conditionally enable/ disable steps in the login flow is formally defined as “Conditional Authentication”, and WSO2 Identity Server has come up with a neat feature to facilitate this.
I hope the following walkthrough will help you grasp this concept and use it wisely!
Background of the Sample
To demonstrate this feature, I will pick a scenario where a system has 2 authentication steps; Basic credentials and Email based One-Time-Password authentication. The example will be based on the case of the user trying to login to the system from public internet, or from the company intranet, so that, if the user is within the company intranet, this sample will skip the Email OTP authentication step. The condition will be based on the authentication request’s source IP address, inferred from the “x-forwarded-for” header.
Pre-requisites
- WSO2 Identity Server 5.6.0
- Nginx server (To simulate the x-forwarded-for header)
- Apache tomcat 9.x
- Google account with app permission: (You will need to create a test google account)
- Sample web application with SSO enabled
Sample Web application
You can download the war file I used here (http://saml2-web-app-dispatch.com.war/), or build it from the source by cloning from this repo.
Copy the built war file from above path to your tomcat webapps directory and make sure that it is deployed successfully.
** Going along WSO2 guidelines, I have used “localhost.com” in the webapp. Therefore, we need to add a mapping to it at the /etc/hosts file (in case of Linux) :
`127.0.0.1 localhost.com`
Identity server configuration
Email OTP configuration
We can follow the official WSO2 documentation to configure this. For reference, I have listed the changes to “repository/conf” directory here.
However, different to the document, we are using the newer SAML2 SSO sample web application. The Identity provider and service provider configuration from my setup is below:
Identity Provider required for federating EmailOTP to Google
Service Provider needed for the web application
Service Provider — SAML SSO configuration
Service Provider — Local and Inbound Authentication Configuration
Now we can try out the login flow with URL “http://localhost.com:8080/saml2-web-app-dispatch.com“. You will see the below sequence:
*** Make sure to update the admin user’s email address to a valid email address so that you can retrieve the OTP code from the email.
Making things conditional
Custom Claim to hold the trusted values
We are trying to skip the OTP authentication if the user’s IP address is a trusted one. So, we need a way to store each user’s trusted IP address in their profile for comparison with incoming requests. Best way to do this is by using a custom claim.
1. Navigate to “Claims” -> “Add” -> “Add local claim” and create a claim with following attributes:
2. Since we have enabled the claim as a default attribute on user profile, you can update the test user’s profile with the trusted IP address value:
Adding the authentication script
- Restart the Identity server as: “sh wso2server.sh -DenableConditionalAuthenticationFeature”
- Once started you will be able to see below extra section below the authentication steps in the “Local and Outbound Authentication Configuration” -> “advanced” section in the service provider:
- Check the box “Enable script based conditional authentication” and paste the following block into the text area :
- Click update on the configuration as well as the service provider page.
All ready except…
The “x-forwarded-for” header is not a default header in HTTP requests. It is only added if the request has been routed through a proxy / load balancer, meaning that the WSO2 Identity server has to be behind a proxy for us to capture the header from the authentication request.
This is common practice in production environments. Therefore, to simulate the header, we need to front the Identity server with a load balancer, which is in our case, Nginx.
- Install Nginx.
- Create a public and private key to be used for Nginx routing:
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout <KEY_FILE_PATH> -out <CERTIFICATE_PATH>
- Add the following block to nginx.conf file just before the line “include servers/*;”
- Restart the Nginx server.
- What we are essentially doing here is let Nginx listen on the default WSO2 port while the traffic can be routed to an Identity server with port offset. So, you will also need to increase the port offset = 2 in the IS_HOME/repository/conf/carbon.xml and restart the Identity server.
Ready … Set … Go!!
Now we are ready to try out conditional authentication based on the request IP address!
- Update the “Known IP Address” value of “admin” user to be “10.10.3.12” from the WSO2 management console.
- Login to the app at “http://localhost.com:8080/saml2-web-app-dispatch.com“. You will notice that the login flow includes both basic authentication and the OTP code.
- Update the “Known IP Address” value of “admin” user to be “127.0.0.1” from the WSO2 management console.
- Login to the app at “http://localhost.com:8080/saml2-web-app-dispatch.com“. You will notice that the login flow ends with just basic authentication.
Finishing thoughts
This is a very powerful feature coming with the latest WSO2 Identity server, and we can use various other information from the request headers, user claims, or even user roles to customize the authentication flow at run time.
Happy authenticating!