1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JWT徹底解説:FastAPIで純粋なPythonだけで認証を実装する

Posted at

Group192.png

Leapcell: The Best of Serverless Web Hosting

FastAPIでのJWTを用いた認証の実装

はじめに

現代のウェブアプリケーション開発において、認証はシステムのセキュリティを確保する上で重要な要素です。JSON Web Token (JWT) は、JSONに基づくオープン標準 (RFC 7519) として、コンパクトでセキュアな特性から、分散型サイトのシングルサインオン (SSO) シナリオで広く利用されています。FastAPIはPythonベースの高性能なウェブフレームワークです。この記事では、与えられたJWT実装コードを用いて、FastAPIでJWTを使った認証を行う方法を紹介します。

JWTの基本

JWTとは?

JSON Web Token (JWT) は、ネットワークアプリケーション環境間でクレームを伝達するために設計されています。コンパクトでセキュアであり、分散型サイトのシングルサインオンシナリオに特に適しています。JWTのクレームは、認証されたユーザーのID情報をアイデンティティプロバイダとサービスプロバイダ間で伝達し、リソースサーバからリソースを取得するためによく使われ、またビジネスロジックで必要な他のクレーム情報を追加することもできます。このトークンは、直接認証に使用したり、暗号化したりすることができます。

JWTの構成

JWTは3つのセグメント情報から構成されています。

  • ヘッダー:通常、ファイルタイプと暗号化アルゴリズムを宣言します。
  • ペイロード:JSON形式で送信するデータを記録します。
  • 署名:検証に使用する署名です。

base64UrlEncodeメソッド

JWTを生成する際には、署名に base64UrlEncode メソッドが使用されます。このメソッドの基本的な機能は以下の通りです。

  1. UTF-8でエンコードされた文字列 s1 を入力します。
  2. 文字列 s1 をbase64でエンコードして文字列 s2 を取得します。
  3. s2 の末尾に等号がある場合、末尾のすべての等号を削除します。
  4. s2 の中にプラス記号 (+) がある場合、すべてのプラス記号をマイナス記号 (-) に置き換えます。
  5. s2 の中にスラッシュ (/) がある場合、すべてのスラッシュをアンダースコア (_) に置き換えます。

以下は、このメソッドを実装するPythonコードです。

import hmac
import base64
from hashlib import sha256
from urllib import parse as urlp

def b64url(str1):
    if type(str1) == str:
        return str(base64.b64encode(str1.encode('utf-8')), encoding="utf-8").strip('=').replace('+', '-').replace('/', '_')
    elif type(str1) == bytes:
        return str(base64.b64encode(str1), encoding="utf-8").strip('=').replace('+', '-').replace('/', '_')
    else:
        raise TypeError("The type of given argument must be string or bytes")

HS256で暗号化されたJWTの生成

以下は、上記の b64url 関数を使用してHS256で暗号化されたJWTを生成するコードです。

# Enter Your Infomation Here ...
header = b64url('{"alg":"HS256","typ":"JWT"}')
payload = b64url('{"sub":"1234567890","name":"John Doe","iat":1516239022}')
secret = 'happynewyear'.encode('utf-8')
# ###

sig = b64url(hmac.new(
    secret, (header + '.' + payload).encode('utf-8'), digestmod=sha256
).digest())

jwt = header + '.' + payload + '.' + sig
print(jwt)

FastAPIでのJWTの統合

依存関係のインストール

始める前に、fastapiuvicorn をインストールする必要があります。以下のコマンドを使用してインストールできます。

pip install fastapi uvicorn

実装手順

1. FastAPIアプリケーションの作成

from fastapi import FastAPI, HTTPException, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import hmac
import base64
from hashlib import sha256

app = FastAPI()

