# JWT認証

[JWT](https://jwt.io/)認証は、クライアントの認証情報やセッション情報をサーバー側で保持しないトークンベースの認可機構です。鍵を所有していれば認証情報を一括発行できるため、最もシンプルな認証方法となります。

::: tip 注意

JWT認証はEMQX Serverlessのデプロイメントではサポートされていません。

:::

## JWT認証の仕組み

クライアントは接続開始時に、JWTをユーザー名またはパスワードフィールド（モジュール設定に依存）に保持します。EMQXプラットフォームは設定された鍵または証明書を用いてJWTを復号します。復号が成功すれば認証成功とみなされ、失敗すれば認証失敗となります。

署名検証が成功すると、JWT認証器はクレームの検証に進みます。JWT認証器は`iat`（発行時刻）、`nbf`（有効開始時刻）、`exp`（有効期限）などのクレームに基づきJWTの有効性を積極的にチェックします。さらにカスタムクレームの検証も指定可能です。署名とクレームの両方の検証が成功した場合にのみ、クライアントにアクセスが許可されます。

デフォルト設定では、JWT認証を有効にすると任意のユーザー名と以下のパスワードで接続可能です。パスワードはデフォルトの鍵フィールド`emqxsecret`に対して検証されます。

```bash
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFTVFYIENsb3VkIiwiaWF0IjoxNTE2MjM5MDIyfQ.-k9Ggc6L_Jxq4uUf9xwdJpwRrS3PquL-JZKtAJoOvBo
```

> 上記のJWTトークンはテスト用です。ビジネス要件に合わせたJWTトークンは適切なツールで生成してください。詳細は[JWTの生成方法](#how-to-generate-jwt)をご参照ください。

### JWTの生成方法

本節では、EMQXでクライアント認証に使用する有効なJWTを生成する手順を説明します。

#### 前提条件

- 秘密鍵（HMACアルゴリズムの場合）または秘密鍵ペア（RSA/ECDSAの場合）
- JWT生成ツールまたはライブラリ（例：[jwt.io](https://jwt.io)、[jjwt](https://www.jsonwebtoken.io/)、[mkjwk](https://mkjwk.org)、Python、Node.js）
- EMQXが期待するアルゴリズム（`HS256`、`RS256`など）を把握していること

#### JWTの構造

JWTは以下の3つの部分で構成されます。

```
JWT = base64UrlEncode(Header) + "." + base64UrlEncode(Payload) + "." + Signature
```

- **Header**はトークンのメタデータ（アルゴリズムやトークンタイプなど）を指定します。例：

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

- **Payload**は`username`、`exp`、`client_attrs`などのクレーム（ユーザー情報）を含みます。例：

  ```json
  {
    "username": "emqx_user",
    "exp": 1719830400,
    "client_attrs": {
      "role": "admin",
      "sn": "device-001"
    }
  }
  ```

- **Signature**はトークンの改ざんを防止するため、HeaderとPayloadを秘密鍵や秘密鍵ペアで署名して生成されます。

#### 手順

1. JWTのHeaderを定義します。例（HMAC SHA-256の場合）：

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

   RSA/ECDSAの場合は、鍵の種類に応じて`"alg"`を`RS256`、`ES256`などに置き換えます。

2. JWTのPayloadを定義します。PayloadにはEMQXで利用するクレームを含めます。主なフィールド例：

   ```json
   {
     "sub": "mqtt_client",       // サブジェクト：任意、識別用
     "username": "emqx_user",    // 任意：EMQX設定でバインドされている場合に使用
     "clientid": "client_123",   // 任意：クライアント制限用
     "exp": 1719830400           // 必須：有効期限（Unixタイムスタンプ）
   }
   ```

   **重要事項**：

   - `exp`は必須です。設定しないとEMQXでトークンが拒否される可能性があります。
   - `username`や`clientid`は、EMQXがトークン内で検証する設定の場合に追加します。
   - `acl`や`client_attrs`などのカスタムクレームも含められます。

3. HeaderとPayloadをBase64Urlエンコードします。手動でもJWTライブラリを使っても構いません。

4. JWTに署名します。秘密鍵や秘密鍵ペアを使って署名を生成します。

   - HMAC（例：`HS256`）の場合：`HMACSHA256(base64Url(header) + "." + base64Url(payload), secret)`
   - RSA/ECDSA（例：`RS256`）の場合：適切な署名アルゴリズムで秘密鍵を使用します。

5. JWTを組み立てます。エンコード済みHeader、Payload、Signatureをピリオドで連結します。

   ```
   <header>.<payload>.<signature>
   ```

6. JWTの検証（任意ですが推奨）を行います。[jwt.io](https://jwt.io)などのツールでデコード・検証してください。

#### Python（`pyjwt`）の例

```
import jwt
import datetime

secret = "your_shared_secret"

payload = {
    "username": "emqx_user",
    "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}

token = jwt.encode(payload, secret, algorithm="HS256")
print(token)
```

`RS256`の場合は`secret`を秘密鍵に置き換え、アルゴリズムを`RS256`に指定してください。

#### 結果

これでEMQXのMQTT `CONNECT`パケットのユーザー名またはパスワードフィールドに使用可能な署名済みJWTトークンが生成されました。

複雑なビジネス用途向けのJWT生成手順は、ブログ記事[JWT Authentication and JWKS Endpoint in MQTT: Principle and a Hands-on Guide](https://www.emqx.com/en/blog/jwt-authentication-and-jwks-endpoint-in-mqtt)をご参照ください。

## アクセスコントロールリスト（任意）

アクセスコントロールリスト（ACL）は、認証結果の拡張機能で、ログイン後のクライアントの権限を制御します。JWTに`acl`フィールドを含めてクライアントの権限を指定可能です。

詳細は[アクセスコントロールリスト（ACL）](https://docs.emqx.com/en/emqx/latest/access-control/authn/acl.html)をご覧ください。

## クライアント属性

EMQX v5.7.0以降、JWT Payloadの任意フィールド`client_attrs`を使って[クライアント属性](https://docs.emqx.com/en/emqx/latest/client-attributes/client-attributes.html)を設定できます。キーと値はどちらも文字列型である必要があります。

例：

```json
{
  "exp": 1654254601,
  "username": "emqx_u",
  "client_attrs": {
      "role": "admin",
      "sn": "10c61f1a1f47"
  }
}
```

## JWT認証の設定

デプロイメント画面で、**アクセス制御** - **拡張認証**をクリックし、**JWT認証設定**をクリックして新しい認証を作成します。

以下のように関連設定を完了できます。

**認証方式**で**JWT**を選択した場合：

- **JWT From**：クライアント接続リクエスト内のJWTの位置を指定します。選択肢は`password`または`username`（それぞれMQTTクライアントの`CONNECT`パケットの`Password`、`Username`フィールドに対応）
- **Algorithm**：JWTの暗号化方式を指定します。選択肢は`hmac-based`、`public-key`です。
  - `hmac-based`を選択した場合（JWTが対称鍵で署名・検証される、HS256、HS384、HS512をサポート）、以下も設定します。
    - **Secret**：署名検証に使う鍵。署名生成時と同じ鍵を指定します。
    - **Secret Base64 Encode**：EMQXが署名検証前に`Secret`をBase64デコードするかどうか。選択肢はTrue、False（デフォルトはFalse）。
  - `public-key`を選択した場合（JWTが秘密鍵で署名され、公開鍵で検証される。RS256、RS384、RS512、ES256、ES384、ES512をサポート）、以下も設定します。
    - **Public Key**：署名検証に使うPEM形式の公開鍵を指定します。
- **Disconnect After Expiration**：JWTの有効期限切れ後にクライアントを切断するかどうか。デフォルトで有効。
- **Payload**：カスタムクレームの検証を追加します。ユーザーはクレームのキーと期待値をそれぞれ指定可能で、`${clientid}`や`${username}`のプレースホルダーが使えます。キーはJWT内のクレームを特定し、値は実際のクレーム値と比較されます。

**JWTS**を認証方式に選択した場合：

上記に加え、以下も設定します。

- **JWKS Endpoint**：EMQXがJWKSを問い合わせるサーバーのエンドポイントアドレスを指定します。GETリクエストをサポートし、標準に準拠したJWKSを返す必要があります。
- **JWKS Refresh Interval**：JWKSの更新間隔（EMQXがJWKSを問い合わせる頻度）を秒単位で指定します。デフォルトは300秒。設定後、**作成**をクリックして設定を完了します。
- **Headers**：JWKSサーバーへのリクエストに含める追加のHTTPヘッダーを指定します。サーバーの要件に合わせてリクエストを適切にフォーマットするために使用します。キーと値のペアを追加可能です。例：
  - **Key**: `Accept`
  - **Value**: `application/json`

::: tip

- 専用版のデプロイメントの場合は、[VPCピアリング接続](./vpc_peering.md)を作成し、サーバーアドレスに内部ネットワークアドレスを使用してください。
- BYOC版のデプロイメントの場合は、パブリッククラウドコンソールでVPCピアリング接続を作成する必要があります。詳細は[BYOCデプロイメント作成 - VPCピアリング接続設定](../create/byoc.md#vpc-peering-connection-configuration)をご参照ください。サーバーアドレスには内部ネットワークアドレスを使用してください。
- 「Init resource failure!」というメッセージが表示された場合は、サーバーアドレスの正確性とセキュリティグループの開放状況を確認してください。

:::
