Continuous Transformation Blog

Authorization and Authentication with Microservices | LeanIX

Written by Ruth Reinicke | June 14, 2017

Logging in to a website is so easy: I enter my username and password and can use the site. That's it.

What seems so simple is actually simple if the site is based on a monolithic application. But what if a website is powered by multiple microservices? How do the microservices know that a user is who s/he claims to be, and how can this be handled efficiently? The use of JSON Web Tokens can provide a secure and high-performance solution here.According to a survey by German IT industry association BITKOM, 37 percent of Internet users use between eleven and twenty online services that have login functionalities.

To the user, the process of logging in to the frontend always appears to be the same. Username or e-mail, password, press enter, done. In the backend, things look a little different. With a monolithic backend environment the authorization system is implicit, and user verification is no problem. The application can handle user tracking here.

 

 

According to a LeanIX study, companies are however increasingly using microservices, and in these environments things get a little more difficult. Here the microservices either have to rely on the user being authorized or ask the authorization service with every call whether the user is actually allowed to access the service.  

The latter results in a highly chained microservices construct, as every microservice request triggers a request to the authorization service. If that service fails or there are lags in the network, this bad design means the effects are apparent everywhere.

Auth calls within a monolithic infrastructure are no problem; with microservices, latency and system failure are added risks.

What you need here is a solution that allows reliable authorization checking without additional calls.

The solution: JWT header, payload & signature

This is where JSON Web Tokens, JWT for short, can be the answer. JWTs consist of three parts: header, payload and signature. The JWT header contains the token type and the hashing algorithm used. It is Base64-encoded, as is the payload, which contains the authorization information for the user and typically the user's rights and name. It is moreover useful to include an expiry period in the payload.

The signature is required to verify the authenticity of the token. It consists of the encoded header, the payload and the secret key, which is the authorization service's signature making it possible to check the token's authenticity.

Pure oAuth2: tokens must be verified against resource server

 

oAuth2 with JWTs: tokens can be verified locally with a public key

As other microservices can verify the token based on the signature, there are almost no further calls to the authorization server after login. The token can be signed with a "private/public key" method; other microservices then only have to contain the code for checking the signature and know the public key.

As the token is sent with the authorization header as a bearer token, it can be evaluated by the microservices. Thanks to the signature there are no restrictions regarding URLs; this means that cross-site authorization is also possible, which in turn supports single sign-on (SSO) and is of great interest to users.

The JWT flow in detail

What does the JWT flow look like in detail? First a user has to log in to the authorization service with his/her login data. This can be done either via SSO or standard login. The authorization service now checks the login data and creates a new JWT.  In Java, the code for creating a JWT, including signing it with the RS256 algorithm, could be as follows:

public String createJwt(User loggedInUser) {

  JwtBuilder builder = Jwts.builder()

    .setSubject(loggedInUser.getUsername())

    .claim(„payload“, loggedInUser.getPayload())

    .setId(loggedInUser.getId())

    .setExpiration(calculateExpirationTime());

 

  return builder.signWith(

      SignatureAlgorithm.RS256, privateKey

    )

    .compact();

}

The name of the user is set as the subject; the claim method sets the user's payload for the authorization information. This is followed by a unique ID by means of which the token can be identified at any time. Finally, an expiry date is set. The token is signed with the private key and serialized into a compact string before it is returned. All the JWT methods used here can be found in the io.jsonwebtoken package, which can easily be included in your Java project. Libraries exist for almost all standard programming languages, so integration is fairly easy.

Now the user can send the compact token seen in the following listing in the browser's authorization header in order to authenticate him-/herself to the website. The individual parts of the JWT are separated by periods (Header.Payload.Signature):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

On the microservices side the token's signature has to be checked to see whether it is valid. The expiry date should of course also be checked. In Java this is a single line of code:

Claims claims = Jwts.parser()

.setSigningKey(publicKey)

.parseClaimsJws(accesTokenString)

.getBody();

The parser verifies that the token is in the correct format and calls the parseClaimsJws method to check whether the token is correctly signed and still valid.

A token was thus successfully issued, and that it has not yet expired was verified when it was read out. The microservice can now assume that the user is legitimate and consider him or her trustworthy.

A visualized AuthFlow between different microservices (blue) and the UI (gray)

Now that we have shown how easily a JWT can be created and read out, we should also explain a few conditions that must be met. The size of the payload in a token should not exceed a certain length in order to avoid truncation by the browser or server (Apache, for example, has a standard limit of 8 kB here). Tokens should also have an expiry date, as otherwise, it becomes almost impossible to revoke user permissions. Finally, tokens should not contain security-critical information, such as passwords, as this information would merely be Base64-encoded and could thus be decoded and read out by any user.

Alternatives: SAML too bulky & SWT too vulnerable

JWTs are not the only way to authenticate users in web and microservices contexts. Alternatively, you can use Security Assertion Markup Language (SAML). SAML, however, is an XML format and therefore requires a significantly greater transfer of data, which is undesirable especially on mobile end devices. On the other hand, SAML can operate with asymmetric signature methods such as "private/public key pair." Its digital signature syntax however makes XML very complex and significantly harder to convert into objects than JSON. A further option would be Simple Web Tokens (SWT). These, however, can only be checked with symmetric signing methods, which increases their vulnerability to hacking attacks because as soon as one microservice has been hacked, the secret key is known and the entire system compromised. With JWTs, only the authorization service knows the secret key—and this one service is easier to protect than a multitude of services.

JSON Web Tokens: overwhelming advantages

Compared to other options, JWTs have more advantages than disadvantages: they are easily integrated into many programming languages, provide reliable authentication while reducing network overheads, and can be used for single sign-on and login authorization. In addition, JWTs have a significantly smaller footprint comp

ared to SAML applications. JSON Web Tokens thus appear to be the most appropriate option for user authentication in microservices environments.

More information on JSON Web Tokens can be found at http://jwt.io and in RFC 7519.