Centralized Authentication with Apache APISIX Plugins
Tags: programming, authentication, plugins, tutorial
Published: 2022-05-03 05:52:22

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](https://github.com/apache/apisix) 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](https://apisix.apache.org/) 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](https://apisix.apache.org/plugins).

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofr8wm8anx51g4np72fh.png)

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](#apache-apisix-plugins-tutorial) below directly:

- [HTTP Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
- [API Key Authentication](https://en.wikipedia.org/wiki/Application_programming_interface_key)
- [OAuth/Auth2 Authentication](https://en.wikipedia.org/wiki/OAuth)

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/en9gavsqmqmqy34ba4yv.jpg)

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ijjn6dysho25mex85bve.jpg)

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wo9seqngl2hp86324znj.jpg)

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](https://apisix.apache.org/docs/apisix/architecture-design/upstream) 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](https://www.okta.com/), [Cognito](https://aws.amazon.com/cognito/), [Azure Active Directory](https://azure.microsoft.com/en-us/services/active-directory/), and more. 

![IDP with Apache APISIX](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wbqi26h2fiotxejcflue.jpg)
 
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](https://apisix.apache.org/docs/apisix/architecture-design/route/) is the most critical concept in Apache APISIX; it instructs APISIX on how to forward traffic to the correct upstream.
- [Upstream](https://apisix.apache.org/docs/apisix/architecture-design/upstream/) is the view of backend microservices from Apache APISIX point of view.
- [The Plugin](https://apisix.apache.org/docs/apisix/architecture-design/plugin/) is a mechanism to manage traffic (authentication, authorization, and so on) on the APISIX side.

### Pre-requisites

- Installed [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) component.
* [curl](We use the https://curl.se/docs/manpage.html) 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.

```bash
git clone https://github.com/apache/apisix-docker.git
```

Switch the current directory to the _apisix-docker/example_ path.

```bash
cd apisix-docker/example
```

Run the docker-compose command to install Apache APISIX.

```bash
docker-compose -p docker-apisix up -d
```

### HTTP Basic Authentication Plugin

[Apache APISIX basic-auth plugin](The https://apisix.apache.org/docs/apisix/plugins/basic-auth) 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.

```bash
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:

```json
{
  "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:

```bash
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:

```json
{
  "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.

```bash
curl -i http://127.0.0.1:9080/get
```

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

```json
{"message":"Missing authorization in request"}
```

- Case 2: When the user provides a valid username and password.

```bash
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.

```text
HTTP/1.1 200 OK
```

- Case 3: When we access the upstream with the wrong user credentials.

```bash
curl -i -uexample_username:wrong_password http://127.0.0.1:9080/get
```

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

```json
{"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](The https://apisix.apache.org/docs/apisix/plugins/key-auth) 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.

```bash
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:

```bash
{
  "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.

```bash
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

```json
{
    "key-auth": {
        "header": "Authorization"
    }
}
```

You will get the following response from the Admin API endpoint:

```json
{
  "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.

```bash
curl http://127.0.0.2:9080/get
```

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

```json
{"message":"Missing API key found in request"}
```

Alternatively, if we specify the _apiKey_ in the header, it should respond with the success:

```bash
curl http://127.0.0.2:9080/get -H 'apikey: auth-one' -i
```

Great, now the API Key authentication is enabled for the endpoint.

```text
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_

```bash
curl http://127.0.0.2:9080/get -H 'apikey: wrong_api_key' -i
```

Output:

```json
{"message":"Invalid API key in request"}
```

### JWT Plugin

[JWT (JSON Web Token) plugin](The https://apisix.apache.org/docs/apisix/plugins/jwt-auth) is another solid option for API gateway authentication. JWT simplifies authentication setup, taking care of the nitty-gritty details. Please refer to [JWT](https://jwt.io/) for more information.

The [Apache APISIX JWT Plugin](https://apisix.apache.org/docs/apisix/plugins/jwt-auth) 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zwetx4mdcx4yjdpje9gb.png)

Let's apply the JWT plugin to our existing API. We update the existing `Consumer` plugin config with JWT-related configuration:

```bash
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:

```json
{
  "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:

```bash
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:

```bash
{
  "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](https://apisix.apache.org/docs/apisix/plugins/jwt-auth#:~:text=jwt%2Dauth%20uses%20the%20HS256%20algorithm) for more details.

First, you need to set up the route for the API that signs the token, which will use the [public-api](https://apisix.apache.org/docs/apisix/plugins/public-api/) plugin.

```bash
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:

```json
{
  "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:

```bash
curl http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key -i
```

Apache APISIX returns a token:

```text
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:

```bash
curl -i -X GET http://127.0.0.1:9080/get -H 'Authorization: <SET_GENERATED_TOKEN>'
```

Output with token:

```text
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:

```bash
curl -i -X GET http://127.0.0.1:9080/get
```

Output without token:

```json
{ "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] (https://apisix.apache.org/docs/apisix/plugins/key-auth) 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](https://apisix.apache.org/downloads )

➔ Watch Video Tutorial [Getting Started with Apache APISIX](https://youtu.be/dUOjJkb61so)  

➔ Watch Video Tutorial [Getting Started with Apache APISIX Dashboard](https://youtu.be/-9-HZKK2ccI)
 
➔ Watch Video Tutorial [Centralized Authentication with Apache APISIX Plugins](https://youtu.be/kbELyQIXAsw)

➔ [Install Apache APISIX](https://apisix.apache.org/docs/apisix/how-to-build)

➔ Watch the Video version of the blog post [APIs Observability with Apache APISIX Plugins](https://youtu.be/XK0xcui5BQU) 

Community⤵️

🙋 [Join the Apache APISIX Community](https://apisix.apache.org/docs/general/community) 
🐦 [Follow us on Twitter](https://twitter.com/ApacheAPISIX)
📝 [Find us on Slack](https://join.slack.com/t/the-asf/shared_invite/zt-vlfbf7ch-HkbNHiU_uDlcH_RvaHv9gQ)
📧 [Mail to us](dev@apisix.apache.org) with your questions