🔐

JWT認証ってなんなん【Expressを用いた使用例】

2024/12/31に公開

JWTとは

JSON Web Tokenの略。
ユーザの認証と認可に使用されるトークンベースの認証方法

JWTによりユーザの識別情報や権限情報を含むトークンを生成し、
それをクライアント-サーバー間でやり取りすることで、安全に認証情報を保持するしくみ

JWTを使うメリット

ユーザ特有のIDと、共通の秘密鍵を組み合わせJWTを生成することで以下のようなメリットがある

  1. セキュリティの強化
    秘密鍵は、JWTトークンの署名と検証に使用されるため、トークンの改ざんを防止できる。
    正しい秘密鍵がなければ、トークンを生成したり安全性を保てない。

  2. ユーザーの識別
    JWTのペイロードにユーザーIDを含めることで、トークンを使用してリクエストを行う際にサーバーがユーザーを簡単に識別できる。
    これにより、各ユーザーに異なる権限やデータを提供することが可能になる。

  3. スケーラビリティ
    トークンベースの認証は、セッション管理を必要としないため、スケーラビリティに優れている。
    ユーザーのセッション情報をサーバー側で保持する必要がないため、分散システムやクラウド環境でも効率的に動作する。

  4. 権限の管理
    JWTのペイロードにユーザーの権限情報を含めることで、特定のリソースや機能へのアクセスを制限できる。認証と認可がシンプルかつ効率的に行われる。

JWTの基本構造

  1. ヘッダー(Header)
    トークンのタイプ(今回で言うところのJWT)と署名アルゴリズム(HMAC SHA256)などで構成
  2. ペイロード(Payload)
    ユーザ情報や権限などのクレーム(ID、発行日時、有効期限)が含まれる。
  3. 署名(Sinnature)
      ヘッダーとペイロードのハッシュを生成し、秘密鍵で署名する
      トークンの改ざんを防ぐことができる

Expressを用いて使ってみる

  1. jsonwebtokenの導入
yarn add jsonwebtoken
yarn add @types/jsonwebtoken --dev
  1. ユーザのIDと.envに設定した秘密鍵を用いてJWTを生成する
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';

dotenv.config();

export const generateJWT = (id: string): string => {
  const payload = { id }; // ユーザーIDをペイロードに含める
  const secret = process.env.JWT_SECRET; // 環境変数から秘密鍵を取得
  const options = { expiresIn: '30d' }; // トークンの有効期限を設定
  return jwt.sign(payload, secret, options); // トークンの生成
}
 const token = generateJWT(user.id)
  1. 生成したJWTを用いて認証を行う
    Postmanを用いる場合、
    JWT認証用APIのリクエストを追加
     ↓
     認可のAuth TypeをBearer Tokenに設定、
     生成したJWTをトークンの入力欄に貼り付け
     ↓
     実行すると、レスポンスボディに以下のように出る
"Bearer <JWTのトークン>"
  1. ヘッダーごとにJWTを送信して検証、有効なJWTである場合、ユーザ情報を取得する
    const bearer = req.headers.authorization
    if (!bearer) {
      res.status(401).json({ error: '認証が必要です' })
      return
    }
    //MEMO: Bearer <JWT>の間の を利用してJWTのみをtokenとして取得
    const [, token] = bearer.split(' ')
    res.json({
      token
    })
    if (!token) {
      res.status(401).json({ error: 'トークンが無効です' })
      return
    }
    try {
      //MEMO: 秘密鍵とJWTを用いて検証、JWTが有効であればデコードする
      const decoded = jwt.verify(token, process.env.JWT_SECRET)
      //MEMO: デコードされたトークンがオブジェクトであり、idプロパティを持っていることを確認
      if (typeof decoded === 'object' && decoded.id) {
        //MEMO: idプロパティを用いてUserテーブルからユーザ情報を取得
        const user = await User.findByPk(decoded.id, {
          attributes: ['id', 'name', 'email']
        })
        console.log(decoded, 'デコード');

        res.json(user)
      }
    } catch (error) {
      res.status(500).json({ error: 'サーバーエラーが発生しました' })
    }

3で取得できたレスポンスからJWTのみを取得

秘密鍵とJWTを用いて検証、JWTが有効であればデコードする

デコードされたトークンがオブジェクトであり、idプロパティを持っていることを確認

レスポンスにユーザ情報を渡す

Discussion