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.
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.
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.
Both terms will come to mind when talking about securing applications. And yet, there might be people that confuse the meaning of these terms.
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.
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:
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.
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.
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.
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.
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.
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.
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.
- 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.
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.