JSON Web Tokens are made for Microservices

Modern applications that follow microservices architecture typically are compositions of a set of microservices. At some point application may need some sort of authentication and authorization logic. Let's imagine some client component, that communicates with set of microservices where interactions need to be authenticated and authorized.

By reference tokens

Typically authentication and authorization logic utilizes tokens. That token are generated by some AuthService can authenticate a user, let's say by his username and password. Conceivable this service is able to authorize particular user by matching rights & roles data.

Well know protocols utilizing token by reference validation are kerberos, cas and OAuth. Not going to protocol details here, we can summarize, that some Authorization Service will give you at least one token, that has to be used for every interaction with protected service...

Like shown in the diagram ones token is acquired form AuthService it is used in all service calls, and service need to talk to AuthService again just to validate the token. This also means that token is only a reference to the user-data and the whole data set behind it. The service is able to fetch everything it needed using that token reference.

In the age of monoliths1 and statefull backend workers this approach had more advantages than disadvantages. The protected service could ask AuthServive to validate the token on every request.

Recalling that in case o microservices architecture services are tending to be stateless (state is inhibiting, think of REST). Also if services are tending to be small and simple, the complexity is not completely gone, it just moved out of services to the level of interactions and communication between the services and we need to be very careful here. Imagine 10 services as backend of one Single Page Applications that need to talk to one authoritative service just to verify tokens on every request.
Utilization of by reference tokens protocols become less favorably because:

  • AuthService become single point of failure, need to be always online (Even several seconds of downtime means complete outage.
  • Every microservice has to know where to find the AuthService and how to talk to it (adds code + complexity).
  • Increases communication complexity
  • By that increases complexity if infrastructure code (firewalls, properties with service names, more environment dependent parts)
  • Makes deployment complex
  • Makes development and tests complex (more mocking needed potentially)
  • Don't scale well with increasing number of services

JSON Web tokens (by value)

So we need something like token by value, tokens that contain and not reference all needed information (at least for authentication & authorization) and could be validated in place, by the called microservice self, but also protected by cryptography in untrusted environment.
This is exactly what JSON Web Tokens (JWT) are here to solve and this is just genius.

Utilizing crypthographic function you can encrypt some portion of information including validity period, sign this information and present it in the form of a token - JWT. To encrypt and decrypt the token you can use one shared key2 or public & private keys. And since the tokens could be validated in place it reduce a lot of communication to the AuthService, potentially influencing the design of AuthService itself, by reducing it's complexity drastically.

JWT code example

Maybe code example will undermine previous advisement. I will utilize the jwtk/jjwt java library. And to make it work we need this dependency.

<dependency>  
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.7.0</version>
</dependency>  

Here is example code that generates JWT token

public String createToken(long userId, String userEmail, long ttlMillis) {  
//Get current timestamp
long nowMillis = System.currentTimeMillis();  
// Let's set the JWT Claims
JwtBuilder builder = Jwts.builder().setIssuedAt(new Date(nowMillis)).setSubject(String.valueOf(userId))  
  .setIssuer("MyAuthoritativeService")
  .claim("email", userEmail)
  .signWith(SignatureAlgorithm.HS256, jwtKey)
  .setExpiration(getExpDate(nowMillis, ttlMillis));
// Builds the JWT and serializes it to a compact, URL-safe string
return builder.compact();  
}

Here token is generated for userId and userEmail that are "backed in" as two claims. The getExpDate(nowMillis, ttlMillis) method produces expiration Date. Here jwtKey is used as pre-shared key. The resulting token may look like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZW1haWwiOiJteXVzZXJuYW1lQGdtYWlsLmNvbSIsInJvbGUiOiJtYXN0ZXIifQ.Le-mu-DstBdPhSoaeaN9Rl_TlIe05NLpsy2vs_q8c74  

Let's look at the validation code part:

Jws<Claims> claims = Jwts.parser().setSigningKey(jwtKey).parseClaimsJws(token);  
String userId = claims.getBody().getSubject();//Subject is user ID in this example.  
Object email = claims.getBody().get("email");  

Here token is parsed by use of the Key (jwtKey) and two backend in claims are extracted from the body. Of course different exception may occur on parsing.
Token could be just invalid and signature violated or token can be already expired of course you should react on that exceptions.

More JWT details

JWT is standardised by RFC7519. As you maybe recongnized, by example token, JWT consist of 3 parts:

  • JOSE Header: JSON object containing the parameters describing the cryptographic operations and parameters employed. The JOSE (JSON Object Signing and Encryption)
  • JWS Payload: The sequence of octets to be secured -- a.k.a. the message. The payload can contain an arbitrary sequence of octets
  • JWS Signature: Digital signature or MAC over the JWS Protected Header and the JWS Payload

I hope you like it.
I will propose two token solution with JWT next time.

  1. Mentioning "Monoliths age" i mean the past. Maybe that term is to harsh.

  2. See also Symmetric Key Cryptography