Using Stormpath on our Java Backend

In a previous post, I talked about our motivation for picking Stormpath as the user management solution for Distinction.ng. To recap, our app is built using Java on the backend and React on the front-end. In this post, I’ll go through how we got Stormpath working on our Java backend.

We are making use of the Stormpath Java Servlet Plugin, which has an excellent getting started guide. The plugin is mostly configuration driven and comes with reasonable defaults. Most of our application-specific configurations reside in a file called `stormpath.properties`.

Our backend API is RESTful, so we started by figuring out the endpoints we wanted to authenticate, and then simply added lines like these to our `stormpath.properties` file:


stormpath.web.uris./api/exams/** = authc
stormpath.web.uris./api/subjects/** = authc
stormpath.web.uris./api/questions/** = authc

By default, the Stormpath servlet plugin comes with a bunch of preconfigured endpoints, e.g. `/login, /register, /oauth/token` e.t.c. for different user management functions. We wanted our endpoints to be prefixed with `/api`, so we did some renaming in our `stormpath.properties` file:


stormpath.web.logout.uri = /api/logout
stormpath.web.register.uri = /api/register
stormpath.web.accessToken.uri = /api/oauth/token

The property name for the HTTP basic authentication endpoint is `stormpath.web.login.uri` with a default value of `/login`. We prefer to use token authentication.

Our backend API and front-end are located on separate domains, so we needed to specify origins that are allowed to request for access tokens by adding them to the properties file:


stormpath.web.accessToken.origin.authorizer.originUris = http://localhost:3500, https://development-1.distinction.ng

The values above are for local development, and we update them based on environment, i.e. development, staging and production. One way of updating the URIs is programmatically via environment variables, which is also 12-Factor App compliant. Additionally, we needed to setup a CORS filter to handle cross-domain communication between the client and server. If you aren’t familiar with CORS, MDN has an excellent write-up. For that, we created a Jersey filter like the following:

import com.stormpath.sdk.servlet.filter.HttpFilter;
// Imports skipped
public class CORSStormpathFilter extends HttpFilter {

    @Override
    protected void filter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws Exception {
        String origin = request.getHeader("Origin");

        // Ensure the origin is allowed based on the environment
        checkOrigin(origin);

        // Handle CORS preflight
        if (request.getMethod().equalsIgnoreCase("OPTIONS")) {
            response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
            response.setHeader("Access-Control-Allow-Headers",
                    "origin, content-type, accept, authorization, X-xsrf-token");
            response.setHeader("Access-Control-Max-Age", "1209600");
        }
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Origin", origin);

        super.filter(request, response, chain);
    }
}

Next, we needed to ensure HTTP requests to Stormpath endpoints pass through `CORSStormpathFilter`, otherwise they wouldn’t work on the browser because of CORS. Again, we added to the properties file:


# Create URI filter called cors
stormpath.web.filters.cors = com.flexisaf.cbt.filter.CORSStormpathFilter

# Specify cors filter for desired endpoints
stormpath.web.uris./api/logout = cors
stormpath.web.uris./api/register = cors
stormpath.web.uris./api/oauth/token = cors

With that, our backend API was ready to accept user registration, authenticate users, and perform other functions supported by the Stormpath Java Servlet Plugin.

2 replies
  1. Guilherme
    Guilherme says:

    Hi.
    I tried this but I didn’t get success. The error about the cross origin persists. Can you show the implementation of the method checkOrigin() ? When I call super.filter, I need to cast as well.
    Will be better if you can provide the sample on github.
    Cheers.

    Reply

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *