Securing your mobile API - Spring Security

Having recently posted an article on an approach to securing an API for a mobile app to use, here are the details of how something similar can be configured using Spring-MVC and Spring-Security (this post uses Java config - if you are un-familiar with Java config rather than XML then check out my previous post on the topic).


Basic webapp registration and authentication

The first thing we need is a basic web app that supports user registration and authentication.  I am not going to go into the details of how to build a Spring MVC app with basic security and users etc, so will assume you are familiar with basic Spring MVC architectures. For an example Spring webapp that supports user security and registration, you can check out one of my several random webprojects on GitHub.


As I am only looking at an API application - no web client right now - All I need to start with is to set up the security for registration and login:



Ok, so the above is my web security config - the configure() method is what is defining my URLs and the security - it's pretty basic, I state that anyone can access the resources (for css etc), the log-in and the sign-up and everything else should be authenticated.

The only parts of interest are:
  1. In the registerAuthentication() method, as well as the normal setting of UserServices and password encryptor, I am also adding a Remember Me Authentication provider
  2. In the configure() method on the formLogin component we are also setting a RemeberMeService
  3. We are setting @Order annotation on the class with value 2

This class basically just sets up the web part of the security, so all pretty straight forward.


API security configuration

For clarity and readability of code, I like to create separate configuration classes for the different aspects of security - Below is the security config for the API endpoints:



This file is a little more interesting, let's have a look at what is going on:
  1. First of all, we define @Order with value 1 - as you have probably guessed, this defines the order in which the security config is loaded/considered - we want the API security to kick in first
  2. In our configure() method, we first specify an ant matcher so the rules in this config only apply to URLs that start /api/
  3. We state that all requests to this pattern must be authenticated and apply a RememberMeAuthenticationFilter to run before the Basic authentication chain runs
  4. We also set the session management to be stateless - this refers to serverside sessions, as we don't want to retain server session state
  5. We then configure the Remember Me beans - this is all standard and as per the Spring guidelines here.  The only difference is that we have a custom TokenBasedRememberMeService - that we will look at later.


Security flow

Below is the basic security chain that we get as a result of the above confirmation
  1. Request comes in - we check if it matches /api/**
  2. If it does match, then all requests must be authenticated - and run the RememberMeAuthenticationFilter 
  3. The filter simply uses the RememberMeService and AuthenticationProvider to check the token/cookie value to see if it can pre-authenticate the request.
  4. If it has matched the URL but doesn't authenticate on the filter, then it will 403
  5. If the URL does not match /api/** then the request starts the second security config
  6. If the url matches the resources, sign-up, sign-in then allow the request to complete, otherwise require authentication, so (default spring behaviour) direct all other requests to the registered login page ("/")


Customising remember me

 So the good news is we have been able to re-use core Spring Security functionality so far, so no dubious hand-rolled solutions - We use the standard security chain, username/password authentication provider and Remember Me filter/provider. The only thing that we have to modify slightly is the RememberMeTokenService - the default behaviour for Remember Me assumes that the token will always be provided in a cookie (designed for auto-login websites if you tick "remember me"), and as we are extracting the token from the cookie and passing it in the request header instead, we need to modify the service class to grab it from the header.

As you can see, it's pretty simple - all we are doing is overriding the default extract cookie method so it looks in the request header instead.  I would really recommend you read through the source code for the RememberMe classes - starting with the filter - and seeing what is actually happening between the Filter, token service and auth provider, it's pretty clear to read and will help get a good understanding of what is going on.


Caveats

There are a few things to consider here:
  • This is fairly basic security - requests should be made over https to prevent man-in-the-middle attacks, alternatively look at a solution like Amazon use whereby the token isn't passed, rather it is used to hash the request body which is then checked on the server side (prevent the need for sending the user token every request)
  • This is currently open to anyone to use/build against - there aren't any checks for app key/secret etc - so you would probably want to build that in to prevent other parties building apps against the api and getting a user token in the same way.
  • I'm not a security expert - this is just my interpretation of the Google guidelines using Spring Securities RememberMe classes. This is really just a thought experiment, getting up to speed with how Spring Security works in more details and experimenting with mobile app fun. Use at your own risk..

10 comments:

  1. Very helpful. After a lot of googling on REST authentication, this looks the best to me. Would try in my next Spring MVC REST + AngularJs project.

    ReplyDelete
  2. Great Help! Please help me with this compile time error -

    The method registerAuthentication(AuthenticationManagerBuilder) of type ApiSecurityConfig must override or implement a supertype method

    ReplyDelete
  3. Excellent article Rob. Thanks for the thoughts on Oauth and the Google link.

    ReplyDelete
  4. I try implemented it but I have problem - http://stackoverflow.com/questions/32101153/spring-restful-authentication-using-cookies I would be grateful if you could look at this.

    ReplyDelete
  5. I updated my post on the SO I will be grateful for help, because I have no idea what can be wrong :(

    ReplyDelete
  6. Added an answer on SO: http://stackoverflow.com/a/32212810/258813 - possibly also worth looking at a simple webapp setup with basic spring-security login setup: https://github.com/robhinds/spring-four-template/

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete
  10. Is that possible to test rememberMe by JUnit and Mockito?

    ReplyDelete