DEV Community

Cover image for Authentication & Authorization in Microservices Architecture - Part I
Tzachi Strugo for Behalf Inc.

Posted on

Authentication & Authorization in Microservices Architecture - Part I

About Behalf

Behalf facilitates in-purchase financing for B2B buyers and sellers. As a financial institution, it is critical to maintain the customer's trust by ensuring that only qualified customers can access their Behalf Account.

Background

Behalf is based on microservices architecture, meaning that each service is loosely coupled and has its own enclosed, well-defined bounded context.
Moving from Monolith to microservice architecture world have a lot of advantages:

  • Working with small components creates room to scale the service in separate parts.
  • Each microservice has its own autonomy & provides flexibility on the technology that will be used.
  • Productivity & velocity can increase by allowing different development teams to work on various components simultaneously without impacting each other.
  • Development teams are focused and organized around the business functionality.
  • Developers have the freedom to work more independently and autonomously without having a dependency on other teams.

Alt Text

However, as engineers and architects, we face security challenges in a distributed architecture. Microservices expose endpoints to the public audience, which are usually referred to as APIs.

  • A monolith needs to secure only itself, and that's manageable. Microservices have a bigger attack surface, which means a larger number of services comes with more significant risks. Each one needs to take care of all the weaknesses that might be exposed.
  • In a monolithic architecture, components invoke one another via a method call. In contrast, microservices may expose internal API's (synchronous calls) to communicate with each other. This requires more effort and attention to secure it.
  • In a monolith, all internal components share the same user session context. In a microservices architecture, nothing is shared between them, so sharing user context is harder and must be explicitly handled from one microservice to another.

According to the above security challenges, we conclude that a microservice's security needs to be tackled differently from the monolith’s.

This article will run you through the challenges and decisions to implement flexible, secure, and efficient authentication and authorization layers in a microservice architecture.

The Difference between Authentication and Authorization

Both terms will come to mind when talking about securing applications. And yet, there might be people that confuse the meaning of these terms.

Alt Text

In the authentication process, the identity of the user is checked to provide access to the system. This process verifies ‘who you are?’ so the user needs to supply login details to authenticate.
Authorization is the process of verifying if the authenticated user is authorized to access specific information or be allowed to execute a certain operation. This process determines which permissions the user has.

Authentication Strategy in a Microservice Architecture

While moving from monolith to microservices architecture, it is important to manage security and access control by understanding how to implement authentication and authorization in the microservices world.
There are several approaches:

Authentication & Authorization on each service

Each microservice needs to implement its own independent security and enforce it on each entry-point. This approach gives the microservice team autonomy to decide how to implement their security solution. However, there are several downsides about this approach:

  • The security logic needs to be implemented repeatedly in each microservice. This causes code duplication between the services.
  • It distracts the development team from focusing on their domain main service.
  • Each microservice depends on user authentication data, which it doesn't own.
  • It’s hard to maintain and monitor.
  • Authentication should be a global solution and handle as a cross-cutting concern.

One option to refine this solution would be to use a shared authentication library loaded on each microservice. This will prevent code duplication, and the development team will focus only on their business domain. However, there are still downsides that this refinement can’t solve.

Alt Text

Global Authentication & Authorization Service

In this strategy, a dedicated microservice will handle authentication and authorization concerns. Each business service must authenticate the request before processing it by downstreaming it to the authentication service. However, there are several downsides about this approach:

  • The authorization check is a business concern. What specific user roles are allowed to do on the service is governed by business rules. Therefore, the authorization concern should not be handled in the global authentication service.
  • This strategy increases the latency of processing requests.

Alt Text

Global Authentication (API Gateway) and authorization per service

When moving to a microservice architecture, one of the questions that need to be answered is how an application’s clients communicate with the microservices. One approach would be to use direct access between client and microservice. This approach suffers from a strong coupling between clients and microservices.

The API gateway is a single endpoint entry for all requests. It provides flexibility by acting as a central interface for clients using these microservices. Instead of having access to multiple services, a client sends a request to the API gateway responsible for routing it to the downstream service.

Alt Text

Because the API gateway is a single endpoint entry, it is an excellent candidate to enforce authentication concerns. It reduces the latency (call to Authentication service) and ensures the authentication process is consistent across the application. After successful authentication, the security component will enrich the request with the user/security context (identity details on the login user) and route the request to a downstream service that enforces the authorization check.

