1636488120
FastAPIは比較的新しいPythonフレームワークであり、アプリケーションを非常に迅速に作成できます。このフレームワークを使用すると、組み込みモジュールを使用してAPIリクエストデータをシームレスに読み取ることができ、Flaskの軽量な代替手段となります。
この記事では、の機能について説明FastAPI
し、基本的なAPIを設定し、Auth0を使用してエンドポイントを保護し、開始するのがいかに簡単かを学びます。
始める前に、以下のビデオを再生して、このブログ投稿の内容をビデオ形式でチェックすることもできます:👇
FastAPIを使用してビルドを開始する前に、Pythonと無料のAuth0アカウントが必要です。ここでサインアップできます。3.8.2
そのPythonバージョンをインストールし、Auth0アカウントを取得している場合は、新しいFastAPI
アプリケーションを作成できます。まず、開発する新しいディレクトリを作成します。この例では、というディレクトリと;というサブフォルダを作成します。このサブフォルダーは、コードが存在する場所です。fastapi-exampleapplication
フォルダで、次のコマンドを使用して仮想環境を作成します。fastapi-example
python3 -m venv .env
これにより仮想環境が作成され、依存関係が残りのコンピューターライブラリから分離されます。つまり、他のPythonプロジェクトに影響を与える可能性のある、ライブラリと依存関係でグローバル名前空間を汚染することはありません。
仮想環境を作成したら、それをアクティブ化する必要があります。Unixベースのオペレーティングシステムの場合、コマンドは次のとおりです。
source .env/bin/activate
別のオペレーティングシステムを使用している場合は、このドキュメントページで環境をアクティブ化する方法のリストを見つけることができます。仮想環境をアクティブ化した後、使用するパッケージをインストールできます:FastAPI
、uvicorn server、pyjwt、およびupdate pip
:
pip install -U pip
pip install fastapi uvicorn 'pyjwt[crypto]'
すべてのライブラリがインストールされたので、フォルダ内にファイルを作成できます。それはあなたのAPIコードが住む場所です。の内容は次のようになります。main.pyapplicationmain.py
"""main.py
Python FastAPI Auth0 integration example
"""
from fastapi import FastAPI
# Creates app instance
app = FastAPI()
@app.get("/api/public")
def public():
"""No access token required to access this route"""
result = {
"status": "success",
"msg": ("Hello from a public endpoint! You don't need to be "
"authenticated to see this.")
}
return result
これを分解しましょう:
FastAPI
ライブラリをインポートしますFastAPI()
@app.getGET
最後に、と呼ばれるパス操作関数があります。これは、そのルートが呼び出されるたびに実行される関数であり、ウェルカムメッセージを含む辞書を返します。public()
最初のエンドポイントコードを取得したので、サーバーを起動して実行するには、プロジェクトのルートディレクトリで次のコマンドを実行します。
uvicorn application.main:app --reload
サーバーが実行されている状態で、次の画像に示すように、最初のエンドポイント用に自動生成されたドキュメントを表示するために移動できます。http://127.0.0.1:8000/docs
または、を使用して、新しいターミナルウィンドウで最初のリクエストを行うこともできますcURL
。古いバージョンのオペレーティングシステムを使用しているWindowsユーザーの場合は、次のコマンドを実行する前にcurlをインストールする必要があることに注意してください。
curl -X 'GET' \
--url http://127.0.0.1:8000/api/public
そして、これと同じように行ったリクエストの結果として、JSONが表示されるはずです。
{
"status": "success",
"msg": "Hello from a public endpoint! You don't need to be authenticated to see this."
}
簡単にするcURL
ために、この投稿の残りの部分ではを使用します。
ベースAPIサーバーがセットアップされたので、ファイルにもう1つのエンドポイントを追加します。このアプリケーションでは、すべての人が利用できるルートと、Auth0から取得するアクセストークンを使用して自分だけがアクセスできるルートがあります。main.pyGET /api/publicGET /api/private
次に、ファイルを更新する必要があります。インポートセクションに変更する必要があるものは次のとおりです。main.py
Depends
からfastapi
FastAPI依存性注入システムのモジュールと、HTTPBearer
からクラスをインポートする必要があります。これは、ベアラートークンを使用した承認ヘッダー用の組み込みのセキュリティスキームです。fastapi.security
HTTPBearer
。これはBearer
、プライベートエンドポイントに対して行われる各リクエストでトークンを含む承認ヘッダーの存在を保証するために使用されます。トークン知らせるトークンのベアラがいることをAPIにアクセスするAPIを承認し、承認の際に付与された範囲で指定された特定のアクションを実行されています。
インポートを更新する以外に、プライベートエンドポイントを実装する必要があります。エンドポイントはまた、受け入れるの要求を、ここでのコードは何である今のようになっています。/api/privateGETmain.py
"""main.py
Python FastAPI Auth0 integration example
"""
from fastapi import Depends, FastAPI # 👈 new imports
from fastapi.security import HTTPBearer # 👈 new imports
# Scheme for the Authorization header
token_auth_scheme = HTTPBearer() # 👈 new code
# Creates app instance
app = FastAPI()
@app.get("/api/public")
def public():
"""No access token required to access this route"""
result = {
"status": "success",
"msg": ("Hello from a public endpoint! You don't need to be "
"authenticated to see this.")
}
return result
# new code 👇
@app.get("/api/private")
def private(token: str = Depends(token_auth_scheme)):
"""A valid access token is required to access this route"""
result = token.credentials
return result
Depends
このクラスは、特定のエンドポイントは、関数、クラス、またはインスタンスに対して受信する各要求を評価する責任があります。この場合、HTTPBearer
ベアラートークンを使用して承認ヘッダーのリクエストをチェックするスキームに対してリクエストを評価します。
FastAPI
依存性注入がどのように機能するかについての詳細は、そのドキュメントに記載されています。
これで、プライベートエンドポイントが受信したトークンを返します。トークンが提供されていない場合は、403 Forbidden
あなたがいることを示す詳細を含むステータスコードが返されます"Not authenticated"
。--reload
サーバーの実行中にフラグを使用したため、コマンドを再実行する必要はありません。uvicorn
ファイルを保存するたびに、変更を取得してサーバーを更新します。次に、エンドポイントにリクエストを送信して、その動作を確認します。まず、認証ヘッダーを渡さずにリクエストを作成しましょう。GET /api/private
curl -X 'GET' \
--url 'http://127.0.0.1:8000/api/private'
# {"detail": "Not authenticated"}
そして今、認証ヘッダーを使用してリクエストを行ったが、トークン値としてランダムな文字列を使用した場合、結果として同じランダムな値が表示されるはずです。
curl -X 'GET' \
--url 'http://127.0.0.1:8000/api/private' \
--header 'Authorization: Bearer FastAPI is awesome'
# "FastAPI is awesome"
ご覧のとおり、承認ヘッダーの値として任意の文字列を受け入れるため、エンドポイントは保護されていません。承認ヘッダーを受け取るだけでは不十分です。また、誰かがエンドポイントにアクセスできるようにするには、ベアラートークンの値を確認する必要があります。その動作を修正しましょう。
エンドポイントでトークンを検証する準備が整う前に、Auth0でAPIを設定する必要があります。このAPIを設定すると、Auth0に必要ないくつかの情報(オーディエンス、クライアントID、クライアントシークレット)にアクセスできます。
また、サーバー内からその情報にアクセスできる必要があります。そこで、構成ファイルが役立ちます。.config
プロジェクトのルートで呼び出される構成ファイルを作成する必要があります。これは、.config
ファイルが以下のように見えるはずです。それに応じて値を更新することを忘れないでください。
# .config
[AUTH0]
DOMAIN = your.domain.auth0.com
API_AUDIENCE = your.api.audience
ALGORITHMS = RS256
ISSUER = https://your.domain.auth0.com/
この構成は、トークン検証段階でAuth0構成設定をチェックするパズルの最初のピースです。従うべきもう1つの良いルールは、環境変数を含む構成ファイルをソースコードにコミットしないことです。これを防ぐに.gitignore
は、プロジェクトのルートにファイルを作成し、その.config
ファイルをエントリとして追加する必要があります。
# .gitignore
.config
これでFastAPI
サーバーにルートができましたが、まだ保護されていません。リクエストに認証ヘッダーがあるかどうかのみをチェックします。つまり、プロセスのステップが欠落しています。アクセストークンを検証する必要があります。これを行うには、Auth0のアクセストークンがJWTであるため、JWTを検証するためのすべての手順を実行するオブジェクトを作成する必要があります。GET /api/private
責任をルーティング定義から分離するには、フォルダー内に呼び出される新しいファイルを作成して、アクセストークンの検証や構成情報の読み取りなど、すべてのユーティリティコードを保持する必要があります。utils.pyapplication
Pythonos
ライブラリ、PyJWT
およびconfigparser
ライブラリをインポートすることから始めます。OSライブラリを使用すると、環境変数にアクセスできます。JWTライブラリは、JWTをチェックおよび検証するための関数を提供します。ConfigParser
名前の由来のライブラリのクラスは、Pythonが.config
以前に作成したファイルにある構成設定を読み取る方法を提供します。そして、インポート後に最初に持っているのは、と呼ばれる関数です。これを以下に示します。set_up()
"""utils.py
"""
import os
import jwt
from configparser import ConfigParser
def set_up():
"""Sets up configuration for the app"""
env = os.getenv("ENV", ".config")
if env == ".config":
config = ConfigParser()
config.read(".config")
config = config["AUTH0"]
else:
config = {
"DOMAIN": os.getenv("DOMAIN", "your.domain.com"),
"API_AUDIENCE": os.getenv("API_AUDIENCE", "your.audience.com"),
"ISSUER": os.getenv("ISSUER", "https://your.domain.com/"),
"ALGORITHMS": os.getenv("ALGORITHMS", "RS256"),
}
return config
この関数は、ファイルを読み取り、辞書のように機能する構成オブジェクトを作成する役割を果たします。このサンプルコードは環境変数でも実行できるように準備されているため、関数はデフォルトでファイルを読み取ろうとします。この動作は、環境変数を他の値に設定することで変更できます。その場合、上記の句で確認できるすべての環境変数を読み取ることで辞書が作成されます。set_up().configset_up().configENVelse
パズルの次のピースは、魔法が起こる場所です。VerifyToken
JWTトークンの検証を処理するクラスを作成します。
# paste the code 👇 after the set_up() function in the utils.py file
class VerifyToken():
"""Does all the token verification using PyJWT"""
def __init__(self, token):
self.token = token
self.config = set_up()
# This gets the JWKS from a given URL and does processing so you can
# use any of the keys available
jwks_url = f'https://{self.config["DOMAIN"]}/.well-known/jwks.json'
self.jwks_client = jwt.PyJWKClient(jwks_url)
def verify(self):
# This gets the 'kid' from the passed token
try:
self.signing_key = self.jwks_client.get_signing_key_from_jwt(
self.token
).key
except jwt.exceptions.PyJWKClientError as error:
return {"status": "error", "msg": error.__str__()}
except jwt.exceptions.DecodeError as error:
return {"status": "error", "msg": error.__str__()}
try:
payload = jwt.decode(
self.token,
self.signing_key,
algorithms=self.config["ALGORITHMS"],
audience=self.config["API_AUDIENCE"],
issuer=self.config["ISSUER"],
)
except Exception as e:
return {"status": "error", "message": str(e)}
return payload
ここでの手順を理解するために、このクラスを分解してみましょう。
__init__()
token
、VerifyToken
クラスが必要とするパラメーターを指定する役割を果たします。set_up()
JWKS
のPyJWKClient
fromを使用してファイルのパスを設定しPyJWT
ます。A JSONのWebキーセット、または略してJWKSは、トークンの署名を検証し、それが有効なトークンであることを保証するために必要な情報が含まれています。Auth0はOAuth2.0を実装しているため、トークンとそのプロパティの検証に使用される追加のメタデータを呼び出して取得できる「既知の」エンドポイントがあります。verify()
kid
トークンヘッダーに存在するクレーム)を使用して、JWKSから使用されるキーを取得してトークンの署名を検証します。考えられるエラーのいずれかによってこのステップが失敗した場合、エラーメッセージが返されます。前のセクションのプライベートエンドポイントでこのコードを使用する方法を見てみましょう。
最後のパズルのピースは、ファイルに作成したクラスをインポートして、エンドポイントで使用することです。変更する必要があるものは次のとおりです。utils.pyGET /api/private
VerifyToken
から、エンドポイントに移動します。また、エラーが発生した場合に詳細な応答を提供できるように、からResponse
クラスとstatus
オブジェクトをインポートする必要fastapi
があります。VerifyToken
クラスに渡し、メソッドの結果がエラーであるかどうかを確認して、エンドポイントを調整する必要があります。verify()
上記のすべての変更を加えたファイルは次のようになります。main.py
"""main.py
Python FastAPI Auth0 integration example
"""
from fastapi import Depends, FastAPI, Response, status # 👈 new imports
from fastapi.security import HTTPBearer
from .utils import VerifyToken # 👈 new import
# Scheme for the Authorization header
token_auth_scheme = HTTPBearer()
# Creates app instance
app = FastAPI()
@app.get("/api/public")
def public():
"""No access token required to access this route"""
result = {
"status": "success",
"msg": ("Hello from a public endpoint! You don't need to be "
"authenticated to see this.")
}
return result
@app.get("/api/private")
def private(response: Response, token: str = Depends(token_auth_scheme)): # 👈 updated code
"""A valid access token is required to access this route"""
result = VerifyToken(token.credentials).verify() # 👈 updated code
# 👇 new code
if result.get("status"):
response.status_code = status.HTTP_400_BAD_REQUEST
return result
# 👆 new code
return result
この更新により、保護されたエンドポイントが適切に設定され、必要なアクセストークンのすべての検証手順が実行されます。🎉
--reload
構成がロードされていることを確認する必要があるため、フラグを使用してサーバーを起動した場合でも、uvicorn
プロセスを終了してからサーバーを再起動することをお勧めします。これにより、.config
ファイルまたは環境変数の構成パラメーターを使用してAPIの適切な機能が保証されます。
FastAPI
サーバー内の保護されたエンドポイントにリクエストを送信する前に、Auth0からのアクセストークンが必要です。Test
APIのタブにあるAuth0ダッシュボードからコピーして取得できます。
POST
Auth0のエンドポイントへのcurlリクエストを使用してアクセストークンを取得することもできます。このリクエストは、Auth0ダッシュボードのAPIのタブからコピーできます。カールリクエストは次のようになります。必要に応じて値を入力することを忘れないでください。oauth/tokenTest
curl -X 'POST' \
--url 'https://<YOUR DOMAIN HERE>/oauth/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data 'client_id=<YOUR CLIENT ID HERE>' \
--data client_secret=<YOUR CLIENT SECRET HERE> \
--data audience=<YOUR AUDIENCE HERE>
コマンドラインに、次のようなベアラートークンを含む応答が表示されます。
{
"access_token": "<YOUR_BEARER_TOKEN>",
"expires_in": 86400,
"token_type": "Bearer"
}
これで、このアクセストークンを使用してプライベートエンドポイントにアクセスできます。
curl -X 'GET' \
--url 'http://127.0.0.1:8000/api/private' \
--header 'Authorization: Bearer <YOUR_BEARER_TOKEN>'
リクエストが成功すると、サーバーはアクセストークンのペイロードを送り返します。
{
"iss": "https://<YOUR_DOMAIN>/",
"sub": "iojadoijawdioWDasdijasoid@clients",
"aud": "http://<YOUR_AUDIENCE>",
"iat": 1630341660,
"exp": 1630428060,
"azp": "ADKASDawdopjaodjwopdAWDdsd",
"gty": "client-credentials"
}
検証が失敗した場合は、何が悪かったのかを詳細に確認する必要があることに注意してください。
これで、プライベートエンドポイントの保護とその保護のテストが完了しました。
あなたはこのブログ投稿でかなり多くのことを学びました。まず、FastAPI
2つのエンドポイント(1つはパブリック、もう1つはプライベート)を実装することで、の基本を学びました。これらのエンドポイントの両方にリクエストを送信するのがいかに簡単であるかを見てきました。検証クラスを作成し、PyJWTがAuth0アクセストークンの検証にどのように役立つかを確認し、JWKSとは何かを学びました。
Auth0ダッシュボードでAPIを作成するプロセスを実行しました。また、FastAPIが提供する依存性注入システムを活用して統合の実装を支援することにより、エンドポイントの1つを保護する方法も学びました。そして、あなたはこれらすべてを非常に迅速に行いました。
つまり、FastAPI
エンドポイントを保護するためにAuth0を使用する方法だけでなく、を起動して実行するのがいかに簡単であるかを学びました。
このGitHubリポジトリには、今日作成したサンプルアプリケーションの完全なコードが含まれています。ご不明な点がございましたら、コミュニティフォーラムのスレッドでこのブログ投稿をお尋ねください。
リンク: https://auth0.com/blog/build-and-secure-fastapi-server-with-auth0/
1636488120
FastAPIは比較的新しいPythonフレームワークであり、アプリケーションを非常に迅速に作成できます。このフレームワークを使用すると、組み込みモジュールを使用してAPIリクエストデータをシームレスに読み取ることができ、Flaskの軽量な代替手段となります。
この記事では、の機能について説明FastAPI
し、基本的なAPIを設定し、Auth0を使用してエンドポイントを保護し、開始するのがいかに簡単かを学びます。
始める前に、以下のビデオを再生して、このブログ投稿の内容をビデオ形式でチェックすることもできます:👇
FastAPIを使用してビルドを開始する前に、Pythonと無料のAuth0アカウントが必要です。ここでサインアップできます。3.8.2
そのPythonバージョンをインストールし、Auth0アカウントを取得している場合は、新しいFastAPI
アプリケーションを作成できます。まず、開発する新しいディレクトリを作成します。この例では、というディレクトリと;というサブフォルダを作成します。このサブフォルダーは、コードが存在する場所です。fastapi-exampleapplication
フォルダで、次のコマンドを使用して仮想環境を作成します。fastapi-example
python3 -m venv .env
これにより仮想環境が作成され、依存関係が残りのコンピューターライブラリから分離されます。つまり、他のPythonプロジェクトに影響を与える可能性のある、ライブラリと依存関係でグローバル名前空間を汚染することはありません。
仮想環境を作成したら、それをアクティブ化する必要があります。Unixベースのオペレーティングシステムの場合、コマンドは次のとおりです。
source .env/bin/activate
別のオペレーティングシステムを使用している場合は、このドキュメントページで環境をアクティブ化する方法のリストを見つけることができます。仮想環境をアクティブ化した後、使用するパッケージをインストールできます:FastAPI
、uvicorn server、pyjwt、およびupdate pip
:
pip install -U pip
pip install fastapi uvicorn 'pyjwt[crypto]'
すべてのライブラリがインストールされたので、フォルダ内にファイルを作成できます。それはあなたのAPIコードが住む場所です。の内容は次のようになります。main.pyapplicationmain.py
"""main.py
Python FastAPI Auth0 integration example
"""
from fastapi import FastAPI
# Creates app instance
app = FastAPI()
@app.get("/api/public")
def public():
"""No access token required to access this route"""
result = {
"status": "success",
"msg": ("Hello from a public endpoint! You don't need to be "
"authenticated to see this.")
}
return result
これを分解しましょう:
FastAPI
ライブラリをインポートしますFastAPI()
@app.getGET
最後に、と呼ばれるパス操作関数があります。これは、そのルートが呼び出されるたびに実行される関数であり、ウェルカムメッセージを含む辞書を返します。public()
最初のエンドポイントコードを取得したので、サーバーを起動して実行するには、プロジェクトのルートディレクトリで次のコマンドを実行します。
uvicorn application.main:app --reload
サーバーが実行されている状態で、次の画像に示すように、最初のエンドポイント用に自動生成されたドキュメントを表示するために移動できます。http://127.0.0.1:8000/docs
または、を使用して、新しいターミナルウィンドウで最初のリクエストを行うこともできますcURL
。古いバージョンのオペレーティングシステムを使用しているWindowsユーザーの場合は、次のコマンドを実行する前にcurlをインストールする必要があることに注意してください。
curl -X 'GET' \
--url http://127.0.0.1:8000/api/public
そして、これと同じように行ったリクエストの結果として、JSONが表示されるはずです。
{
"status": "success",
"msg": "Hello from a public endpoint! You don't need to be authenticated to see this."
}
簡単にするcURL
ために、この投稿の残りの部分ではを使用します。
ベースAPIサーバーがセットアップされたので、ファイルにもう1つのエンドポイントを追加します。このアプリケーションでは、すべての人が利用できるルートと、Auth0から取得するアクセストークンを使用して自分だけがアクセスできるルートがあります。main.pyGET /api/publicGET /api/private
次に、ファイルを更新する必要があります。インポートセクションに変更する必要があるものは次のとおりです。main.py
Depends
からfastapi
FastAPI依存性注入システムのモジュールと、HTTPBearer
からクラスをインポートする必要があります。これは、ベアラートークンを使用した承認ヘッダー用の組み込みのセキュリティスキームです。fastapi.security
HTTPBearer
。これはBearer
、プライベートエンドポイントに対して行われる各リクエストでトークンを含む承認ヘッダーの存在を保証するために使用されます。トークン知らせるトークンのベアラがいることをAPIにアクセスするAPIを承認し、承認の際に付与された範囲で指定された特定のアクションを実行されています。
インポートを更新する以外に、プライベートエンドポイントを実装する必要があります。エンドポイントはまた、受け入れるの要求を、ここでのコードは何である今のようになっています。/api/privateGETmain.py
"""main.py
Python FastAPI Auth0 integration example
"""
from fastapi import Depends, FastAPI # 👈 new imports
from fastapi.security import HTTPBearer # 👈 new imports
# Scheme for the Authorization header
token_auth_scheme = HTTPBearer() # 👈 new code
# Creates app instance
app = FastAPI()
@app.get("/api/public")
def public():
"""No access token required to access this route"""
result = {
"status": "success",
"msg": ("Hello from a public endpoint! You don't need to be "
"authenticated to see this.")
}
return result
# new code 👇
@app.get("/api/private")
def private(token: str = Depends(token_auth_scheme)):
"""A valid access token is required to access this route"""
result = token.credentials
return result
Depends
このクラスは、特定のエンドポイントは、関数、クラス、またはインスタンスに対して受信する各要求を評価する責任があります。この場合、HTTPBearer
ベアラートークンを使用して承認ヘッダーのリクエストをチェックするスキームに対してリクエストを評価します。
FastAPI
依存性注入がどのように機能するかについての詳細は、そのドキュメントに記載されています。
これで、プライベートエンドポイントが受信したトークンを返します。トークンが提供されていない場合は、403 Forbidden
あなたがいることを示す詳細を含むステータスコードが返されます"Not authenticated"
。--reload
サーバーの実行中にフラグを使用したため、コマンドを再実行する必要はありません。uvicorn
ファイルを保存するたびに、変更を取得してサーバーを更新します。次に、エンドポイントにリクエストを送信して、その動作を確認します。まず、認証ヘッダーを渡さずにリクエストを作成しましょう。GET /api/private
curl -X 'GET' \
--url 'http://127.0.0.1:8000/api/private'
# {"detail": "Not authenticated"}
そして今、認証ヘッダーを使用してリクエストを行ったが、トークン値としてランダムな文字列を使用した場合、結果として同じランダムな値が表示されるはずです。
curl -X 'GET' \
--url 'http://127.0.0.1:8000/api/private' \
--header 'Authorization: Bearer FastAPI is awesome'
# "FastAPI is awesome"
ご覧のとおり、承認ヘッダーの値として任意の文字列を受け入れるため、エンドポイントは保護されていません。承認ヘッダーを受け取るだけでは不十分です。また、誰かがエンドポイントにアクセスできるようにするには、ベアラートークンの値を確認する必要があります。その動作を修正しましょう。
エンドポイントでトークンを検証する準備が整う前に、Auth0でAPIを設定する必要があります。このAPIを設定すると、Auth0に必要ないくつかの情報(オーディエンス、クライアントID、クライアントシークレット)にアクセスできます。
また、サーバー内からその情報にアクセスできる必要があります。そこで、構成ファイルが役立ちます。.config
プロジェクトのルートで呼び出される構成ファイルを作成する必要があります。これは、.config
ファイルが以下のように見えるはずです。それに応じて値を更新することを忘れないでください。
# .config
[AUTH0]
DOMAIN = your.domain.auth0.com
API_AUDIENCE = your.api.audience
ALGORITHMS = RS256
ISSUER = https://your.domain.auth0.com/
この構成は、トークン検証段階でAuth0構成設定をチェックするパズルの最初のピースです。従うべきもう1つの良いルールは、環境変数を含む構成ファイルをソースコードにコミットしないことです。これを防ぐに.gitignore
は、プロジェクトのルートにファイルを作成し、その.config
ファイルをエントリとして追加する必要があります。
# .gitignore
.config
これでFastAPI
サーバーにルートができましたが、まだ保護されていません。リクエストに認証ヘッダーがあるかどうかのみをチェックします。つまり、プロセスのステップが欠落しています。アクセストークンを検証する必要があります。これを行うには、Auth0のアクセストークンがJWTであるため、JWTを検証するためのすべての手順を実行するオブジェクトを作成する必要があります。GET /api/private
責任をルーティング定義から分離するには、フォルダー内に呼び出される新しいファイルを作成して、アクセストークンの検証や構成情報の読み取りなど、すべてのユーティリティコードを保持する必要があります。utils.pyapplication
Pythonos
ライブラリ、PyJWT
およびconfigparser
ライブラリをインポートすることから始めます。OSライブラリを使用すると、環境変数にアクセスできます。JWTライブラリは、JWTをチェックおよび検証するための関数を提供します。ConfigParser
名前の由来のライブラリのクラスは、Pythonが.config
以前に作成したファイルにある構成設定を読み取る方法を提供します。そして、インポート後に最初に持っているのは、と呼ばれる関数です。これを以下に示します。set_up()
"""utils.py
"""
import os
import jwt
from configparser import ConfigParser
def set_up():
"""Sets up configuration for the app"""
env = os.getenv("ENV", ".config")
if env == ".config":
config = ConfigParser()
config.read(".config")
config = config["AUTH0"]
else:
config = {
"DOMAIN": os.getenv("DOMAIN", "your.domain.com"),
"API_AUDIENCE": os.getenv("API_AUDIENCE", "your.audience.com"),
"ISSUER": os.getenv("ISSUER", "https://your.domain.com/"),
"ALGORITHMS": os.getenv("ALGORITHMS", "RS256"),
}
return config
この関数は、ファイルを読み取り、辞書のように機能する構成オブジェクトを作成する役割を果たします。このサンプルコードは環境変数でも実行できるように準備されているため、関数はデフォルトでファイルを読み取ろうとします。この動作は、環境変数を他の値に設定することで変更できます。その場合、上記の句で確認できるすべての環境変数を読み取ることで辞書が作成されます。set_up().configset_up().configENVelse
パズルの次のピースは、魔法が起こる場所です。VerifyToken
JWTトークンの検証を処理するクラスを作成します。
# paste the code 👇 after the set_up() function in the utils.py file
class VerifyToken():
"""Does all the token verification using PyJWT"""
def __init__(self, token):
self.token = token
self.config = set_up()
# This gets the JWKS from a given URL and does processing so you can
# use any of the keys available
jwks_url = f'https://{self.config["DOMAIN"]}/.well-known/jwks.json'
self.jwks_client = jwt.PyJWKClient(jwks_url)
def verify(self):
# This gets the 'kid' from the passed token
try:
self.signing_key = self.jwks_client.get_signing_key_from_jwt(
self.token
).key
except jwt.exceptions.PyJWKClientError as error:
return {"status": "error", "msg": error.__str__()}
except jwt.exceptions.DecodeError as error:
return {"status": "error", "msg": error.__str__()}
try:
payload = jwt.decode(
self.token,
self.signing_key,
algorithms=self.config["ALGORITHMS"],
audience=self.config["API_AUDIENCE"],
issuer=self.config["ISSUER"],
)
except Exception as e:
return {"status": "error", "message": str(e)}
return payload
ここでの手順を理解するために、このクラスを分解してみましょう。
__init__()
token
、VerifyToken
クラスが必要とするパラメーターを指定する役割を果たします。set_up()
JWKS
のPyJWKClient
fromを使用してファイルのパスを設定しPyJWT
ます。A JSONのWebキーセット、または略してJWKSは、トークンの署名を検証し、それが有効なトークンであることを保証するために必要な情報が含まれています。Auth0はOAuth2.0を実装しているため、トークンとそのプロパティの検証に使用される追加のメタデータを呼び出して取得できる「既知の」エンドポイントがあります。verify()
kid
トークンヘッダーに存在するクレーム)を使用して、JWKSから使用されるキーを取得してトークンの署名を検証します。考えられるエラーのいずれかによってこのステップが失敗した場合、エラーメッセージが返されます。前のセクションのプライベートエンドポイントでこのコードを使用する方法を見てみましょう。
最後のパズルのピースは、ファイルに作成したクラスをインポートして、エンドポイントで使用することです。変更する必要があるものは次のとおりです。utils.pyGET /api/private
VerifyToken
から、エンドポイントに移動します。また、エラーが発生した場合に詳細な応答を提供できるように、からResponse
クラスとstatus
オブジェクトをインポートする必要fastapi
があります。VerifyToken
クラスに渡し、メソッドの結果がエラーであるかどうかを確認して、エンドポイントを調整する必要があります。verify()
上記のすべての変更を加えたファイルは次のようになります。main.py
"""main.py
Python FastAPI Auth0 integration example
"""
from fastapi import Depends, FastAPI, Response, status # 👈 new imports
from fastapi.security import HTTPBearer
from .utils import VerifyToken # 👈 new import
# Scheme for the Authorization header
token_auth_scheme = HTTPBearer()
# Creates app instance
app = FastAPI()
@app.get("/api/public")
def public():
"""No access token required to access this route"""
result = {
"status": "success",
"msg": ("Hello from a public endpoint! You don't need to be "
"authenticated to see this.")
}
return result
@app.get("/api/private")
def private(response: Response, token: str = Depends(token_auth_scheme)): # 👈 updated code
"""A valid access token is required to access this route"""
result = VerifyToken(token.credentials).verify() # 👈 updated code
# 👇 new code
if result.get("status"):
response.status_code = status.HTTP_400_BAD_REQUEST
return result
# 👆 new code
return result
この更新により、保護されたエンドポイントが適切に設定され、必要なアクセストークンのすべての検証手順が実行されます。🎉
--reload
構成がロードされていることを確認する必要があるため、フラグを使用してサーバーを起動した場合でも、uvicorn
プロセスを終了してからサーバーを再起動することをお勧めします。これにより、.config
ファイルまたは環境変数の構成パラメーターを使用してAPIの適切な機能が保証されます。
FastAPI
サーバー内の保護されたエンドポイントにリクエストを送信する前に、Auth0からのアクセストークンが必要です。Test
APIのタブにあるAuth0ダッシュボードからコピーして取得できます。
POST
Auth0のエンドポイントへのcurlリクエストを使用してアクセストークンを取得することもできます。このリクエストは、Auth0ダッシュボードのAPIのタブからコピーできます。カールリクエストは次のようになります。必要に応じて値を入力することを忘れないでください。oauth/tokenTest
curl -X 'POST' \
--url 'https://<YOUR DOMAIN HERE>/oauth/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data 'client_id=<YOUR CLIENT ID HERE>' \
--data client_secret=<YOUR CLIENT SECRET HERE> \
--data audience=<YOUR AUDIENCE HERE>
コマンドラインに、次のようなベアラートークンを含む応答が表示されます。
{
"access_token": "<YOUR_BEARER_TOKEN>",
"expires_in": 86400,
"token_type": "Bearer"
}
これで、このアクセストークンを使用してプライベートエンドポイントにアクセスできます。
curl -X 'GET' \
--url 'http://127.0.0.1:8000/api/private' \
--header 'Authorization: Bearer <YOUR_BEARER_TOKEN>'
リクエストが成功すると、サーバーはアクセストークンのペイロードを送り返します。
{
"iss": "https://<YOUR_DOMAIN>/",
"sub": "iojadoijawdioWDasdijasoid@clients",
"aud": "http://<YOUR_AUDIENCE>",
"iat": 1630341660,
"exp": 1630428060,
"azp": "ADKASDawdopjaodjwopdAWDdsd",
"gty": "client-credentials"
}
検証が失敗した場合は、何が悪かったのかを詳細に確認する必要があることに注意してください。
これで、プライベートエンドポイントの保護とその保護のテストが完了しました。
あなたはこのブログ投稿でかなり多くのことを学びました。まず、FastAPI
2つのエンドポイント(1つはパブリック、もう1つはプライベート)を実装することで、の基本を学びました。これらのエンドポイントの両方にリクエストを送信するのがいかに簡単であるかを見てきました。検証クラスを作成し、PyJWTがAuth0アクセストークンの検証にどのように役立つかを確認し、JWKSとは何かを学びました。
Auth0ダッシュボードでAPIを作成するプロセスを実行しました。また、FastAPIが提供する依存性注入システムを活用して統合の実装を支援することにより、エンドポイントの1つを保護する方法も学びました。そして、あなたはこれらすべてを非常に迅速に行いました。
つまり、FastAPI
エンドポイントを保護するためにAuth0を使用する方法だけでなく、を起動して実行するのがいかに簡単であるかを学びました。
このGitHubリポジトリには、今日作成したサンプルアプリケーションの完全なコードが含まれています。ご不明な点がございましたら、コミュニティフォーラムのスレッドでこのブログ投稿をお尋ねください。
リンク: https://auth0.com/blog/build-and-secure-fastapi-server-with-auth0/