Python | Web フレームワーク:認証(Bearer)

Python
スポンサーリンク

概要(Bearer 認証=「トークンを見せて通る入館証チェック」)

Bearer 認証は、

「HTTP ヘッダに“トークン”を入れて送り、
サーバー側がそのトークンを確認して OK なら処理を続行する」

というタイプの認証方式です。

イメージとしては「入館証バッジ」です。
ログインなどで一度発行されたトークンを、
その後の API 呼び出しのたびにヘッダに付けて送ります。

この記事では FastAPI を例に、

Bearer 認証の考え方
Authorization ヘッダの中身
FastAPI での実装(超シンプル版 → OAuth2PasswordBearer 版)
トークン検証のポイントとエラーハンドリング

を、初心者向けにかみ砕いて解説します。


Bearer 認証の基本イメージ(Authorization ヘッダの中身)

「トークンを持っている人だけが通れるゲート」を作る

Bearer 認証では、クライアントは HTTP リクエストのヘッダに

Authorization: Bearer <トークン文字列>

という形でトークンを送ります。

例としては、こんなイメージです。

GET /protected HTTP/1.1
Host: example.com
Authorization: Bearer abc123xyz...

サーバー側は、これを見て

Authorization ヘッダはついているか
「Bearer 」で始まっているか
トークン文字列が有効なものか(期限切れじゃないか、改ざんされてないか)

をチェックします。

OK なら「認証済みユーザー」として処理を進め、
ダメなら 401(Unauthorized)などのエラーを返します。

ここで大事なのは、「トークンそのものが身分証」だという感覚です。
トークンを知っている人=ログイン済みユーザー(とみなす)
なので、トークンが漏れるとやばい、という点も頭に置いておいてください。


FastAPI での超シンプルな Bearer 認証(自作 dependency 版)

まずは「ヘッダからトークンを取り出してチェックする」だけやってみる

FastAPI では、認証は dependency(依存関係)として実装するのが基本です。
まずは超シンプルな「固定トークン」をチェックする例から。

from fastapi import FastAPI, Request, HTTPException, status

app = FastAPI()

API_TOKEN = "SUPER_SECRET_TOKEN"

async def verify_token(request: Request):
    auth_header = request.headers.get("Authorization")
    if not auth_header:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authorization header missing",
        )

    try:
        scheme, token = auth_header.split(" ")
    except ValueError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid Authorization header format",
        )

    if scheme.lower() != "bearer":
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authorization scheme must be Bearer",
        )

    if token != API_TOKEN:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token",
        )

    return token
Python

この verify_token が「Bearer トークン認証」を行う小さな関数です。

やっていることを順番に整理します。

Authorization ヘッダを取り出す。なければ 401。
スペース区切りで schemetoken に分解する。
scheme が Bearer かどうかを見る。違えば 401。
token がサーバー側の想定(ここでは固定の API_TOKEN)と一致するかチェックする。一致しなければ 401。

これをエンドポイントで dependency として使います。

from fastapi import Depends

@app.get("/protected")
async def protected_route(token: str = Depends(verify_token)):
    return {"message": "You are authorized", "token": token}
Python

このように書いておくと、

GET /protected にアクセスが来るたびに
FastAPI が先に verify_token を実行し、
ここでエラーが出たらエンドポイント本体は呼ばれません。

正しい Authorization ヘッダを付けて呼び出すと通り、
間違っていると 401 になる、という「ゲート」ができました。

本番では固定トークンではなく、
DB や JWT などを使いますが、
「ヘッダから取り出してチェックして、ダメなら 401 を投げる」という骨格は同じです。


OAuth2PasswordBearer を使った FastAPI 流 Bearer 認証

「/token でトークン発行 → それを Authorization に付けて使う」流れ

FastAPI には、Bearer 認証を楽にするための仕組みとして
OAuth2PasswordBearer というヘルパーが用意されています。

これは「どの URL でトークンを発行するか」を宣言し、
そのトークンを Authorization ヘッダから取り出してくれる dependency です。

簡略版のコードで流れを見てみます。

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

fake_token = "abc123"  # 本番では JWT などに置き換える

@app.post("/token")
async def login():
    return {"access_token": fake_token, "token_type": "bearer"}

@app.get("/me")
async def read_me(token: str = Depends(oauth2_scheme)):
    if token != fake_token:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token",
        )
    return {"username": "taro"}
Python

動きを分解します。

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
ここで、「トークンを発行するエンドポイントは /token ですよ」と宣言しています。
同時に、これが「Authorization ヘッダから Bearer トークンを取り出す dependency」にもなります。

/token エンドポイント
ここでは簡略化のため、固定トークン fake_token を返しています。
クライアントは、このエンドポイントに対してログイン情報を送って
access_token をもらう、という設計が一般的です。

