API GatewayをOSSで実現。Kongを使ったJWT認証の方法。

API Gatewayといえば、Amazon API Gatewayとか、Apigeeとか有名ですが、OSSのKongというのも非常に便利です。今回はこのOSSのAPI Gateway 「[Kong](https://konghq.com/)」を用いて、サービスにJWT認証の機能をつけてみます。

JWT認証とは、「JSON Web Token」認証のことで、詳細な解説は以下のあたりの記事を参照してください。

* [JWT について調べた内容をまとめました。](https://qiita.com/gctoyo/items/8d0ffb265845ab8cc87c)

# Kong API Gatewayの構築

今回はDockerコンテナ上に構築しました。
手順はここに記載の通りで簡単に稼動します。
[Docker Hub kong](https://hub.docker.com/_/kong/)

Kongのバージョンは0.14.1

# 前提の環境

APIサーバ(api-server)に、http://api-server:3000 というURLでサービスが稼動しているとします。
ここに対し、KongのAPI Gateway(api-gw-server)の8000ポートの/apiのパスで中継するような構成を作ることにします。
認証をAPI Gateway側に機能をもたせて、api-server:3000へのアクセスを認証済みのリクエストのみ通過させるようなことを想定します。

# Kongにサービスを登録
まずは、サービス登録します。

```
$ curl -i -X POST \
  --url http://api-gw-server:8001/services/ \　#8001ポートはKongの管理APIポート
  --data 'name=api-service' \
  --data 'url=http://api-server:3000'
```

# Kongにルートを登録

次に先程登録したサービスに対してルート登録します。

```
$ curl -i -X POST \
  --url http://api-gw-server:8001/services/api-service/routes \
  --data 'paths[]=/api'
```

これで、http://api-gw-server:8000/api にアクセスすると、認証なしでAPIサーバにアクセスすることができます。

では、ここからJWT認証を設定していきます。

# JWT認証プラグインを有効化

JWT認証はプラグインとして機能提供されているため、先程作成したapi-serviceのサービスに対してJWTプラグイン機能を有効化します。

```
$ curl -X POST http://api-gw-server:8001/services/api-service/plugins --data "name=jwt"
```

これだけで機能が有効化されるため、先程のhttp://api-gw-server:8000/api にアクセスすると認証エラーでアクセスできなくなっていることがわかります。

```
{"message":"Unauthorized"}
```

# Consumerの登録

認証設定を行うためにはConsumerを登録し、認証設定を行う必要があります。

```
$ curl -X POST http://api-gw-server:8001/consumers --data "username=ike-dai" --data "custom_id=user-10000"
```

ike-daiという名前で1件consumerを作成しています。

# ConsumerのCredentialを発行

作成したConsumerに対し、jwtのcredentialを発行します。

```
$ curl -X POST http://api-gw-server:8001/consumers/ike-dai/jwt -H "Content-Type: application/x-www-form-urlencoded"
```
すると、以下のようなレスポンスが得られます。

```json
{
  "created_at":1541997140178,
  "id":"e22aecf1-d82f-4f84-9d6d-4f369a014227",
  "algorithm":"HS256",
  "secret":"8C1MDYIY3Byc2ZTndTyzThN9zDq8fKRy",
  "key":"Ua0Xx3s9vUwHhFL2IuINgMBlkYObgBeU",
  "consumer_id":"ef4c78fe-05cb-47f1-b404-331d3741bb4f"
}
```

# Tokenの発行

Token発行自体はKongでできないようなので以下のサービスを使って生成したり、pythonで独自に生成します。

https://jwt.io/

上記サービスを使う場合、以下を指定して生成します。

***ヘッダー指定***

```json
{
  "alg": "HS256",
  "typ": "JWT"
}
```

***Payload指定***

```json
{
  "iss": "Consumerのcredentialのキー情報(上の例の場合、Ua0Xx3s9vUwHhFL2IuINgMBlkYObgBeU)"
}
```

***Verify Signature指定***

```
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  credentialのsecret情報(上の例の場合、8C1MDYIY3Byc2ZTndTyzThN9zDq8fKRy)
)
```

また、PythonのPyJWTを利用して生成する場合は以下のようなコードでできます。

```python

import jwt

encoded = jwt.encode({'iss':'Ua0Xx3s9vUwHhFL2IuINgMBlkYObgBeU'}, '8C1MDYIY3Byc2ZTndTyzThN9zDq8fKRy', algorithm='HS256')
print(encoded)
```

# Tokenを指定してAPIにアクセス

できあがったTokenはxxx.xxx.xxxという.区切りのものになっています。これをAuthenticationヘッダーに指定して先程のAPIにアクセスします。

以下の例はToken「eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJVYTBYeDNzOXZVd0hoRkwySXVJTmdNQmxrWU9iZ0JlVSJ9.zoNrGPLz-hW22wcNX-izQBFfo9pnO-nRI9Psj0laUPA」を引き渡している例です。

```
$ curl -XGET http://api-gw-server:8000/api -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJVYTBYeDNzOXZVd0hoRkwySXVJTmdNQmxrWU9iZ0JlVSJ9.zoNrGPLz-hW22wcNX-izQBFfo9pnO-nRI9Psj0laUPA'
```

これでJWT認証が有効化されたAPIサーバの出来上がりです。

# まとめ

Kong APIはpluginとしてJWT認証、Basic認証、LDAP認証、Key認証、OAuth2.0認証等様々な認証方式に対応しています。サービス開発する際には有効に活用して認証管理の機能の手間を軽くできます。