This is a step by step presentation of how to use the google OAuth to secure your application. The source code is included, you can download it and customize it for your needs.
There are a lot of libraries available for OAuth implementation. This is an implementation from scratch grouped inside a servlet filter, the only library used is GSON. I wanted to use the Google libraries but wanted to do something useful for other identity providers as well.
Information needed for the implementation.
You need to setup a number of parameters for the implementation, both in your application and on the identity provider (google) side.
The first step is to create a google account, then register your application at the address https://console.developers.google.com. You provide the application, the callback urls (where google will redirect after successful authentication) and in exchange you will get two strings, one called client id, the other one called secret. Client id is pretty much common knowledge, secret should be kept private and usually is only passed via POST https requests from your app to Google.
One thing about callback urls: if you provide a https url and the certificate on your side is not signed by a CA, it seems it will not work.
This is a list with all the values you need to gather before proceeding
- GOOGLE_AUTH_URL - this is where you forward the first time in order to start the authentication process. A number of parameters is passed as per OAuth standard. Since the implementation is provided with this article and a documentation about these parameters is available everywhere, will not get in detail
- GOOGLE_TOKEN_REQUEST_URL - the url where the second request is pushed, this time a POST request, as a second step of the protocol
- GOOGLE_TOKEN_VERIFIER_URL - the url where the token that is ultimately provided by the identification provider (google) is passed. Most of the specialists recommend you use a JWT library, download the keys from google and check the token yourself, however this url comes in pretty handy.
- REDIRECT_URI - where google redirects back after successful authentication. A number of parameters are provided together with this url. This url must be registered as part of the applicton setup on the google side and the process will not work if there is any discrepancy between the two. localhost based urls work as well, so that you can adequately test the functionality from your machine
- CLIENT_ID - client id as provided upon successfully configuring the application at Google
- CLIENT_SECRET - the secret string provided upon successfully configuring the application at Google
- SCOPES - a list of space delimited scopes where you tell Google what kind of information you need from the user account. For authentication you don't care about anything except the openid and maybe email and some brief profile. Google is very good at providing access to various other resources and for this purpose additional scopes might be requested, but for now those three are the only thing needed
To recap, these are the values. I changed the client id and the secret because you should create your own :).
- private static final String GOOGLE_TOKEN_REQUEST_URL = "https://www.googleapis.com/oauth2/v4/token";
- private static final String GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
- private static final String GOOGLE_TOKEN_VERIFIER_URL = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=";
- private static final String REDIRECT_URI = "http://localhost:8080/ts/app.oauth";
- private static final String SCOPES = "openid email profile";
- private static final String CLIENT_ID = "the client id";
- private static final String CLIENT_SECRET = "the secret";
The solution presented here has a servlet filter. The filter is set to capture all the application traffic ("/*" url pattern) however the filter is rigged to only restrict access for the urls that end in html. Usually you register a front controller, be it the struts servlet or the spring dispatcher servlet and this is the traffic you need to restrict for non authenticated users.
Here is the web xml file
- <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
- id="Your_WebApp_ID" version="2.5">
- <display-name>Archetype Created Web Application</display-name>
In this situation I really had some index.html file, but you are free to define a servlet and map all the *.html traffic through it, as you probably do at Spring MVC. For other type of traffic for example *.do, go in the filter and change the SUFFIX_HTML from .html value to the .do one.
The filter accesses a value object in the application session through the method getSessionData(). This is the object where the user data is stored. For this purpose I provided the class SessionData. The object will always be in the session because of this functionality - under the key "session.data". The user is considered authenticated when the userId attribute of this object is filled out. Feel free to change the key, the class, everything as long as the isLoggedIn method will still work consistently.
So how all works:
- You are not logged in
- Hit the application
- The filter established this is about trying to access a protected resource (in my case html)
- The session data object is retrieved from the session or created if needed and the fact the user is not logged in is confirmed
- The app redirects to Google if the user is not logged in
- Google does the authentication. First time in, the user will be asked if he / she wants to provide the id, email and "brief profile info" to this application
- If confirmed, Google redirects back in the application with a code
- Code, client id and secret are sent back to google by the filter who issues a POST request to Google and in exchange gets the token
- Filter uses the validation token url at Google to get all the information associated with the id token. The auth token is not needed here as the only use of Google service is to authenticate the user, we don't care about other Google API access
- Upon completion, the filter populates the session SessionData object
- Put together the web.xml
- Ensure the urls that are protected are .html ones or change to your front controller like *.do, whatever you need
- Ensure the callback url configured in the application matches the one configured on Google and that the filter properly knows when it processes a callback url - in this case I am looking for all the application requests whose url ends in app.oauth
Here are the sources