Contents
FastAPIとJWT認証の概要
Webアプリケーション開発において、ユーザー認証はセキュリティとユーザーエクスペリエンスの両面で不可欠な要素です。特にREST APIでは、認証情報の共有を避けながら信頼性のあるアクセス制御を行う必要があります。この記事では、FastAPIという高性能なPythonフレームワークにおいて、JSON Web Token(JWT)による認証機能の実装手順をステップバイステップで解説します。初心者でも理解しやすいように、コード例と具体的な操作手順を併記し、即戦力となる知識をお届けします。
FastAPIプロジェクト初期設定
FastAPIは開発効率が高く、TypeScriptやSwagger UIなどもサポートするため、バックエンドエンジニアの間で人気があります。認証機能を実装するにあたり、まずプロジェクトの基本構造を作成します。
必要なパッケージのインストール
FastAPIとJWTを扱うには以下のようなライブラリが必要です。
fastapi:フレームワーク本体uvicorn:ASGIサーバー(開発用)pyjwt:JWTトークンの発行・検証python-dotenv:環境変数の読み込み (オプション)
以下のコマンドでインストールしてください。
|
1 2 |
pip install fastapi uvicorn pyjwt python-dotenv |
基本的なアプリケーション構造
プロジェクトディレクトリにmain.pyを作成し、以下のようにして初期設定します。
|
1 2 3 4 5 6 7 8 |
from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"message": "FastAPI JWT認証の準備完了"} |
この構造を基盤として、認証が必要なエンドポイント(例:/loginや/protected)を追加していきます。
JWTトークン発行処理
ユーザーがログインする際、APIはJWTトークンを発行します。このトークンをクライアントが持つことで、後続のリクエストで認証されます。
トークン発行用のエンドポイント作成
以下に/loginというエンドポイントでトークンを発行するコードを示します。
環境変数でのSECRET_KEY管理推奨
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
from fastapi import Depends, FastAPI, HTTPException, status import jwt from datetime import datetime, timedelta from pydantic import BaseModel app = FastAPI() # 環境変数から取得(実装例) import os from dotenv import load_dotenv load_dotenv() SECRET_KEY = os.getenv("SECRET_KEY") ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 class TokenData(BaseModel): username: str | None = None def create_access_token(data: dict, expires_delta: timedelta | None = None): to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt @app.post("/login") def login(username: str, password: str): if username == "user" and password == "password": access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( data={"sub": username}, expires_delta=access_token_expires ) return {"access_token": access_token, "token_type": "bearer"} raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials", ) |
このコードでは、ユーザー名とパスワードを確認してトークンを作成し、HTTP 200 OKで返却します。環境変数を使用することでセキュリティ強化が可能です。
トークン検証ミドルウェア構築
認証が必要なエンドポイントに対しては、JWTの検証処理を自動的に実行するミドルウェアを設置します。これは依存性(Depends)機能を利用して実現できます。
ミドルウェアクラスの定義
以下のようにget_current_userという関数を作成し、トークンが有効かどうかを検証します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
from fastapi import Depends, FastAPI, HTTPException, status import jwt from datetime import datetime, timedelta from pydantic import BaseModel from typing import Optional app = FastAPI() # OAuth2認証スキームの定義 from fastapi.security import OAuth2PasswordBearer oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login") class TokenData(BaseModel): username: str | None = None def get_current_user(token: str = Depends(oauth2_scheme)): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise credentials_exception token_data = TokenData(username=username) except JWTError: raise credentials_exception return {"username": username} |
この関数は、Depends(get_current_user)のようにしてエンドポイントに適用されます。OAuth2認証スキームを定義し、トークンが有効であることを検証する仕組みになります。
セキュリティ注意点
JWT認証を実装する際には、いくつかのセキュリティ的な配慮が必要です。
暗号化アルゴリズムの選択比較表
|
1 2 3 4 5 6 7 8 9 10 |
| 項目 | HS256 | RS256 | |---------------|-------------------------------|-------------------------------| | **暗号方式** | 対称鍵(秘密鍵が必要) | 非対称鍵(公開鍵/秘密鍵) | | **安全性** | 中程度 | 高め | | **管理コスト**| 秘密鍵の保護が必須 | 公開鍵の配布が必要 | | **導入難易度**| 低い(シンプル) | 高い(証明書管理など必要) | | **用途例** | マイクロサービス内通信 | 外部認証・公開API | |
トークン有効期限の設定
- 有効期間:
ACCESS_TOKEN_EXPIRE_MINUTESのようなパラメータで調整。 - リフレッシュトークン:長期間利用する場合は、別途管理が必要です(本記事では省略)。
blockquote: トークン有効期限を過度に長く設定すると、セキュリティリスクが高まるため注意してください。また、リフレッシュトークンは単独で管理し、アクセストークンと分離する必要があります。
サンプルコード実装
ここまで解説した内容を組み合わせた全体像と、動作確認方法を示します。
全体像の確認(保護エンドポイント例)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
from fastapi import Depends, FastAPI, HTTPException, status import jwt from datetime import datetime, timedelta from pydantic import BaseModel app = FastAPI() # 環境変数から取得 import os SECRET_KEY = os.getenv("SECRET_KEY") ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 class TokenData(BaseModel): username: str | None = None oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login") def create_access_token(data: dict, expires_delta: timedelta | None = None): to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt @app.post("/login") def login(username: str, password: str): if username == "user" and password == "password": access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( data={"sub": username}, expires_delta=access_token_expires ) return {"access_token": access_token, "token_type": "bearer"} raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials", ) def get_current_user(token: str = Depends(oauth2_scheme)): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise credentials_exception except JWTError: raise credentials_exception return {"username": username} @app.get("/protected") def protected_route(current_user: dict = Depends(get_current_user)): return {"message": f"保護されたルートへアクセス可能: {current_user['username']}"} |
動作検証手順
- アプリケーション起動:
uvicorn main:app --reloadでサーバーを起動。 - トークン発行:Postmanやcurlで
POST /loginにアクセスし、トークンを取得。 - 認証付きリクエスト:取得したトークンを
Authorization: Bearer [トークン]ヘッダーに含めて、保護されたエンドポイントへリクエスト。
まとめ
- FastAPIではJWT認証を簡潔かつ効率的に実装できます。
- 初期設定とトークン発行処理は、PyJWTライブラリを活用して手軽に実装可能です。
- ミドルウェアによって自動でトークン検証を行うことで、セキュリティも確保できます。
- 暗号化アルゴリズムや有効期限の設定には注意が必要です。
この記事を参考にして、FastAPIアプリケーションの認証機能を実装し、ユーザーごとのアクセス制御を導入してみてください。