コンテンツへスキップ

セキュリティ - はじめの一歩

あなたのバックエンドAPIがあるドメインにあると想像してみましょう。

そして、別のドメイン、または同じドメインの別のパス(あるいはモバイルアプリケーション)にフロントエンドがあるとします。

そして、ユーザー名パスワードを使って、フロントエンドがバックエンドと認証する方法が欲しいとします。

FastAPIでそれを作成するためにOAuth2を使用できます。

しかし、あなたが必要なわずかな情報を見つけるためだけに、長くて完全な仕様を読む時間を節約しましょう。

セキュリティを処理するためにFastAPIが提供するツールを使用しましょう。

どのように表示されるか

まず、コードを使ってどのように動作するかを見てみましょう。それから何が起こっているのか理解するために戻ります。

main.py を作成する

例をファイル main.py にコピーしてください

from typing import Annotated

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

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
    return {"token": token}
🤓 その他のバージョンとバリアント
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from typing_extensions import Annotated

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
    return {"token": token}

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

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

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}

実行

情報

python-multipart パッケージは、pip install "fastapi[standard]" コマンドを実行すると、FastAPI とともに自動的にインストールされます。

しかし、pip install fastapi コマンドを使用すると、python-multipart パッケージはデフォルトでは含まれません。

手動でインストールするには、仮想環境を作成し、それをアクティブにしてから、次のようにインストールしてください

$ pip install python-multipart

これは、OAuth2usernamepasswordを送信するために「フォームデータ」を使用するためです。

この例を実行します

$ fastapi dev main.py

<span style="color: green;">INFO</span>:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

確認

対話式ドキュメントにアクセス: http://127.0.0.1:8000/docs

次のように表示されます

承認ボタン!

すでにきらびやかな新しい「Authorize」ボタンがあります。

そして、あなたのパスオペレーションの右上隅には、クリックできる小さなロックがあります。

クリックすると、usernamepassword(およびその他のオプションフィールド)を入力する小さな認証フォームが表示されます

注意

フォームに何を入力しても、まだ機能しません。しかし、いずれ機能するようにします。

これはもちろん最終ユーザーのためのフロントエンドではありませんが、すべてのAPIを対話的に文書化するための素晴らしい自動ツールです。

これは、フロントエンドチーム(あなた自身であることもあります)によって使用できます。

サードパーティのアプリケーションやシステムによって使用できます。

そして、デバッグ、チェック、同じアプリケーションのテストのために、あなた自身によっても使用できます。

password フロー

では、少し戻って、それがすべて何であるかを理解しましょう。

password 「フロー」は、OAuth2で定義されているセキュリティと認証を処理する方法(「フロー」)の1つです。

OAuth2は、バックエンドまたはAPIがユーザーを認証するサーバーから独立できるように設計されました。

しかし、この場合、同じFastAPIアプリケーションがAPIと認証を処理します。

では、この簡略化された視点から見てみましょう。

  • ユーザーはフロントエンドでusernamepasswordを入力し、Enterを押します。
  • フロントエンド(ユーザーのブラウザで実行されている)は、そのusernamepasswordを、私たちのAPIの特定のURL(tokenUrl="token"で宣言されている)に送信します。
  • APIは、そのusernamepasswordをチェックし、「トークン」で応答します(これらはまだ何も実装していません)。
    • 「トークン」とは、後でこのユーザーを検証するために使用できる、何らかのコンテンツを含む単なる文字列です。
    • 通常、トークンは一定時間後に期限切れになるように設定されます。
      • したがって、ユーザーは後でどこかの時点で再度ログインする必要があります。
      • トークンが盗まれても、リスクは少なくなります。永久に機能する永続的なキーのようなものではありません(ほとんどの場合)。
  • フロントエンドは、そのトークンを一時的にどこかに保存します。
  • ユーザーはフロントエンドでクリックして、フロントエンドウェブアプリの別のセクションに移動します。
  • フロントエンドは、APIからさらにデータを取得する必要があります。
    • しかし、その特定のエンドポイントには認証が必要です。
    • したがって、当社のAPIで認証するには、Bearerとトークンを組み合わせた値を持つAuthorizationヘッダーを送信します。
    • トークンにfoobarが含まれている場合、Authorizationヘッダーの内容はBearer foobarになります。

