This blogpost was written by the team at CleverAnalytics about their use of Stormpath and is reprinted from them with permission (and our thanks!). The updated Java SDK now includes token based authentication, and more!

CleverAnalytics is a location intelligence cloud platform. It allows you to easily create interactive and highly responsive business-oriented maps based on internal and external data. For example, you could calculate a store catchment area from order records and relate it to demographics and purchasing power. Business users can map their businesses in context and make informed decisions.

Cleveranalytics in action!

Providing this type of platform for enterprise businesses comes with lots of mainly non-functional requirements:

  • User Management – advanced tools for user provisioning via LDAP or Active Directory
  • Authentication – support standard authentication protocols for accessing the API
  • Scalability – grow with our customers
  • Data Security – guarantee data security and strong password management
  • Service Availability – SLA 99%
  • Flexibility – allow customers to customize their projects
  • Integration – easily integrate CleverAnalytics into customer’s business environment, for example:
  • Single Sign-on
  • Embedding maps as iFrame widget
  • Customer Workflow integration – data loading, scheduled exports etc.

Internally, we have added following requirements:

  • Performace – it’s always feature number one, the most visible feature for every customer
  • Multi-tenancy – Host thousands of projects in one shared AWS environment
  • Developer effectiveness – invest our development time in developing our core business and outsource all the general functionality

Our Stack

Fortunately, we started the project from scratch in February 2014, so we could easily choose technologies optimized for our requirements. We chose the AWS stack as a primary environment and a microservice oriented architecture to meet our scalability and flexibility needs.

HATEOAS REST API architecture is also a core architecture concept for communication between the javascript client and the server backend, as well as for internal communication between microservices and for any external application or public REST APIs.

Backend

As a core framework for our microservice, we decided to use a new Spring Boot framework that comes with completely self-contained executable jar. Getting rid of shared server container and running executable jars as a competely separated system processes, is an important backend principle. Spring Boots adds a powerful APIs for health check monitoring.

We also use other Spring frameworks in our stack, such as Spring MVC for REST API, Spring Security for application authentication and authorization, Spring Data for relational database access.

For storing persistent data there is a Postgres DB with Postgis extension for spatial data. Geo-spatial data are served by GeoServer – an open source server. It publishes a standard WFS (Web Feature Service) API for serving geospatial data in vector form, so we can transform the DB data to GeoJSON, add metadata and support operations like bounding box.

Frontend

Our web client is based on AngularJS MVW Framework and loads all data and metadata through a public REST API from CleverAnaltytics backend. The interactive map uses a Leaflet library. All business mapping data are transferred as vectors (GeoJSON), only the background map is consumed as an outsourced WMS (raster) service from MapBox.com.

REST Authentication API

This section describes how CleverAnalytics authentication works and describes the whole authentication workflow.

We have chosen OAuth 2.0 as an authentication protocol, because it suits SaaS application use cases best. A REST API can be used by many different types of clients:
* CleverAnalytics web client
* Third party SaaS applications
* Consultant tools
* Some operational and management tools

OAuth 2.0 focuses on developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and living room devices.

The protocol uses two types of tokens: refresh and access token. Firstly, a refresh token needs to be obtained and to do this, client must send valid authorization credentials to a login resource. When obtained, this token can be exchanged for a fresh access token (Bearer) that is used for authentication all HTTP requests.

Workflow

To see an authentication workflow in more detail, this section describes the REST API for the resources that participate in the authentication workflow.

Login – Refresh Token

The login resource handles requests with user name and password. If credentials pass verification, a new refresh token is generated and the client gets detail about the authenticated account. The following example shows it from the HTTP perspective:

As you can see, this login request succeeded and the server generated a new refresh token. It is returned as HTTP response header and the web browser will store it’s value as a cookie with given expiration time. The browser will completely maintain sending this refresh token with each request to our https://our.domain.com/rest url. The cookie’s flag HttpOnly means that the cookie cannot be accessed through client side script – a good practice for mitigating the majority of Cross Site Scripting (XSS) attacks.

The request body is returned with some basic information about the authenticated account.

Remember Me

Allowing the user to stay logged in when working with the web application is a “must-have” functionality and requires storing authentication credentials in a browser. For security reasons, it is not a good practice to store user credentials in browser cookie. OAuth 2.0 offers a better option – store the obtained refresh token, which is a better choice because it does not contain any user data, has limited validity and can be independently invalidated. This means a user can have more than one valid refresh token at the same time – the unique token is then stored at user’s laptop and tablet web browser.

When filling in the credentials in login screen, there is an option for enabling the “Remember Me” option. If enabled, the user will not be asked for credentials by current web browser for the next 90 days. Otherwise, the authentication is remembered only for the current browser session – it expires when user closes the browser application.

In both cases, the refresh token is stored in browser cookie and what differs is a setting of the max age:

Access Token

When REST API client gets a refresh token, it can ask for access token what is used for each request authentication. To obtain access token, REST API client only sends a GET request into /rest/oauth/token resource:

As you can see, the request above was successfully authenticated – and the web browser correctly set a cookie HTTP request header. The server returns a new access token with expiration time 15 minutes.

Access Authenticated Resource

The majority of CleverAnalytics REST API resources require authentication. The following example shows how to use the access token to make authenticated call on a REST API resource:

The authenticated request must have an Authentication header and the header value must be set to a valid access token with prefix Bearer. As you can see, the web browser sets the Cookie header too, but it is not evaluated on server side.

