The introduction post sort of served as a primer about why I wanted to create this module. This post is going to talk about technical side of the module and for what it should (and shouldn’t) be used. Or something like that. We’ll see where it went once I’m done writing it.
Authentication Module
Every site that wants to deliver customized content needs a way to recognize the user. The more customized and personal, the more likely you’ll want actual user identification rather than just plain browser recognition.
I figured that most sites I want to build will have some element of user identification, I may as well write a module that I can drop in and get going without having to worry too much about recreating it from scratch each time. Thus here we are, a Java servlet based module that will allow the developer to drop in a few servlets, modify a configuration file or two, and have a bare bones site that handles user logins from a variety of 3rd party systems.
Java
Yeah, it’s what I like, and what I still think is pretty relevant when it comes to server side content. I debated if I wanted to try something new, but I think for the backend, I’m comfortable with Java and it provides well in terms of support and being able to find and use libraries. I’ll save my experimenting for front end work.
Concept
The idea is to have a system that will allow the developer to add 3rd party login support by trivially calling some servlets and inserting provided Javascript. I want to get away from having to rely on some deep configuration of the host platform for user and password storage. It just makes things too complicated since most of the time, all you really want is to know who the user is and if they have been validated so that you can get/set their preferences and create a customized experience for them.
I also want to have this divorced from any particular system or infrastructure so that it can just run. No need for anything WebSphere or Tomcat or Glassfish specific or any deep configuration of LDAP or JAAS or some security module. Just alter a property file and you’re good to go. Now, is this the right way to do it? Well, that can be debatable especially since these servlet systems do offer a lot of robust options, but I also just want to give creating this project a shot.
In the end, it’s meant to serve needs that I’m identifying during site creation, so that’s my driving force. Along the way, I hope that it may be of help to others who want a bootstrap base to explore and jump from. We’ll see how well that gets accomplished.
Flow
The basic idea is to:
- Have a screen with a bunch of login buttons.
- Hook those buttons onto a servlet or javascript as needed.
- Upon user click, initiate the needed redirection and prompting defined by the flow requirements of the 3rd party systems.
- Upon proper authorization, grab the basic data returned from the 3rd party and present that information to the backend system to allow it to use it for a customized user experience.
Implementation
A collection of 4 servlets, 2 of which are more core, and the other 2 slightly more optional (read: yet to be fully implemented).
- LoginServlet - Triggers sending the browser to the official authentication page of the 3rd party system.
- CallbackServlet - Location the browser will be sent to after the user has authenticated with the 3rd party. This is usually a registered address with the 3rd party system or passed to it as part of the initial login (automatic).
- LogoutServlet - A way to remove the current user information from the browser session.
- DisconnectServlet - A way to decouple the 3rd party system from the user. ie. Remove authorization to leverage that 3rd party platform.
Looking aside the last 2 for now (since their implementations are on the lesser scope of interest), the system really only requires 2 solid servlets to function.
Login
The com.subdigit.auth.servlet.LoginServlet
instantiates the com.subdigit.auth.AuthenticationHelper
class which is what actually handles the gritty details. I did this with the hope that I can easily decouple the system from a specific servlet framework instance. For now the AuthenticationHelper requires the servlet’s request and response to be passed in so that it has access to the necessary information, but I can see this evolving so that you can extract the information from any source into the AuthenticationHelper to have it behave accordingly with 0 requirement to be in a servlet environment at all.
The AuthenticationHelper is issued a authenticationHelper.connect()
call to start the process. It figures out which 3rd party system you’re calling, finds the proper class that can handle that system’s requirements (which needs to extend the com.subdigit.auth.AuthentcationService
interface), dynamically loads it, and calls that service’s authenticationService.connect()
method. That method creates the URL to the 3rd party system with all the required parameters (like the application id and key) and redirects the browser to it to prompt the user to authenticate.
Callback
Once the user authenticates, the 3rd party system is told to callback to the com.subdigit.auth.servlet.CallbackServlet
which instantiates another AuthenticationHelper that chains the same AuthenticationService to call the authenticationService.validate()
method. At this point, the system usually has an abstract token for the user, which then needs to be looked up with the 3rd party system to get the relevant details (like name, email, etc). Each service varies how this is done, which is why we need a per-service class (See the com.subdigit.auth.service
package) that can handle these issues per 3rd party you want to connect to the Authentication Module.
Once the user’s information is retrieved, the service then packages all the data it has into an com.subdigit.auth.AuthenticationResults
object which is floated back up to the CallbackSservlet. At the CallbackServlet level, the developer can probe the AuthenticationResults object’s various methods to access the stored information about the user. This will then allow the developer to correlate the data in the AuthenticationResults object to an existing user in the local datastore or to create a new user.
Installation
Download the source from GitHub:
github clone https://github.com/subdigit/authentication.git
That should get you an authentication/
directory. You will need to copy:
src/main/resources/authenticationserviceconfiguration.properties.sample -> src/main/resources/authenticationserviceconfiguration.properties
And fill in the appropriate .appid
and .appsecret
values for the services you want to use. It’s a hassle, but you’ll need to figure out the developer pages for the services and do the whole registration of your app there. Some URL hints for that are on the project’s README.md file. Once inserted, make sure the .enabled
is set to true for the ones you will use.
Test Instance
To run a test instance, be sure you have Maven installed and run:
mvn tomcat7:run
That should fire up a bare bones system on localhost:8080. The systems you’ve enabled should show up there and you should be able to click and log into the test page with them. Should :).
Integration
The servlets provided are really bare bone samples. Take the flow in each of the goGet/doPost of the servlets and implement your own desired customizations in them. You can probably copy the LoginServlet as is and write your own LogoutServlet for however you want to get the user logged out. The only one where you should modify the code would be in the CallbackServlet. Once the AuthenticationResults object is returned from the authenticationHelper.validate()
, this would be where you need to probe the results to figure out who the user is and how they relate to your existing system (new user, existing user, etc). At this point you will need to decide where to redirect the user to and if a new account needs to be created and so on.
The authenticationResults.getServiceUserID()
will return back the primary user identifier from the 3rd party system and authenticationResults.getService()
will let you know the 3rd party service in question. That should be enough information to find that same user in your back end and load their information. If they are new, you can get back some information from authenticationResults.getVariable(<String>)
or get back all the parsed data via HashMap<String,Object> authenticationResults.getDataStore()
. And if you still need more information, you can get the actual object that was returned back from Object authenticationResults.getReturnData()
. You will need to look into the code for each service to find out what type of object is being stored.
Questions
If you have any questions about how to get this working, feel free to leave them here or to just bug me on Google+. Twitter is ok too, but the 140 character limit will just end up being frustrating.
Oh, and be more than free to do what you want with the code, just that if you make any changes, share them!
yeah, it’s still got a while to go, but this can be a start.