🔐
JWT認証ってなんなん【Expressを用いた使用例】
JWTとは
JSON Web Tokenの略。
ユーザの認証と認可に使用されるトークンベースの認証方法
JWTによりユーザの識別情報や権限情報を含むトークンを生成し、
それをクライアント-サーバー間でやり取りすることで、安全に認証情報を保持するしくみ
JWTを使うメリット
ユーザ特有のIDと、共通の秘密鍵を組み合わせJWTを生成することで以下のようなメリットがある
-
セキュリティの強化
秘密鍵は、JWTトークンの署名と検証に使用されるため、トークンの改ざんを防止できる。
正しい秘密鍵がなければ、トークンを生成したり安全性を保てない。 -
ユーザーの識別
JWTのペイロードにユーザーIDを含めることで、トークンを使用してリクエストを行う際にサーバーがユーザーを簡単に識別できる。
これにより、各ユーザーに異なる権限やデータを提供することが可能になる。 -
スケーラビリティ
トークンベースの認証は、セッション管理を必要としないため、スケーラビリティに優れている。
ユーザーのセッション情報をサーバー側で保持する必要がないため、分散システムやクラウド環境でも効率的に動作する。 -
権限の管理
JWTのペイロードにユーザーの権限情報を含めることで、特定のリソースや機能へのアクセスを制限できる。認証と認可がシンプルかつ効率的に行われる。
JWTの基本構造
- ヘッダー(Header)
トークンのタイプ(今回で言うところのJWT)と署名アルゴリズム(HMAC SHA256)などで構成 - ペイロード(Payload)
ユーザ情報や権限などのクレーム(ID、発行日時、有効期限)が含まれる。 - 署名(Sinnature)
ヘッダーとペイロードのハッシュを生成し、秘密鍵で署名する
トークンの改ざんを防ぐことができる
Expressを用いて使ってみる
- jsonwebtokenの導入
yarn add jsonwebtoken
yarn add @types/jsonwebtoken --dev
- ユーザの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)
- 生成したJWTを用いて認証を行う
Postmanを用いる場合、
JWT認証用APIのリクエストを追加
↓
認可のAuth TypeをBearer Tokenに設定、
生成したJWTをトークンの入力欄に貼り付け
↓
実行すると、レスポンスボディに以下のように出る
"Bearer <JWTのトークン>"
- ヘッダーごとに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