DEV Community

Cover image for Centralized Authentication with Apache APISIX Plugins
Bobur Umurzokov for Apache APISIX

Posted on

Centralized Authentication with Apache APISIX Plugins

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.

Authentication with several methods

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:

HTTP Basic Authentication

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.

API Key Authentication example diagram

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.

Centralized Authentication with Apache APISIX

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.

IDP with Apache APISIX

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
Enter fullscreen mode Exit fullscreen mode

Switch the current directory to the apisix-docker/example path.

cd apisix-docker/example
Enter fullscreen mode Exit fullscreen mode

Run the docker-compose command to install Apache APISIX.

docker-compose -p docker-apisix up -d
Enter fullscreen mode Exit fullscreen mode

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"
        }
    }
}'
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}'
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

You will receive the HTTP 401 error in the response, as we expected:

{"message":"Missing authorization in request"}
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

Urraa! Now we got 200 OK response from the upstream indicating that the authentication plugin is working well.

HTTP/1.1 200 OK
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

Correct, we got an error saying that "Password is incorrect"

{"message":"Password is error"}
Enter fullscreen mode Exit fullscreen mode

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"
        }
    }
}'
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode
  • 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"
}'
Enter fullscreen mode Exit fullscreen mode

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"
    }
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

Now we can test our key-auth plugin by accessing our GET endpoint without apiKey header.

curl http://127.0.0.2:9080/get
Enter fullscreen mode Exit fullscreen mode

As we expected, it returns an error with 401 HTTP Status code:

{"message":"Missing API key found in request"}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Output:

{"message":"Invalid API key in request"}
Enter fullscreen mode Exit fullscreen mode

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.

disable-key-auth-plugin

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"
        }
    }
}'
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}'
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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": {}
    }
}'
Enter fullscreen mode Exit fullscreen mode

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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Output without token:

{ "message": "Missing JWT token in request" }
Enter fullscreen mode Exit fullscreen mode

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⤵️

Download Apache APISIX

➔ 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

Install Apache APISIX

➔ 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)

Collapse
 
jagedn profile image
Jorge Eψ=Ĥψ

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 ?