Unsure how to share authentication state between stateless microservices? This post will try to answer these questions using Spring Boot, Spring Security (OAuth2) and JSON Web Tokens (JWT).
OAuth
OAuth defines a standard contract of providing token based authentication and authorization on the internet.
It defines a protocol for notifying a resource provider ( Facebook ) that the resource owner ( you ) grants access to their information ( e.g your email ) to a third-party application ( e.g a Web App )
The OAuth standard defines several Grant Flows, this post will focus on the Authorization Code Grant. The flow is well suited to traditional web applications that has server side session storage. Modern single page javascript applications will be better suited to the Implicit Grant, which is not covered here.
Authorization Code Grant
The diagram depicts the interaction between the 3 main components of the system, Auth Server, Web App and a Microservice. A JWT token encapsulates the identity of the authenticated user and is only passed between the system components, never to the browser. The token is stored in the HTTP Session of the Web App which is maintained through a traditional session id Cookie.
Strictly speaking the Authorization Code Grant flow is complete after the secret_code
has been exchanged for a JWT. The rest of the diagram shows how the Web App proxies requests to the Microservice and passes the JWT along.
The optional "authorize" step is useful in cases where the Resource Provider (Facebook) cannot vouch for the credibility of the 3rd party application requesting access, thereby delegating the decision to the Resource Owner (the User). In some cases, it also provides an opportunity to the Resource Owner to review what information is requested by the application.
JWT
JWT is an open standard for securely sending information (Claims) between parties. The information can be verified and trusted because it is digitally signed, using a public / private key pair in this instance.
JWT consists of 3 parts separated by a .
, the header, payload and the signature.
The payload is a combination of:
- Registered Claims - keys reserved by the JWT Spec.
- Public Claims which can be defined by the application.
Below is a rough example of how JWT is composed:
Sample System Architecture
Auth Server
The Auth Server will provide authentication and play the role of the Resource Provider. A Java keystore (Public + Private keys) is packaged into the server and is used to sign the JWT.
The approval step is skipped since interaction is between trusted applications.
Generate the keystore in auth-server/src/main/resources
:
$ keytool -genkeypair -alias jwt -keyalg RSA -dname "CN=jwt, L=Brisbane, S=Brisbane, C=AU" -keypass mySecretKey -keystore jwt.jks -storepass mySecretKey
The Spring context configuration for the Auth Server consists of two parts, the WebSecurityConfig
and OAuth2Configuration
.
WebSecurityConfig
configures a basic form based login page. Furthermore it secures all OAuth endpoints exposed by the Auth Server. For simplicity's sake an in memory store of users is also included.
OAuth2Configuration
configures the required OAuth endpoints for the Authorization Code Grant flow. Additionally it takes care of setting up the JWT token store and the key pair used to sign the tokens.
Web App
The Stateful Web App hosts the view (HTML,JS and CSS) of the application. The default, in memory, implementation of HTTP Session is used to store JWT Tokens.
As with the Microservice, this app contains a shared public key to verify the signature on the JWT.
The role of an API Gateway is provided by a ZUUL Proxy and has a dual purpose:
- Automatically attach the JWT to the
authorization
header before proxying an API request - Prevent the need for CORS configuration between the browser and the Microservice.
Both the ZUUL Proxy and the public key can be configured in src/main/resources/application.yml
To extract the public key from the keystore created for the Auth Server (keystore password is 'mySecretKey'):
$ keytool -list -rfc --keystore boot-auth-server/src/main/resources/jwt.jks | openssl x509 -inform pem -pubkey
and the Spring configuration:
Microservice
The Microservice is a stateless application that exposes a single API endpoint. Invoking the endpoint will produce JSON with a random UUID and the name of the current authenticated user. This is automatically extracted from the JWT in the authorization
header.
The Microservice also demonstrates authorization, by only allowing users with the ROLE_READER
authority to access the resource.
Spring configuration:
Running the Sample Application
All the code for the sample application is hosted on GitHub and can be run using gradle.
$ git clone git@github.com:monkey-codes/spring-boot-authentication.git
$ cd spring-boot-authentication
$ ./gradlew bootRun --parallel
Web App starts at http://localhost:8080:/web-app
References
http://stytex.de/blog/2016/02/01/spring-cloud-security-with-oauth2/
https://spring.io/guides/tutorials/spring-boot-oauth2/
https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v
https://scotch.io/tutorials/the-anatomy-of-a-json-web-token