If the authentication fails, the response is:

Server returns 401 Unauthorized response status and sets response WWW-Authenticate header to Bearer realm="CleverAnalytics API" error="invalid_token". In this case, the access token was expired and REST API client will ask for the new Bearer token by GET /rest/oauth/token and retry the failed request again.

Logout

The final but still very important functionality in authetication workflow is a logout operation. For OAuth 2.0, this means the user wants to invalidate the refresh token. In a REST API, it is implemented as authenticated resource with method DELETE /rest/oauth/login

Server response is 204 No Content if the request was successfully processed.

Implementation

OAuth 2.0 focuses on client developer simplicity while providing specific authentication flows for web applications, tools, third party applications etc. But on the server side, implementation of OAuth 2.0 is quite a complex task. Every mistake in the implementation could cause a security vulnerability. Further to our requirement of offloading non-core functionality, we sought out a service that could handle all the hard work connected with authentication and user management for us.

Stormpath

After some research, we chose Stormpath, as it is strongly oriented to developers and offers exactly what we need. Stormpath announced support for OAuth 2.0 authentication this summer, and we successfully started to use this service in beta immediately after the launch. Visit the Stormpath web for more details about this service: http://docs.stormpath.com/guides/api-key-management/

To use Stormpath SDK in a Java Maven project, you need add following dependencies:

Spring Security

Spring security is a really powerful and highly customizable authentication and access-control framework. This section describes how to integrate Stormpath authentication into Spring Boot application.

Setting Security Configuration

To enable Spring Security you need add a new Maven dependency:

The next step is to enable integration Spring Security with Spring MVC in a Configuration with the annotation @EnableWebMvcSecurity. You can see complete example of configuration of WebSecurityConfigurerAdapter for hosting stateless REST API below. Look out for more comments inside the code:

The configuration enforces authentication for all resources except the following, two of which are available for an anonymous user:
* POST on /rest/oauth/login
* GET on /rest/oauth/token

A complete authentication flow with authentication filter is vizualized in a following sequence diagram:

Authentication sequence diagram

Stormpath Client

Stormpath Client is a SDK wrapper for Stormpath REST API. It is a primary class for communication with Stormpath from a Java application. In the Spring ecosystem, you can simply inject the Client bean anywhere you need it, but first it needs to be properly initialized. Stormpath provides a ClientBuilder for constructing the Client instance. An example below demonstrates setting up Stormpath Administrator Key and configuring cache manager:

In following resources examples is not injected the bean Client directly but a concrete Application bean for CleverAnalytics application. You can read more about Stormpath Application here: http://docs.stormpath.com/rest/product-guide/#applications

Login Resource

The login controller verifies the given user credentials by calling the Stormpath service. If verification passes, the resource generates a new refresh token. Here is a simplified fragment of code without any exception handling, logging etc.:

Token Resource

The controller of the token resource reads the value of the cookie CAN-RefreshToken, verifies validity of the refresh token and generates a new access token. The Stormpath REST API generates a new access token is provided on, but the API is looking for the refresh token in Authorization header. By contrast, we get a request that has a refresh token in cookie header.

Because we do not want to manipulate with refresh token on the client side (read this cookie is forbidden by the javascript HttpOnly flag), it is necessary to do this work on server. Instead of simply resending the incoming HTTP request, the server creates a new HTTP request and sets the refresh header to the Authorization request header to fit the expectations of the Stormpath API.

The following example is again a simplified fragment of code. Lots of the logic should be definitely be part of some Service class, and proper exception handling and logging are also omitted:

Logout Resource

The logout operation is an invalidation of the refresh token. This could mean an invalidation of all existing refresh tokens (ie. logout user from all it’s devices) or invalidating only the selected ones. In this example, all active refresh tokens are removed:

Authentication Filter

All incoming HTTP requests are handled by authentication filter before being dispatched to MVC Controllers. The authentication filter StormpathAuthenticationFilter is registered in the filters chain by the WebSecurityConfig configuration. The filter verifies the account token present from Authorization HTTP header. If the authentication succeeds, Stormpath API returns the ApiAuthenticationResult instance withdetails of the authenticated Stormpath account.

The Account is then set into SecurityContextHolder to be available anywhere as a ThreadLocal variable.

Here is how filter authenticates a request and sets result to SecurityContextHolder:

If authentication fails, user is authenticated as anonymously and only URLs with permitAll settings in WebSecurityConfig are available.

Injecting Account into Controller

It is very useful to inject the current Account directly into Controller’s method. Spring Security supports injecting the current Principal general instance, but it is a bit ugly when you want to get the Account object from it:

Much more elegant is defining the custom annotation that directly provides the Account object:

To do this, you need define a custom annotation:

and register HandlerMethodArgumentResolver by ActiveAccountConfigurerAdapter::

Summary

This article wraps up how we have integrated Stompath OAuth 2.0 authentication into Spring Boot backend application. Thanks to outsourcing all the hard work to Stormpath, it only required configuring Spring Security and calling a few Stormpath SDK methods. The authentication logic is encapsulated into the Authentication Filter and resource access rules are configured as a standard HttpSecurity object in WebSecurityConfig.

Hopefully, you can now imagine how to write a custom resource for getting or updating an Account or creating a user request for password reset. It is again only about injecting the Stormpath Application instance and calling the SDK method. You can read complete SDK documentation here: http://docs.stormpath.com/java/apidocs/