Page cover

πŸ”Appendix C

JWT Authentication in a Microservice Architecture


Introduction

In modern applications, especially those built on a microservices architecture, services need to communicate with each other securely. A common challenge is handling user authentication and authorization across these distributed services. Sharing a single secret key (as used with the HS256 algorithm) among all services is a security risk; if one service is compromised, the entire system's secret is exposed.

A more secure and scalable solution is to use an asymmetric key pair (RS256). In this pattern, a dedicated Authentication Service signs JWTs with a private key, while multiple Resource Services verify those tokens using a corresponding public key.

This appendix provides a step-by-step guide to implementing this pattern using two FastAPI applications.

  • auth_service: The central authority that validates user credentials and issues JWTs. It is the only service that holds the private key.

  • course_service: A resource service that protects its endpoints and provides data only to requests with a valid JWT. It only needs the public key to verify tokens.


Core Concept: Asymmetric Keys (RS256)

Unlike symmetric algorithms (like HS256) that use one secret key for both signing and verifying, asymmetric algorithms use a pair of keys:

  • Private Key (.pem) πŸ”‘: Kept secret and known only to the auth_service. It is used to sign (create) the JWT. Think of it as the unique, personal signature of the issuer.

  • Public Key (.pub) πŸ”“: Can be shared freely with any number of resource services. It is used to verify the signature of a JWT. Anyone with the public key can confirm that the token was signed by the holder of the private key, but they cannot create new valid tokens themselves.

This separation is the key to the pattern's security and scalability.


Implementation Walkthrough

Step 1: Generate the RSA Key Pair

First, you need to generate the private and public keys. You can do this using the openssl command-line tool.

Generate a 2048-bit RSA Private Key:

Extract the Public Key from the Private Key:

You will now have two files in your directory: private_key.pem and public_key.pem.

Step 2: Build the Authentication Service

Create a file named auth_service.py. This service will issue tokens signed with the private key.

Step 3: Build the Resource Service

Create a second file named course_service.py. This service will use the public key to validate tokens and protect its data.

Step 4: Run and Test the Services

Open two separate terminal windows in your project directory.

1/ Start the Authentication Service on port 8081:

2/ Start the Course Service on port 8082:

3/ Test the Flow:

  • Navigate to the auth_service docs at http://127.0.0.1:8081/docs.

  • Use the /token endpoint with username: "student1" and password: "password123" to get an access token.

  • Copy the access_token string.

  • Navigate to the course_service docs at http://127.0.0.1:8082/docs.

  • Click the "Authorize" button, paste the token into the value field, and authorize.

  • Now, execute the /courses endpoint. You should successfully receive the list of courses.


Architectural Benefits of This Pattern

  • No Shared Secrets: The highly sensitive private key is isolated within the auth_service. Resource services don't need it, minimizing the attack surface.

  • Decentralized Verification: Any number of microservices can be given the public key to verify tokens independently. This allows them to protect their endpoints without needing to call the auth_service for every request, which is highly efficient and scalable.

  • Improved Security Posture: If a course_service instance is compromised, attackers cannot issue new, valid tokens because they do not have the private key. The scope of the breach is limited.

Last updated