How JWT Authentication Works for Microservices: API Gateway Tutorial
As you build and maintain more applications, your authentication strategy becomes increasingly important. It may also be top of mind for your boss since technology leaders cited "improve application security" as one of their top priorities in this year's Digital Innovation Benchmark.
The Kong Gateway JWT plugin is one strategy for API gateway authentication. JWT simplifies authentication setup, allowing you to focus more on coding and less on security.
API Authentication Is Tough
You know you need a secure front door to your system. If requests don’t have the right credentials, the door should remain locked. If they do have the proper credentials, the entry should be smooth.
But how do you verify that credentials are authentic? And how do you make sure there aren't other ways for those without the right credentials to get into your system?
Let’s walk through those scenarios as I demonstrate how to secure a service (in this case, an API server) with Kong Gateway and its JWT plugin. I'll cover all the steps to set up, configure and test the service — giving you the foundational knowledge needed to implement these tools independently.
Core JSON Web Token Concepts
First, let's cover the core technologies. If you're already familiar with these and just want to get started, feel free to skip ahead by clicking here.
What Is a JWT?
The JSON Web Token (JWT), an api authentication method, format lets two parties exchange secure claims. It's a way of saying, "I am so-and-so, which should give me access to that resource. Here is my access token to prove it."
A JWT has a data payload signed by a trusted party to prevent spoofing. An authorizer verifies that the JWT token is authentic, allowing (or forbidding) access to that resource. Typically, a JWT payload is not encrypted; it’s open for the whole world to read. However, what's critical is the authenticity of a token, which depends on a trusted party signing it.
What Is Kong Gateway?
As more companies move from monolithic systems to microservices, a decoupled front-line API gateway to those services — providing authentication, traffic control, request and response transformation — becomes increasingly crucial. Kong Gateway, which is open source, serves as that thin layer between your users and your upstream microservices.
3 Tips to Supercharge Dev Efficiency: Streamline Operations with Konnect
What Does Kong's JWT API Gateway Plugin Do?
In this approach, the plugin serves as the JWT authorizer. It authenticates the JWT in the HTTP request by verifying that token’s claims and ensuring a trusted party signed it. Then, depending on whether these steps were successful, Kong Gateway routes the upstream service request.
Keep in mind that authentication in this context means validating the user’s credentials. That's the job of the JWT plugin. There’s no way to know how a user got a valid JWT. The system just knows that the user has one and is presenting it for authentication. If the JWT is authentic, you can be confident that the user is who they say.
JWT vs. OAuth 2.0
JWT is a different kind of authentication from OAuth 2.0, where the tokens are often long, random strings without encoded payloads. For OAuth 2.0, an identity provider keeps track of the OAuth 2.0 tokens it hands out on the server. The provider who issued the JWT token is the entity that validates it. For this article, I'll focus on JWT.
Blog Post: 4 Steps to Authorizing Services With the Kong Gateway OAuth2 Plugin
JWT vs. OpenID
A separate but related authentication protocol is OpenID, which allows for authentication at one service to be performed by a third-party identity provider service. Kong's OpenID Connect plugin would help facilitate this kind of interaction.
Blog Post:Getting Started With Kong's OpenID Connect Plugin
Basic JWT and API Gateway Use Case
In this basic use case, I have a login server that accepts login attempts with a user's email and password. If the email/password checks out, the server generates and signs a JWT and hands it back to the user.
With JWT in hand, the user tries to access our microservice: a simple API server with a single endpoint. Kong Gateway sits in front of your API server, using the JWT plugin for authentication. The user presents his JWT with his request.
First, the plugin verifies the token's authenticity. Next, it confirms the installation steps of the claims inside the payload. A common claim used is an expiration timestamp for the access token. It's essentially saying, "This token is valid until this date and time." So, the plugin will check the token’s expiration date.
If the JWT passes all the necessary checks, Kong Gateway grants access to the requested server endpoint. Otherwise, it responds with 401 Unauthorized.
The approach to JWT authentication is quite simple:
- Set up a basic Node.js Express server with a single endpoint.
- Set up Kong Gateway as an API gateway to your server.
- Enable the JWT plugin to protect your server endpoint with JWT authentication.
Lastly, I'll cover advanced use cases for the plugin. Ready to dive in to our JWT Authentication Tutorial? Here we go!
Want to set up key authentication for your API gateway with clicks instead of code? Try Konnect for free >>
1. Set Up a Node.js Express Server and Endpoint
On your local machine, create a folder for your project. Then, initialize a new Node.js project. In the following examples, I'll use yarn, but you could use npm too:
Next, add Express to your project:
You can use your browser to test this new endpoint by visiting http://localhost:3000.
Your API server endpoint should be working now!
Next, use Insomnia to send the request and inspect the response. Because of its usability, you're going to want to use Insomnia exclusively once you start sending requests with a JWT.
In Insomnia, create a GET request to http://localhost:3000. Here's the response:
In Insomnia, you should get a 200 OK with "Hello world!" in the response body.
It looks like the API server is up and running. Now, it's time to put Kong Gateway in front of it.
2. Set Up Kong Gateway
I won’t cover the details here, but the Kong Gateway installation steps may look different depending on your system.
Once you’ve installed Kong, you'll need to take a few additional steps.
DB-Less Declarative Configuration
For the simple setup example, I'll use database-less declarative configuration. When you start up Kong, you'll tell it where to find a .yml file with all of the configuration declared within.
There are only two edits you need to make in your kong.conf file.
When Kong starts up, it will be in DB-less mode, meaning it will look to your project's kong.yml file for a configuration.
Let's go over this.
The _format_version metadata specifies the version number of your declarative configuration format.
Next, you define your service, which Kong describes as "an entity representing an external upstream API or microservice." You can name your service my-api-service and specify its URL — you'll recall that the Express server listens for requests at http://localhost:3000.
Next, define routes, which "determine how (and if) requests are sent to their Services after they reach Kong Gateway." The (local) URL for Kong is http://localhost:8000. You should declare your route so that Kong listens for requests at http://localhost:8000/api, then routes to your service.
Let's see this in action. Make sure your Express server is running in a separate terminal. Then, start Kong.
In your browser, go to http://localhost:8000/api:
Kong Gateway is up. Finally, add authentication.
3. Attach JWT Plugin to Kong Gateway
To add the JWT plugin, add a “plugins” definition to your kong.yml file:
Here, you can add the plugin named jwt and attach it to your service called my-api-server. For its configuration options, tell the plugin to check the exp value to verify that the access token has not expired.
At this point, restart Kong and see what happens:
The response is 401 Unauthorized. Excellent! Kong now requires a valid JWT for any requests to your API server. Next, you need to tell Kong what constitutes a valid JWT.
In kong.yml, you need to add a consumer and a credential. Kong describes consumers as being "associated with individuals using your Service, and can be used for tracking, access management, and more." In a more elaborate setting, every one of your API users could be a consumer. That's a use case you can read more about towards the end of this article. In this situation, your login server is your consumer. Your login server will be the entity generating JWTs and handing them out. Users who make a request to Kong will be holding a "login server" JWT.
Edit your kong.yml file by adding the “consumers” and “jwt_secrets” definitions:
You've added a new consumer, named login_server_issuer. Then, you added a JWT API gateway credential for that consumer, which contains the secret used to sign JWTs for this consumer. Authentication requires two parts:
- The kid (key identifier) value in the JWT header, which is a unique identifier that lets the plugin determine which consumer allegedly issued this JWT
- Verification of the consumer’s secret - Was this the secret used to sign this JWT API gateway? If so, then this JWT is authentic.
If you want to generate a JWT for testing, you need the secret (which you have) and the key to use for the kid value. Kong gives us access to that value through its admin API at http://localhost:8001. You send a GET request to the admin API's endpoint /consumers/CONSUMER-USERNAME/jwt. This gives us information about this consumer's JWT credential:
As you inspect this credential’s information, you should see the JWT secret and signing algorithm. What you’re looking for, though, is the key. In the above example, that's 1nzcMG9Xg7n1lLgmltHnkAmkt7yp4fjZ. This is what you use as the kid value in the JWT header. The Kong plugin will see this kid value, track down the associated consumer and secret, then make sure the JWT was signed with that secret.
To test this, let's start with the happy path. You need a JWT with a header that includes the correct kid value, signed with the right secret. For simplicity, let's do this at jwt.io. Here, you can craft your payload, set the signing secret and then copy/paste the resulting JWT.
In the payload, the kid must match the key value from above. Also, because you configured the plugin to check JWT token expiration, you should set the exp (Unix timestamp) far into the future. The name and email are inconsequential; they just demonstrate that you can put other helpful data in the JWT payload.
Lastly, include your JWT secret at the bottom for proper signing. The result is an encoded JWT.
Back in Insomnia, you have your original request that resulted in 401. You need to add "Authorization" to that request. Choose "Auth – Bearer Token," then paste in your encoded JWT from above.
Now, with a valid JWT attached, resend the request.
Your JWT should have been validated, and Kong routed us to the API server!
If you look back at the terminal running the Express server, you'll recall that you're logging the request headers to the console. When the JWT plugin authenticates an access token, it writes some additional values to the upstream headers, namely the consumer id, username and credential identifier (the key value).
But what happens if your JWT is not valid? Let's test and see.
First, sign the JWT with a different secret. Back at jwt.io, keep the payload, but change the signing secret. Copy the resulting JWT to Insomnia, and send your request again. You'll get a 401 with "Invalid Signature."
If your secret is correct, but the kid is incorrect, Kong won't find an associated credential. Without that credential, there’s no way to find the secret for authenticating the JWT.
Lastly, if the exp value is in the past, then your JWT has expired. Just as you expected, you get the following response.
And that's it! You should be up and running with Kong Gateway and the JWT Plugin acting as an authentication layer in front of an API server.
From here, you should have the foundation needed to deal with more complex cases. Let's look at a few below.
Other Use Cases for Kong Gateway JWT Plugin
Every User Is a Consumer
Earlier, I illustrated a straightforward case where a login server acts as your service's sole consumer. Though that’s a standard model, you can also configure Kong to hook into your user database. You can tell Kong Gateway that every user in your database is a consumer, and they each have a unique JWT secret. Your login server could generate JWTs, but a user's successful login would result in a JWT signed with a secret that is specific to that individual user. This type of granular JWT issuing could allow for more precise tracking and access logging through Kong.
With this model, you could even let users (consumers) know what their kid and signing secret are, and your users could generate their own JWT to use whenever they want.
Authentication for Some Routes, but Not Others
In this project, I enabled the JWT API gateway plugin on the api-requests routes. You can imagine a use case where Kong is configured with a single service (like an API server), but multiple routes are configured to hit different subsets of paths. For example, you might have a route for endpoints related to product inventory but another route for endpoints related to order history. Perhaps you could enable the JWT plugin on the order history route—which requires authentication—but not on the publicly accessible inventory counts route. With Kong, you can pick and choose which services or routes should get the JWT plugin and which shouldn't.
"Anonymous" Access
The plugin also has an anonymous consumer option. With this option, Kong allows the user to anonymously access the upstream service if authentication fails. You can imagine an upstream service like a web application, where "anonymous" (not logged-in) users can still access parts of your application with limited privileges.
Public/Private Keys
The common (and simpler) usage for generating a JWT is to sign it with a secure secret kept by the JWT issuer. You can configure the Kong plugin for generating a JWT with public/private keys. In this model, the consumer maintains a pair of keys — one public and one private. The private API key is secure, while the public key is available to hand out. When configuring a consumer's JWT credentials for Kong, the system stores the public key with Kong instead of a secret. When generating a JWT, the consumer signs it with his or her private key. The JWT API gateway plugin uses the public key for this consumer to authenticate that the consumer signed the JWT.
Set It and Forget It
Implementing authentication —and getting it right —is hard work. As microservices become the norm, delegating authentication makes more and more sense. "Rolling your own" implementation for JWT authentication can muddy a code base and still leave you wondering if you got it right. By using well-tested and community-adopted services that handle concerns like routing, logging or authentication, developers can shift their focus back to their projects' unique needs.
With that, you now have a solid foundation for getting started with Kong API Gateway and the JWT plugin. It's time to get to work.
Want more tutorials?
- How to Convert JSON to XML SOAP and Back
- Kong API Gateway on Kubernetes with Pulumi
- JWT Authentication for Microservices: API Gateway Tutorial
- Using Kong Kubernetes Ingress Controller as an API Gateway
- APM With Prometheus and Grafana on Kubernetes Ingress
- Kubernetes Ingress gRPC Example With a Dune Quote Service
Have questions or want to stay in touch with the Kong community? Join us wherever you hang out: