JSON Web Tokens are made for Microservices

Modern applications of the microservices age are defined by a set of microservices. Still most of the applications need some sort of authentication and authorization mechanism. Let's imagine a client component that communicates with a gorup of microservices, but every interaction need to be authenticated and authorized.

By reference tokens

Typically authentication and authorization logic utilizes tokens. That token are generated by some AuthService. 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 knows protocols utilizing token by reference validation are kerberos, CAS and OAuth. Not going into 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, once acquired token is used in all following service calls. Receiving service need check validity of the token or even query some autorisation details. In a cae of reference tken, every service need to contact AuthService again on ever interaction. 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 probably more advantages than disadvantages.

Recalling that microservices 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 and more services as backend of one Single Page Applications that need to talk to one authoritative service just to verify tokens on every request...
Let me summarize why utilization of by reference tokens protocols become less favorably :

  • 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 calle microservice self, but also protected by cryptography in untrusted environments.

This is exactly what JSON Web Tokens (JWT) are for!

Utilizing crypthographic function you can take a portion of information including validity period and authenticate this information cryptographically to the form of a token - JWT. And since the tokens could be validated in place this token enable us to build architectures that far less coupled to the AuthService and potentially influencing the design of AuthService itself, by reducing it's complexity drastically.

However some complexity remains. There are two cryptographical ways to to authenticate and both require cryptographical keys:

  • You can use one shared key2 utilizing HMAC.
  • Asymmetric RSA and ECDSA algorithms..

JWT crypto details discussion

With shared key you get complexity and risks of shared key distribution.
* First you need to distribute new keys relatively synchronously to all services, or make services robust by checking several keys. Both adds complexity to your micro or macro architecture. * If you have to share key in an environment you can't trust to 100% (technicaly or organisationaly) it becomes security problem. Everyone who has a key can sign tokens.

This is where asymmetric algorithms can help. Symetric algorihms maintain key-pair: public and private, public key is designed to be not a secret, so you can share it with all services that need validate your tokens even with services, you can't trust at all. You just only need to secure your private key and to the issuing service.

The main difference between RSA and ECDSA lies in speed and key size. ECDSA requires smaller keys to achieve the same level of security as RSA. This makes it a great choice for small JWTs.

Keep in mind JWT do not encrypt your data, so do not put enything sensitive into it=. But it's well suited being a token or hold userId (should be not sensitive at all). Hover, nothing prevents you from taking this to the next level: You could also encrypt your JWT token (not covered here)

JWT code example

Maybe code example will undermine previous advisement. Here an example of HMAC based shared key JWT use case.
I this example i relay on jwtk/jjwt java library. Let's start with maven dependencies.

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

and here is code sample 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