In this post, we are going to shortly describe different authentication methods, overview the use-cases and benefits of centralized authentication with Apache APISIX API Gateway and we will learn how to configure common authentication plugins like basic, key, and JWT to protect your services. So, let's get started.
About Apache APISIX
Apache APISIX is an open-source API gateway and it offers services to manage your traffic such as rate-limiting, observability, logging, security, authentication and more. It also has many built-in plugins that suit all APIs and microservices. The one that we are going to be focusing on in the particular tutorial is Authentication plugins. At present, it can already work with a large number of authentication-related plug-ins, as you can see on the plugin hub page.
API Authentication
API Authentication is all about proving or verifying the identity of the people accessing your system. It's the process of using a software protocol to ensure that clients on a network are who they claim to be before granting them access.
There are several methods of API authentication, here are some of the most popular we are reviewing in this tutorial without going into deep details since our main focus is on authentication plugins. If you are already well-acquainted with them, you can skip this theory part and jump into practical tutorial on plugins section below directly:
HTTP Basic Authentication
The simplest way to handle authentication is through the use of HTTP, where the username and password are sent alongside every API call. You can use an HTTP header and encode the username and password, as it is shown in the below picture:
API Key Authentication
Application Programming Interface Key (API Key) technique creates unique keys for users and passes them with every request. The API generates a secret key that is a long, difficult-to-guess string of numbers and letters—at least 30 characters long, although there’s no set standard length. It is typically passed alongside the API authorization header.
OAuth Authentication
Open Authorization (OAuth) is yet another widely used method for both authentication and authorization. In this approach, the user logs into a system. That system will then request authentication, usually in the form of a token. The user will then forward this request to an authentication server, which will either reject or allow this authentication. From here, the token is provided to the user, and then to the requester. Such a token can then be checked at any time independently of the user by the requester for validation and can be used over time with strictly limited scope and age of validity.
From traditional to Centralized Authentication model
Apache APISIX can be used as a centralized authentication gateway. For example, you have three services that you want to expose, the diagram on the left shows us a more common traditional authentication approach. Each application service module goes to develop a separate authentication module that is used to support a set of process handling for authentication. But when the volume of services increases, it becomes clear that the development workload of these modules is huge and repetitive.
For the above scenario, we can achieve consolidation and reduce the amount of development by replacing this part of the development logic in the Apache APISIX gateway.
As shown in the above figure on the right, the user or application party goes directly to request Apache APISIX, and then Apache APISIX passes the authenticated identity information to the upstream application service after it has been identified and authenticated. After that, the upstream application service can read this information from the request header and then process the subsequent related work.
Communication with Identity providers
We can discover yet another advanced usage of APISIX as an authentication handler. The API gateway can be typically where we do all of the interactions with IDP (Identity provider) for those upstream services. It alone interacts with the identity provider, it only has to know how to deal with the authentication logic flows and by avoiding the need to repeat the development of authentication code for each application. There is a variety of IDP providers such as Okta, Cognito, Azure Active Directory, and more.
The process of centralized authentication is shown in the figure first, the user initiates a request, and then the front gateway is responsible for the user authentication process, interfacing with the identity provider and sending an authorization request to the identity provider. The identity provider returns the user information. After the gateway completes user identification, it forwards the user identity information to the back-end application in the form of a request header.
Apache APISIX Plugins Tutorial
We covered enough theoretical knowledge up to now, it is time to play with Apache APISIX Auth Plugins. However, we should learn a couple of core concepts before Apache APISIX works as per our needs. You can read through the documentation if you are not yet familiar with the following.
- The Route is the most critical concept in Apache APISIX; it instructs APISIX on how to forward traffic to the correct upstream.
- Upstream is the view of backend microservices from Apache APISIX point of view.
- The Plugin is a mechanism to manage traffic (authentication, authorization, and so on) on the APISIX side.
Pre-requisites
- Installed Docker and Docker Compose component.
- curl command for API testing. You can also use other tools such as Postman for testing.
Install Apache APISIX
Download the Docker image of Apache APISIX.
git clone https://github.com/apache/apisix-docker.git
Switch the current directory to the apisix-docker/example path.
cd apisix-docker/example
Run the docker-compose command to install Apache APISIX.
docker-compose -p docker-apisix up -d
HTTP Basic Authentication Plugin
Apache APISIX basic-auth plugin is an authentication plugin that works with the consumer object. In the context of an HTTP transaction, basic access authentication is a method to provide a user name and password when making a request.
Let's enable basic-auth plugin for a consumer and configure the value of username and password.
curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"username": "foo",
"plugins": {
"basic-auth": {
"username": "example_username",
"password": "your_strong_password"
}
}
}'
When the consumer is created successfully, you will get the following JSON object in the response:
{
"node": {
"value": {
"create_time": 1650866058,
"username": "foo",
"update_time": 1650866058,
"plugins": {
"basic-auth": {
"password": "your_strong_password",
"username": "example_username"
}
}
},
"key": "/apisix/consumers/foo"
},
"action": "set"
}
Afterward, we need to add a Route and allow basic-auth
to control the requests by specifying them in plugins settings:
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"methods": ["GET"],
"uri": "/get",
"plugins": {
"basic-auth": {}
},
"upstream_id": "1"
}'
Apache APISIX server responds with the success:
{
"node": {
"value": {
"methods": [
"GET"
],
"upstream_id": "1",
"uri": "/get",
"update_time": 1650866398,
"plugins": {
"basic-auth": {
"hide_credentials": false
}
},
"status": 1,
"create_time": 1648567195,
"id": "1",
"priority": 0
},
"key": "/apisix/routes/1"
},
"action": "set"
}
Up to now, we have all set, and we can test different cases below:
- Case 1: Requesting
/get
endpoint without authorization details.
curl -i http://127.0.0.1:9080/get
You will receive the HTTP 401 error in the response, as we expected:
{"message":"Missing authorization in request"}
- Case 2: When the user provides a valid username and password.
curl -i -uexample_username:your_strong_password http://127.0.0.1:9080/get
Urraa! Now we got 200 OK response from the upstream indicating that the authentication plugin is working well.
HTTP/1.1 200 OK
- Case 3: When we access the upstream with the wrong user credentials.
curl -i -uexample_username:wrong_password http://127.0.0.1:9080/get
Correct, we got an error saying that "Password is incorrect"
{"message":"Password is error"}
That's all about basic-auth plugin setup.
API Key Authentication Plugin
In the previous section, we demonstrated the usability of one type of authentication method with the help of APISIX basic-auth
plugin. Now let's have a look at another plugin key-auth
Apache APISIX key-auth Plugin is an authentication plugin, it should work with consumers together.
Add Key Authentication (also sometimes referred to as an API key) to a Service or a Route. Consumers then add their key either in a query string parameter or a header to authenticate their requests.
To enable key-auth
plugin, two steps need to be done:
- Step 1: Create a consumer object, and set the attributes of plugin key-auth.
curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"username": "example_username",
"plugins": {
"key-auth": {
"key": "auth-one"
}
}
}'
The response from APISIX, you can expect like below:
{
"node": {
"value": {
"username": "example_username",
"update_time": 1650822986,
"plugins": {
"key-auth": {
"key": "auth-one"
}
},
"create_time": 1650822986
},
"key": "/apisix/consumers/example_username"
},
"action": "set"
}
- Step 2: After we enabled
key-auth
plugin, we can create a new route and apply key-auth plugin configuration. For simplicity, we can keep JSON Object for key-auth plugin as an empty.
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"methods": ["GET"],
"uri": "/get",
"plugins": {
"key-auth": {}
},
"upstream_id": "1"
}'
By default, APISIX sets key name as apiKey in the header, you can also customize default header by adding configuration for the
key-auth
plugin
{
"key-auth": {
"header": "Authorization"
}
}
You will get the following response from the Admin API endpoint:
{
"node": {
"value": {
"uri": "/get",
"priority": 0,
"status": 1,
"plugins": {
"key-auth": {
"header": "apikey",
"query": "apikey"
}
},
"create_time": 1648567195,
"update_time": 1650823379,
"methods": [
"GET"
],
"upstream_id": "1",
"id": "1"
},
"key": "/apisix/routes/1"
},
"action": "set"
}
Now we can test our key-auth
plugin by accessing our GET endpoint without apiKey header.
curl http://127.0.0.2:9080/get
As we expected, it returns an error with 401 HTTP Status code:
{"message":"Missing API key found in request"}
Alternatively, if we specify the apiKey in the header, it should respond with the success:
curl http://127.0.0.2:9080/get -H 'apikey: auth-one' -i
Great, now the API Key authentication is enabled for the endpoint.
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 326
Connection: keep-alive
Another scenario is when the user is trying to access the endpoint with the wrong apiKey
curl http://127.0.0.2:9080/get -H 'apikey: wrong_api_key' -i
Output:
{"message":"Invalid API key in request"}
JWT Plugin
JWT (JSON Web Token) plugin is another solid option for API gateway authentication. JWT simplifies authentication setup, taking care of the nitty-gritty details. Please refer to JWT for more information.
The Apache APISIX JWT Plugin acts as an issuer and also validates the token on behalf of the API. It means that developers do not have to add any code to process the authentication.
We need to disable the
key-auth
plugin we previously enabled to use another authentication plugin. Disabling is possible via the Dashboard or the CLI.
Let's apply the JWT plugin to our existing API. We update the existing Consumer
plugin config with JWT-related configuration:
curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"username": "example_consumer",
"plugins": {
"jwt-auth": {
"key": "user-key",
"secret": "my-secret-key"
}
}
}'
The response will look something like this:
{
"node": {
"key": "/apisix/consumers/example_consumer",
"value": {
"create_time": 1649158467,
"username": "example_consumer",
"update_time": 1649163154,
"plugins": {
"jwt-auth": {
"base64_secret": false,
"secret": "my-secret-key",
"algorithm": "HS256",
"key": "user-key",
"exp": 86400
}
}
}
},
"action": "set"
}
We can now add the jwt-auth
plugin to the Route we have created previously:
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"methods": ["GET"],
"uri": "/get",
"plugins": {
"jwt-auth": {}
},
"upstream_id": "1"
}'
Response:
{
"node": {
"key": "/apisix/routes/1",
"value": {
"upstream_id": "1",
"uri": "/get",
"create_time": 1648567195,
"status": 1,
"id": "1",
"plugins": {
"jwt-auth": {}
},
"priority": 0,
"methods": [
"GET"
],
"update_time": 1649163340
}
},
"action": "set"
}
We want to validate that the setup is correct as we did before.
jwt-auth
uses the HS256 algorithm by default.
If you use the RS256 algorithm, you must specify the algorithm and configure the public and private keys.
Please check the documentation for more details.
First, you need to set up the route for the API that signs the token, which will use the public-api plugin.
curl http://127.0.0.1:9080/apisix/admin/routes/jas -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/apisix/plugin/jwt/sign",
"plugins": {
"public-api": {}
}
}'
Response:
{
"action": "set",
"node": {
"key": "/apisix/routes/jas",
"value": {
"status": 1,
"priority": 0,
"id": "jas",
"update_time": 1649490287,
"plugins": {
"public-api": {}
},
"uri": "/apisix/plugin/jwt/sign",
"create_time": 1649490287
}
}
}
Then, run the following command to generate a new JWT token:
curl http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key -i
Apache APISIX returns a token:
HTTP/1.1 200 OK
Date: Tue, 05 Apr 2022 12:57:34 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.12.1
<GENERATED_TOKEN>
We can use the newly-generated token to authenticate our next request:
curl -i -X GET http://127.0.0.1:9080/get -H 'Authorization: <SET_GENERATED_TOKEN>'
Output with token:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 454
Connection: keep-alive
Date: Tue, 05 Apr 2022 13:02:30 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/2.12.1
If you try to access the same endpoint without a token in the Header request, you will get HTTP Error _401 Unauthorized:
curl -i -X GET http://127.0.0.1:9080/get
Output without token:
{ "message": "Missing JWT token in request" }
With the JWT plugin demo, our plugin tutorials end.
Summary
We have validated the client's identity attempting to request by using various authentication plugins with the help of Apache APISIX. APISIX is now open to cooperation with various plug-in functions for new the adaptations, and the plug-in library is relatively rich.
To learn more⤵️
➔ Watch Video Tutorial Getting Started with Apache APISIX
➔ Watch Video Tutorial Getting Started with Apache APISIX Dashboard
➔ Watch Video Tutorial Centralized Authentication with Apache APISIX Plugins
➔ Watch the Video version of the blog post APIs Observability with Apache APISIX Plugins
Community⤵️
🙋 Join the Apache APISIX Community
🐦 Follow us on Twitter
📝 Find us on Slack
📧 Mail to us with your questions
Top comments (1)
Great article
Can you elaborate how the service receive the user info in the jwt case ?
I mean once we send the request whit a jwt header and the plugin validate apisix route the request to the service, but how it can know which user is requesting ?