Authentication Types: Stateful vs. Stateless

In Stateful authentication, the server creates a session for the user after successfully authenticating. The session id is then stored as a cookie in the user's browser and the user session store in the cache or database. When the client tries to access the server with a given session id, the server attempts to load the user session context for the session store, checks if the session is valid, and decides if the client has to access the desired resource or rejects the request.

Stateless authentication stores the user session on the client-side. A cryptographic algorithm signs the user session to ensure the session data’s integrity and authority.
Each time the client requests a resource from the server, the server is responsible for verifying the token claims sent as a cookie.

Since the user session is stored on the client-side, this approach frees the overhead to maintain the user session state, and scaling doesn’t require additional effort.

Brief Introduction to JSON Web Token (JWT)

A JWT is an open standard (RFC-7519) that defines a mechanism for securely transmitting information between two parties. The JWT token is a signed JSON object that contains a list of claims which allow the receiver to validate the sender's identity.

The purpose of using the JWT token is for a stateless authentication mechanism. Stateless authentication stores the user session on the client-side.

Alt Text

JWT Structure

The JSON Web token is composed of three parts separated by periods.

  • The header contains the algorithm used for signing.
  • The payload is the session data that also refers to ‘claims’. There are two types of claims:

    • The JWT specifications define reserved claims that are recommended to use while generating the JWT token.
    • Custom claims
  • The signature is the most critical part. The signature is calculated by encoding the header and the payload using Base64 encoded. Then the encode64 is signed using a secret key and cryptographic algorithms specified in the header section. The signature is used to verify the token has not changed or modified.

Alt Text

JWT Best Practices and Pitfalls

  • Always use the HTTPS protocol to offer better protection. This way, all data sent between the client browser and a server are encrypted.
  • Keep the token size as small as possible. The JWT can be either a signed token by using JSON Web Signature (JWS) or a more secure level of protection by using JSON Web Encryption (JWE). Either way, as a rule of thumb, the token should not contain sensitive data.
  • Several attacks rely on ambiguity in the API of certain JWT libraries. Make sure the JWT library is protected against it by validating the algorithm name explicitly.
  • Make sure to use a strong secret key as long as the length of the hash algorithm.
  • Set the JWT token to a short period to reduce the probability of it being used maliciously.
  • The JWT is automatically valid until it expires. If an attacker gets the token, the only way to “kill” the session is by a stateful solution that explicitly detects and rejects those tokens.
  • Using JWT does not prevent CSRF attacks. A Cross-site request forgery, CSRF, is a web security vulnerability that allows the attacker to perform actions that they are not minted to perform. Use a synchronizer token pattern to prevent it.
  • For additional JWT Best practices, read The JSON Web Token Current Best Practices.

Conclusion

Authentication and Authorization are critical core components for applications. This article explains what needs to be considered while building a clean and robust authentication solution under microservice architecture.
In the next article, we are going to explain our implementation to achieve this goal.

Top comments (27)

Collapse
 
wparad profile image
Warren Parad

Great writeup, it's amazing how many teams still try to couple authentication to authorization. But you got that exactly right to keep these separate.

I'm also wondering about the implementation, these are usually really challenging. I've found from many past experiences that building the user IAM aspects to allows encounter some pitfalls. I actually wrote up my most recent adventure into building authz in our services.

Collapse
 
tzachis profile image
Tzachi Strugo

Thanks, Warren
Implementing and enforcing IAM isn't a trivial task and can be challenging in a microservice architecture.
We think that choosing the correct access management control depends on the application requirements and can be changed from one to another.

In the next part, we will see the implementations that feet for our needs.

I am 100% identify with your sentence: ״I've been through this journey myself, more than once. Every time, I was looking back...״

As humans, we have the nature to solve problems; Looking back and asking ourselves the questions, did we achieve our goals? What can we do otherwise? or did we choose the correct solution for that particular problem?

This step is critical while solving problems that keeping us going and improve our skills as software engineers

Collapse
 
tracker1 profile image
Michael J. Ryan

I generally include a handful of high level roles in service and application design. May also want groups. These can be included in the jwt. Authorization by ownership of of course separate.

Collapse
 
wparad profile image
Warren Parad

That works with small systems, but invariably JWTs are not designed to handle resource management. There just was never a way to support granular permissions access stored in the JWT that works at scale.

Thread Thread
 
tracker1 profile image
Michael J. Ryan

I mostly agree, you still may need fine grained permissions. But you can go a long way with roles and groups for most things.

Document ownership. Things like owner read/write, group read, manager read/write and even manager above are typical oversights.

I'm just pointing out that for many applications, rules and groups fit well enough.

Collapse
 
jorgesivil profile image
JorgeSivil

Thanks for the article!

I was thinking in that it is OK to put roles/permissions in the JWT payload, however it could become very very large, and we have to account for header limits: stackoverflow.com/questions/686217...

Even if we have low-level permissions Posts.Manage.UpdateOwn, and we can send it in the JWT payload, the data could be very large as more permissions are created, but most importantly, the Posts microservice should be able to check the business rule regarding Posts.Manage.UpdateOwn.

So we have two options here, given a request PUT /users/profile

1) In the controller, you get the User and the Post object and send them to /authorize/Posts.Manage.UpdateOwn with the Post and User object's serialized, and then you check that Post.ownerId === User.id and return true or

2) In the Posts microservice you do the following check: permissions.includes('Posts.Manage.UpdateOwn') && Post.ownerId === User.id

I think that the second one makes more sense.

Collapse
 
fandiks32 profile image
irfan

@jorgesivil How did you handle header limits?

Collapse
 
jorgesivil profile image
JorgeSivil

By setting only the necessary information (like userid) and then having an endpoint to retrieve the full list of permissions

Collapse
 
honatas profile image
Jonatas de Moraes Junior

Nice. Separating authentication from authorization is a big step. However, using JWT may not be the best approach, and I can see you already started feeling the pain given the rather big pitfalls section of your article.

I hereby invite you to take a look at this article where I describe the huge bunch of problems you will face while using JWT, and propose a more straightforward solution:

dev.to/honatas/a-different-approac...

I'm open for discussion. =)

Collapse
 
tzachis profile image
Tzachi Strugo

Hi Johatas,
Thank you for reading and sharing your thoughts. Understanding the separation of concern is critical to solving the problem of Authentication & Authorization solutions.

Indeed, stateless authentication(JWT in our case) suffers from several downsides that need to be aware of. There are pros and cons for both stateless & stateful concepts. For some use cases, the stateless approach isn't the best one.

With your permission, I would like to hold this discussion until I will publish part II :-)

Collapse
 
mnf profile image
Michael Freidgeim

What will be in part II and when it will be published?

Collapse
 
jennyfive profile image
Jen

what happened to the next article? :)

Collapse
 
alimobasheri profile image
MirAli Mobasheri

Thanks! That's a useful article, and I really enjoyed reading it. I have no experience with microservices on a large scale, but I can easily understand the concepts you have described. Using a single endpoint for authentication, although being difficult, looks like a valid and clean way of getting things in a flow.
Waiting for the next part!

Collapse
 
tracker1 profile image
Michael J. Ryan

Would add that you can use asymmetric rsa signing. This is generally safer than a shared secret. I'm fact the authority generating the jwt can share it's public key publicly.

Other considerations are revocation and renewal.

Collapse
 
jamesmuldrow profile image
James

Great write up, I've been searching for an article that breaks down authentication and authorization in a microservices environment. This article definitely helped me out. Looking forward to part II! When do you expect to release it?

Collapse
 
zvermafia profile image
Mokhirjon Naimov

I'm waiting for the second part of this article, and it will be better if you share the date when you'd like to write that part...

Collapse
 
kevinsalimi profile image
Kevin Salimi

You've really good head in describing things. Awesome!

Collapse
 
caojs profile image
caojs

Great post, seperating authentication and authorization is a right approach in microservices. What do you think if I add RBAC with domains/tenants in authentication? Example:

[PUT] /domainA/user/profile

  1. Login to authentication service and get JWT token which contains identity and user's roles in domainA
  2. Send JWT to domainA service, roles are checked in here.
Collapse
 
mehdicharife profile image
Mehdi Charife

In the example, would domainA service check for the validity of the token?