セキュリティ - はじめの一歩¶
あなたのバックエンド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
これは、OAuth2がusername
とpassword
を送信するために「フォームデータ」を使用するためです。
この例を実行します
$ 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」ボタンがあります。
そして、あなたのパスオペレーションの右上隅には、クリックできる小さなロックがあります。
クリックすると、username
とpassword
(およびその他のオプションフィールド)を入力する小さな認証フォームが表示されます
注意
フォームに何を入力しても、まだ機能しません。しかし、いずれ機能するようにします。
これはもちろん最終ユーザーのためのフロントエンドではありませんが、すべてのAPIを対話的に文書化するための素晴らしい自動ツールです。
これは、フロントエンドチーム(あなた自身であることもあります)によって使用できます。
サードパーティのアプリケーションやシステムによって使用できます。
そして、デバッグ、チェック、同じアプリケーションのテストのために、あなた自身によっても使用できます。
password
フロー¶
では、少し戻って、それがすべて何であるかを理解しましょう。
password
「フロー」は、OAuth2で定義されているセキュリティと認証を処理する方法(「フロー」)の1つです。
OAuth2は、バックエンドまたはAPIがユーザーを認証するサーバーから独立できるように設計されました。
しかし、この場合、同じFastAPIアプリケーションがAPIと認証を処理します。
では、この簡略化された視点から見てみましょう。
- ユーザーはフロントエンドで
username
とpassword
を入力し、Enter
を押します。 - フロントエンド(ユーザーのブラウザで実行されている)は、その
username
とpassword
を、私たちのAPIの特定のURL(tokenUrl="token"
で宣言されている)に送信します。 - APIは、その
username
とpassword
をチェックし、「トークン」で応答します(これらはまだ何も実装していません)。- 「トークン」とは、後でこのユーザーを検証するために使用できる、何らかのコンテンツを含む単なる文字列です。
- 通常、トークンは一定時間後に期限切れになるように設定されます。
- したがって、ユーザーは後でどこかの時点で再度ログインする必要があります。
- トークンが盗まれても、リスクは少なくなります。永久に機能する永続的なキーのようなものではありません(ほとんどの場合)。
- フロントエンドは、そのトークンを一時的にどこかに保存します。
- ユーザーはフロントエンドでクリックして、フロントエンドウェブアプリの別のセクションに移動します。
- フロントエンドは、APIからさらにデータを取得する必要があります。
- しかし、その特定のエンドポイントには認証が必要です。
- したがって、当社のAPIで認証するには、
Bearer
とトークンを組み合わせた値を持つAuthorization
ヘッダーを送信します。 - トークンに
foobar
が含まれている場合、Authorization
ヘッダーの内容はBearer foobar
になります。
FastAPIのOAuth2PasswordBearer
¶
FastAPIは、これらのセキュリティ機能を実装するために、さまざまな抽象化レベルでいくつかのツールを提供しています。
この例では、Bearerトークンを使用して、Passwordフローを持つOAuth2を使用します。これにはOAuth2PasswordBearer
クラスを使用します。
情報
"ベアラー"トークンは唯一の選択肢ではありません。
しかし、私たちのユースケースには最適です。
そして、あなたがOAuth2の専門家で、ニーズに合った別の選択肢がなぜあるのかを正確に知っている場合を除き、ほとんどのユースケースには最適かもしれません。
その場合、FastAPIもそれを構築するためのツールを提供しています。
OAuth2PasswordBearer
クラスのインスタンスを作成する際、tokenUrl
パラメーターを渡します。このパラメーターには、クライアント(ユーザーのブラウザで実行されているフロントエンド)がトークンを取得するためにusername
とpassword
を送信するために使用する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
で使用できます。
使用する¶
これで、Depends
でoauth2_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行の追加で、すでに何らかの原始的なセキュリティ形式を手に入れることができます。