JWTを使って認証・認可の仕組みを構築しよう

## はじめに
現代のウェブ開発では、ユーザー認証は欠かせない要素です。ログインが必要なサービスや、ユーザーごとに異なるデータへアクセスするアプリなど、多くのケースで「この人は本当に本人なのか？」を確かめる必要があります。そこでよく使われる仕組みの一つが、**JWT（JSON Web Token）** です。

ここでは、JWTを活用した認証・認可の基本的な流れや注意点、実装のポイントを、なるべくわかりやすい言葉で紹介します。

### （ここで宣伝）
弊社の開発プロジェクトに参画いただける方（フリーランスのエンジニアの方）を大募集しております！

現在、様々な開発プロジェクトがあり、開発の部分に携わっていただける方をどうにか探しているところです。
サーバーサイド、フロントエンド、インフラ等、どんなポジションでも歓迎しております。
要件定義や設計のフェーズから携ることのできる案件も結構あり、自分の得意な分野を活かせるプロジェクトが絶対あるかと思います！

興味のある方は是非下記フォームに回答して、弊社の開発プロジェクトに参加してください！
（詳細は回答いただいた後、ご連絡いたします）

👉 フォームは[こちら](https://docs.google.com/forms/d/e/1FAIpQLSfY9lckFjAJ_Ddc4rdHDSIFoV4NFHw1q2HriK2aa_jvVhp8QQ/formResponse)

ご回答のほど、お待ちしております！

## JSON Web Token（JWT）とは？
**JWT（JSON Web Token）** は、JSON形式のデータを符号化したトークンで、認証や認可などの情報を安全に持ち運ぶために使われます。

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

1. ヘッダー（Header）：署名アルゴリズムやトークンタイプが記載
2. ペイロード（Payload）：ユーザーIDや有効期限などのクレーム（情報）が含まれる
3. 署名（Signature）：ヘッダーとペイロードを秘密鍵で署名して、改ざんされていないことを保証

この3つを.で繋いだ文字列がJWTです。トークンはBase64URLエンコードされ、人間が読めば中身はJSON（ペイロード）として解読可能です。ただし署名部分があるため、勝手に中身を変えても署名が合わず、サーバー側で無効と判断されます。

JWTの例：

```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```

## JWTでの認証の基本的な流れ
1. ログイン要求：ユーザーがID/パスワードをサーバーに送信
2. トークン発行：サーバーがユーザーを認証し、JWTを生成してクライアントへ返す
3. トークンの保存：クライアント側でJWTを保存（ローカルストレージ等）
4. リクエスト時のトークン送信：クライアントは、リクエストのヘッダー（通常Authorization: Bearer <トークン>）にJWTをつけて送信
5. 検証と認可：サーバーは受け取ったJWTを秘密鍵で検証し、ユーザー情報（IDやロール）を取り出す。その上で、このユーザーがアクセスできる機能やデータか確認し、OKならレスポンスを返す。

## 簡単な実装例（Node.js + Express）
以下は、Node.js + ExpressでJWT認証を導入する簡単な例です。

### 必要なパッケージインストール

```bash
npm install express jsonwebtoken body-parser
```

### サンプルコード

```js
const express = require("express");
const jwt = require("jsonwebtoken");
const bodyParser = require("body-parser");

const app = express();

// トークン署名用シークレットキー（本番では.envなどで隠す）
const SECRET_KEY = "your-secret-key";

app.use(bodyParser.json());

// ユーザーデータ（実際のアプリではデータベースを使用）
const users = [{ username: "user1", password: "password1" }];

// ログインエンドポイント
app.post("/login", (req, res) => {
  const { username, password } = req.body;
  const user = users.find((u) => u.username === username && u.password === password);

  if (user) {
    // JWTトークンを生成
    const token = jwt.sign({ username }, SECRET_KEY, { expiresIn: "1h" });
    res.json({ token });
  } else {
    res.status(401).json({ message: "認証失敗" });
  }
});

// 保護されたルート
app.get("/protected", (req, res) => {
  const token = req.headers["authorization"];

  if (!token) {
    return res.status(403).json({ message: "トークンが必要です" });
  }

  try {
    const decoded = jwt.verify(token.split(" ")[1], SECRET_KEY);
    res.json({ message: "アクセス成功", user: decoded });
  } catch (err) {
    res.status(401).json({ message: "無効なトークン" });
  }
});

// サーバー起動
app.listen(3000, () => {
  console.log("Server running on http://localhost:3000");
});
```

### 動作確認
#### ログインリクエスト
リクエスト例（POST `/login`）：
```json
{
  "username": "user1",
  "password": "password1"
}
```

レスポンス例：
```json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

#### 認証付きリクエスト
リクエスト例（GET `/protected`）：
```
Authorization: Bearer <JWTトークン>
```
レスポンス例
```json
{
  "message": "アクセス成功",
  "user": {
    "username": "user1",
    "iat": 1616148319,
    "exp": 1616151919
  }
}
```

## セキュリティ上の注意点
下記実装する上での、セキュリティ上の注意点を記載します。

* 有効期限設定：短い有効期限を設け、長期間使われ続けるトークンを避けましょう
* リフレッシュトークン：リフレッシュトークンをDBで管理し、万が一トークンが漏れたら無効化できるようにしましょう
* 適切なスコープやロールの付与：JWT内にユーザーの権限やロールを入れて、サーバー側でアクセス許可をコントロールしましょう

## まとめ
JWTは、認証と認可をシンプルかつ効率的に実現するための手段です。セッションレスな運用が可能で、スケールしやすく、マイクロサービス間での認証などにも活用されています。
「認証と認可」はハードルが高いように思いますが、上記のJWTの仕組みを抑えれば、以外とシンプルに実装することができるかと思います。
この記事を参考にして、認証・認可を確実に実装していただければ幸いです！

## おわりに

繰り返しになりますが、弊社ではフリーランスエンジニアの方を募集しております！

熱意を持って新しいプロジェクトに挑戦したい方、様々な大規模開発プロジェクトに興味のある方は、下記フォームにてあなたの経歴をご回答ください！

👉 フォームは[こちら](https://docs.google.com/forms/d/e/1FAIpQLSfY9lckFjAJ_Ddc4rdHDSIFoV4NFHw1q2HriK2aa_jvVhp8QQ/formResponse)

たくさんのご応募、お待ちしております！