/me エンドポイント
ここが「認証が必要な API」です。
token: str = Depends(oauth2_scheme) と書くことで、
FastAPI はヘッダから Bearer トークンを取り出して token に渡してくれます。
その token を検証して、ダメなら 401 を返します。

実際のアクセスの流れとしては、

クライアントが /token にログイン情報を送る
サーバーがトークン(例: “abc123″)を返す
クライアントは、そのトークンを Authorization: Bearer abc123 として /me に送る
サーバーはトークンをチェックして、OK ならユーザー情報を返す

という形になります。

OAuth2PasswordBearer がやってくれること、やらないこと

OAuth2PasswordBearer は、

Authorization ヘッダから Bearer トークン文字列を抜き出し
なければ 401 を返す

という「取り出し」部分をやってくれます。

一方で、

トークンの中身が正しいか
期限が切れていないか
どのユーザーのトークンか

といった「検証」そのものはやってくれません。

ここは自分で実装する必要があります。
多くの場合、JWT(JSON Web Token)を使って署名付きトークンを発行し、
それを verify する、という流れにします。


トークンの検証ロジックをどう書くか(重要な考え方)

単純な「文字列比較」から、JWT の検証へ

学習用や社内ツールなら、
「固定トークンかどうか」だけを見るシンプルな実装でも間に合います。

しかし、実務に近づけようとすると、

ユーザーごとに違うトークン
発行時間と有効期限
改ざんされていないかの署名

などを考える必要が出てきます。

ここでよく使われるのが JWT です。

例えば、PyJWT などを使って、

サーバー側で秘密鍵を持つ
ログイン時に、ユーザーIDや有効期限入りの JWT を発行
各 API で、その JWT を検証し、中からユーザー情報を取り出す

というパターンを組みます。

イメージだけ簡単に示すと、こんな感じです。

import jwt
from datetime import datetime, timedelta
from fastapi import HTTPException, status

SECRET_KEY = "VERY_SECRET"
ALGORITHM = "HS256"

def create_access_token(user_id: int):
    expire = datetime.utcnow() + timedelta(minutes=30)
    payload = {"sub": str(user_id), "exp": expire}
    return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)

def verify_access_token(token: str) -> int:
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        user_id = int(payload.get("sub"))
        return user_id
    except jwt.ExpiredSignatureError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token expired",
        )
    except jwt.InvalidTokenError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token",
        )
Python

この verify_access_token を、
さきほどの read_me の中で呼び出すイメージです。

@app.get("/me")
async def read_me(token: str = Depends(oauth2_scheme)):
    user_id = verify_access_token(token)
    return {"user_id": user_id}
Python

大事なのは、

「Bearer トークン認証」といっても、
本質は「トークンをどう作り、どう検証するか」の設計にある

ということです。
Bearer は単に「ヘッダに載せる形式」の話で、
中身の管理が勝負です。


認証を dependency として設計するメリット

エンドポイントから「認証の細かい話」を追い出す

FastAPI の良さのひとつは、認証を dependency として設計できることです。

例えば、「ログイン済みユーザー情報を返す dependency」を作っておくと便利です。

from fastapi import Depends

async def get_current_user(token: str = Depends(oauth2_scheme)):
    user_id = verify_access_token(token)
    # ここで DB からユーザー情報を取ってくる想定
    return {"id": user_id, "name": "Taro"}

@app.get("/me")
async def read_me(current_user: dict = Depends(get_current_user)):
    return current_user
Python

こうすると、/me の関数は、

現在のユーザー情報がほしい
認証が通らないなら、ここに来る前に 401 にしてほしい

という願いだけを書けばよく、
トークンの取り出しや検証、HTTPException の投げ方などは
全部 get_current_user に閉じ込められます。

この構造にすると、

認証が必要なエンドポイント
current_user: dict = Depends(get_current_user) を付けるだけ

という綺麗な形になります。


まとめ(Bearer 認証は「トークンの作り方・渡し方・確かめ方」を整えること)

Python Web フレームワーク(FastAPI)における Bearer 認証を整理すると、こうなります。

Bearer 認証は、Authorization ヘッダに Bearer <トークン> を載せて送り、サーバーがトークンを検証して通すかどうか決める方式で、「入館証バッジ」のようなイメージ。
FastAPI では、Request から自力で Authorization ヘッダを取り出して verify する方法と、OAuth2PasswordBearer を使って「ヘッダからトークンを取り出す部分」を任せる方法がある。
トークンの検証ロジック(有効期限・署名・ユーザーIDなど)は自分で実装する必要があり、実務では JWT を使って署名付きトークンを発行・検証するパターンがよく使われる。
認証処理を dependency(get_current_user など)として切り出しておくと、エンドポイント側は「認証済みユーザー情報がほしい」という宣言だけ書けばよくなり、コードの見通しが良くなる。

タイトルとURLをコピーしました