JWTs are a surprisingly effective way to authenticate microservices because they shift the burden of trust from a central authority to the individual service requesting authentication.

Imagine a chain of services, each needing to verify the identity of the user or another service making a request. Instead of each service calling back to a central identity provider (IdP) for every single request, a JWT acts like a verified badge. This badge is issued by a trusted IdP, but once issued, it can be independently verified by any service that trusts the IdP’s signature.

Let’s see this in action. Suppose we have an API Gateway, a User Service, and an Order Service.

  1. User Service (acting as IdP): A user authenticates with the User Service. The User Service verifies their credentials against a database. If valid, it generates a JWT.

    {
      "sub": "user123",
      "name": "Alice Smith",
      "iat": 1516239022,
      "exp": 1516242622,
      "iss": "user-service.example.com",
      "aud": "api-gateway.example.com"
    }
    

    This payload is signed by the User Service using a private key. The JWT looks like eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwibmFtZSI6IkFsaWNlIFNtaXRoIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjIsImlzcyI6InVzZXI tc2VydmljZS5leGFtcGxlLmNvbSIsImF1ZCI6ImFwaS1nYXRld2F5LmV4YW1wbGUuY29tIn0.your_signature_here

  2. API Gateway: The user sends a request to the API Gateway, including this JWT in the Authorization: Bearer <token> header. The API Gateway receives the token. It needs to verify the signature. It knows the public key corresponding to the User Service’s private key.

    • Verification: The gateway uses the public key to verify the signature. If it’s valid, the gateway trusts the claims within the token (like sub, iss, aud). It also checks expiration (exp) and issuer (iss) and audience (aud) to ensure it’s meant for the gateway.
    • Forwarding: If the token is valid, the gateway forwards the request (and the JWT) to the Order Service.
  3. Order Service: The Order Service receives the request and the JWT. It also needs to verify the JWT. The Order Service can be configured to trust the User Service’s public key.

    • Verification: The Order Service performs the same signature verification using the User Service’s public key. It checks exp, iss, and aud (which should ideally be order-service.example.com or a broader audience that includes it).
    • Authorization: Once verified, the Order Service can use the sub claim (user123) to determine what actions this user is allowed to perform within the Order Service.

The core problem this solves is distributed trust and reducing latency. Without JWTs, every service would need to call the User Service (or an auth service) to validate each incoming request. This creates a bottleneck and increases the number of network hops. With JWTs, services can verify tokens locally, relying on cryptographic signatures for trust.

The key components are:

  • Header: Contains metadata about the token, like the signing algorithm (alg).
  • Payload (Claims): Contains the actual information, like sub (subject), iss (issuer), aud (audience), exp (expiration time), iat (issued at).
  • Signature: This is crucial. It’s generated by taking the encoded header and payload, signing them with a private key, and then Base64Url encoding the result. The signature allows recipients to verify that the token hasn’t been tampered with and that it was issued by the expected party.

You control the security of this system primarily through key management and claim validation. The issuer (e.g., User Service) must securely store its private key and distribute its public key to all relying parties (API Gateway, Order Service). Relying parties must correctly configure their JWT validation libraries to use the correct public keys, expected issuers, audiences, and algorithms, and to reject expired tokens.

A common pitfall is relying solely on the signature verification without also validating the iss (issuer) and aud (audience) claims. A malicious actor could potentially issue a token with a valid signature that’s intended for a different service or a different issuer, but if your service doesn’t check iss and aud, it might incorrectly trust and process that token. You should always validate that the iss claim matches the expected issuer and the aud claim includes your service’s identifier.

The next challenge you’ll face is managing token revocation, especially if a user’s access needs to be immediately terminated before their JWT expires.

Want structured learning?

Take the full Microservices course →