# Define the b64url function
def b64url(str1):
    if type(str1) == str:
        return str(base64.b64encode(str1.encode('utf-8')), encoding="utf-8").strip('=').replace('+', '-').replace('/', '_')
    elif type(str1) == bytes:
        return str(base64.b64encode(str1), encoding="utf-8").strip('=').replace('+', '-').replace('/', '_')
    else:
        raise TypeError("The type of given argument must be string or bytes")

# Secret key
SECRET_KEY = 'happynewyear'.encode('utf-8')

# Function to generate JWT
def generate_jwt():
    header = b64url('{"alg":"HS256","typ":"JWT"}')
    payload = b64url('{"sub":"1234567890","name":"John Doe","iat":1516239022}')
    sig = b64url(hmac.new(
        SECRET_KEY, (header + '.' + payload).encode('utf-8'), digestmod=sha256
    ).digest())
    return header + '.' + payload + '.' + sig

# Function to verify JWT
def verify_jwt(token: str):
    parts = token.split('.')
    if len(parts) != 3:
        return False
    header, payload, received_sig = parts
    new_sig = b64url(hmac.new(
        SECRET_KEY, (header + '.' + payload).encode('utf-8'), digestmod=sha256
    ).digest())
    return new_sig == received_sig

# Define the JWT verification dependency
class JWTBearer(HTTPBearer):
    def __init__(self, auto_error: bool = True):
        super(JWTBearer, self).__init__(auto_error=auto_error)

    async def __call__(self, request):
        credentials: HTTPAuthorizationCredentials = await super(JWTBearer, self).__call__(request)
        if credentials:
            if not credentials.scheme == "Bearer":
                raise HTTPException(status_code=401, detail="Invalid authentication scheme.")
            if not verify_jwt(credentials.credentials):
                raise HTTPException(status_code=401, detail="Invalid or expired token.")
            return credentials.credentials
        else:
            raise HTTPException(status_code=401, detail="Invalid authorization code.")

2. ログインインターフェイスの作成

# Simulated login interface
@app.post("/login")
def login():
    token = generate_jwt()
    return {"access_token": token, "token_type": "bearer"}

3. 保護されたインターフェイスの作成

# Protected interface
@app.get("/protected", dependencies=[Depends(JWTBearer())])
def protected_route():
    return {"message": "This is a protected route."}

アプリケーションの実行

上記のコードを main.py として保存し、ターミナルで以下のコマンドを実行してアプリケーションを起動します。

uvicorn main:app --reload

インターフェイスのテスト

  • ログインインターフェイスcurl やPostmanなどのツールを使用して、http://localhost:8000/login にPOSTリクエストを送信すると、JWTが返されます。
curl -X POST http://localhost:8000/login
  • 保護されたインターフェイス:返されたJWTを使用して、http://localhost:8000/protected にGETリクエストを送信し、リクエストヘッダーに Authorization: Bearer <your_token> を追加します。
curl -X GET http://localhost:8000/protected -H "Authorization: Bearer <your_token>"

まとめ

以上の手順を通じて、FastAPIでJWTを使った認証を行う方法を学びました。JWTは、ユーザーIDを安全かつ便利に管理する方法を提供し、分散型システム間の認証をより効率的にします。実際のアプリケーションでは、必要に応じてJWTの生成と検証ロジックを調整することができます。例えば、有効期限を追加したり、クレームをカスタマイズしたりすることができます。

Leapcell: The Best of Serverless Web Hosting

最後に、Pythonサービスのデプロイに最適なプラットフォームをおすすめします。Leapcell

brandpic7.png

🚀 好きな言語で構築

JavaScript、Python、Go、またはRustで簡単に開発できます。

🌍 無制限のプロジェクトを無料でデプロイ

使用した分だけ料金がかかり、リクエストがなければ料金はかかりません。

⚡ 従量制、隠れたコストは一切ありません

アイドル料金はなく、シームレスなスケーラビリティがあります。

Frame3-withpadding2x.png

📖 Explore Our Documentation

🔹 Twitterでフォロー:@LeapcellHQ

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?