FastAPIOAuth2PasswordBearer

FastAPIは、これらのセキュリティ機能を実装するために、さまざまな抽象化レベルでいくつかのツールを提供しています。

この例では、Bearerトークンを使用して、Passwordフローを持つOAuth2を使用します。これにはOAuth2PasswordBearerクラスを使用します。

情報

"ベアラー"トークンは唯一の選択肢ではありません。

しかし、私たちのユースケースには最適です。

そして、あなたがOAuth2の専門家で、ニーズに合った別の選択肢がなぜあるのかを正確に知っている場合を除き、ほとんどのユースケースには最適かもしれません。

その場合、FastAPIもそれを構築するためのツールを提供しています。

OAuth2PasswordBearerクラスのインスタンスを作成する際、tokenUrlパラメーターを渡します。このパラメーターには、クライアント(ユーザーのブラウザで実行されているフロントエンド)がトークンを取得するためにusernamepasswordを送信するために使用するURLが含まれます。

from typing import Annotated

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

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
    return {"token": token}
🤓 その他のバージョンとバリアント
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from typing_extensions import Annotated

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
    return {"token": token}

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

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

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}

ヒント

ここでtokenUrl="token"は、まだ作成していない相対URLtokenを指します。相対URLなので、./tokenと同じです。

相対URLを使用しているため、APIがhttps://example.com/にある場合、https://example.com/tokenを参照します。しかし、APIがhttps://example.com/api/v1/にある場合、https://example.com/api/v1/tokenを参照します。

相対URLを使用することは、プロキシの背後のような高度なユースケースでもアプリケーションが機能し続けることを保証するために重要です。

このパラメータは、そのエンドポイント/パスオペレーションを作成しませんが、URL/tokenがクライアントがトークンを取得するために使用すべきものであることを宣言します。この情報はOpenAPIで使用され、その後の対話型APIドキュメントシステムで使用されます。

間もなく、実際のパス操作も作成します。

情報

非常に厳格な「Pythonista」であれば、token_urlではなくtokenUrlというパラメータ名が気に入らないかもしれません。

これは、OpenAPI仕様と同じ名前を使用しているためです。これにより、これらのセキュリティスキームについてさらに調査する必要がある場合、コピーアンドペーストするだけで詳細情報を検索できます。

oauth2_scheme変数はOAuth2PasswordBearerのインスタンスですが、「呼び出し可能」でもあります。

次のように呼び出すことができます

oauth2_scheme(some, parameters)

したがって、Dependsで使用できます。

使用する

これで、Dependsoauth2_schemeを依存関係に渡すことができます。

from typing import Annotated

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

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
    return {"token": token}
🤓 その他のバージョンとバリアント
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from typing_extensions import Annotated

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
    return {"token": token}

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

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

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}

この依存関係は、パス操作関数のパラメーターtokenに割り当てられるstrを提供します。

FastAPIは、この依存関係を使用して、OpenAPIスキーマ(および自動APIドキュメント)に「セキュリティスキーム」を定義できることを認識します。

技術的な詳細

FastAPIは、OpenAPIにセキュリティスキームを定義するために(依存関係で宣言された)OAuth2PasswordBearerクラスを使用できることを知っています。なぜなら、これはfastapi.security.oauth2.OAuth2を継承し、さらにfastapi.security.base.SecurityBaseを継承しているからです。

OpenAPI(および自動APIドキュメント)と統合するすべてのセキュリティユーティリティはSecurityBaseを継承しており、これがFastAPIがそれらをOpenAPIに統合する方法を知る方法です。

それが何をするか

リクエストでAuthorizationヘッダーを探しに行き、値がBearerとトークンであるかどうかを確認し、トークンをstrとして返します。

Authorizationヘッダーがない場合、または値にBearerトークンがない場合、直接401ステータスコードエラー(UNAUTHORIZED)で応答します。

エラーを返すためにトークンが存在するかどうかを確認する必要すらありません。関数が実行された場合、そのトークンにstrが含まれていることを確認できます。

対話型ドキュメントで既に試すことができます

まだトークンの有効性を確認していませんが、それは既に始まりです。

まとめ

したがって、わずか3〜4行の追加で、すでに何らかの原始的なセキュリティ形式を手に入れることができます。