Pairing AngularJS and Java EE 7 for authentication

In this post I will illustrate how to pair your AngularJS SPA (single-page application) with a Java EE backend providing a JAX-RS 2.0 API. Especially the token based authentication part is described in detail. My intention is a more general approach showing the method I use. I will not discuss the whole code, there are many tutorials out there doing a great job covering AngularJS basics.

Infrastructure

In this setup a nginx webserver runs on port 80, providing the AngularJS SPA and static content. Further, nginx acts as a proxy to connect to a Java EE 7 app deployed to a WildFly running on port 8080. Infrastructure

This way, the setup is optimzed to serve the static content by a high performace webserver and a dedicated application server to work in the background. Furthermore, it’s easy to change the backend technology without even thouching the frontend.

Natively the backend is available at http://localhost:8080/rebackend/resource/.  My goal is to make the provided API available at http://localhost/api/. This way CORS issues are prevented, because every resource will be available under the same port and host at http://localhost/*.

The following part shows the nginx configuration in the file nginx.conf. Starting from line 6, you see the proxy setup for /api/. The parameter  proxy_pass  points to the context root of the REST API. Line 14 is the webroot and default name for the index file is provided.

Application architecture and authentication

If the AngularJS application interacts with a RESTful service and, in particular, authenticates against it, some authentication information has to be added to each request.

Frontend

Ok, let’s start with the login process. The user enters username and password in the frontend and submits the login form. In AngularJS, a Login Controller handles this action.

I use the array notation to define the dependencies AngularJS will automatically inject, otherwise the minification process will cause strange behaviour. In any case, there are grunt plugins taking care of this.

All the authentication logic is implemented in the authFactory . During the login process, the credentials are send to the backend. In case of a successful login, AngularJS will store the authentication data in the factory too.

The  authFactory  posts the user credentials to the REST backend via a post request.

Backend

To make life easier, the request data is automatically wrapped by an AuthLoginElement containing the login credentials wich the frontend sends to the API.

The login request is handled by the REST resource  AuthResource  available at /auth/. The resource produces and consumes JSON, as this is the easiest to work with in the frontend. Access to /auth/login  is permitted to all by using the annotation @PermitAll.

The credentials are processed by the AuthService. In this case username and password are checked against the database in this example. If the check is successful, a token is generated and stored with the user profile (see lines 6, 8-9).

The response, generated by AuthResource.login(), will include the username, generated token and role. Again, the data is wrapped by an object, in this case the AuthAccessElement to be returned.

The role returned by the backend can be used to check the authorization of the user to see pages or to take actions in the AngularJS App.

Frontend

As mentioned before, in case of a successful login  LoginCtrl.login  stores the username, token and role to the  authFactory  by authFactory.setAuthData(data)  (see LoginCtrl above in line 4).

The following snippet shows the essential part of the authFactory handling the authentication data after the login. authFactory.setAuthData() saves the parameter to authData.

Knowing authFactory.authData is set in case of a successful login, it’s easy to check if the user is logged in or not by authFactory.isAuthenticated().

Ok, so far, so good, we are logged in. But, how do we authenticate at every single REST request we send to a restricted service? To solve this problem, in AngularJS it is possible to add so called HTTP interceptors. These interceptors will be called on every defined http event and allow manipulating the requests and reponses. Using this mechanism, we will add our custom information to the request. The following block is such a interceptor. In this case it will be called on every request the Application sends (line 3).

So, in case we are logged in (remember authFactory.isAuthenticated()) we have to manipulate the request data (lines 6-7) by adding the username and token as auth-id and auth-token to the request.

The following snippet adds the previously defined interceptor  authHttpRequestInterceptor to the chain.

Ok, so we are done on the frontend part.

Backend

Now, the ingoing request has to be checked for the payload added earlier. This can be done by implementing  ContainerRequestFilter  to intercept the request. All interceptors and filters registered with the @Provider annotation are globally enabled for all resources. At deployment time, the server scans the deployment units for  @Provider  annotations and automatically registers all extensions before the activation of the application.

First, a proper response and message is defined in case the authentication check fails (line 5). In AuthSecurityInterceptor.filter() the username and token is gathered from the request (line 22-23). After that, the method invoked is detected and wether the method is annoted with @RolesAllowed. If this is the case, the user needs to be logged in and having the rigth role.

The authorization check is performed by  AuthServiceBean.isAuthorized(). Firstly, it is verified that the user is logged in. Afterwards the roles allowed for the called function are tested against the user roles (lines 9-11). Imagine, for example, an annotation like @RolesAllowed({"ADMIN"}).

In case the authorization check fails the request will be aborted by  requestContext.abortWith(ACCESS_UNAUTHORIZED);  (line 30 of AuthSecurityInterceptor). Otherwise, the request will be processed as intended.

Conclusion

As you can see, it is pretty straightforward to pair an AngularJS application with a Java EE REST Api. This applies also to the implementation of a token based authentication mechanism.

Search