OAuth認証を使うのが一般的になってきているので、FastAPIでもjwt認証が簡単に実装できるようなライブラリがあります。
今回はそちらを使いながら実装例をやっていきたいと思います。
使うライブラリは、fastapi_jwt_authというもので、access_tokenはもちろんrefresh_tokenも実装できるようになっています。
公式サイトではjoseを使っていますが、わかりやすさを優先したためfastapi_jwt_authを使用しています。
※認証のためのユーザー作成のエンドポイントは別途作成しておいてください。
早速モジュールをインストールします。
pip install fastapi-jwt-auth
FastAPIやらは別途インストールしておき、必要になりそうなモジュールを読み込んでおきます。
from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel
from fastapi_jwt_auth import AuthJWT
app = FastAPI()
# アカウントが作成されたタイミングでユーザーを格納
users=[]
はじめに、jwtのスキーマを定義しておきます。
settingsの値は機密情報を扱うため、secretsで作成した乱数を使用します。
class Settings(BaseModel):
authjwt_secret_key:str = '97389ea7eba8c02e7d66a53bd3076a464d3219238a4cb204577c8fa8bf7df21b'
@AuthJWT.load_config
def get_config():
return Settings()
jwt認証に必要なスキーマを定義しておきます。
ここでは、ユーザ名とパスワードにしていますが、メールアドレスを使用するケースもあると思うのでそこは各々好きなパラメータを使ってください。
class UserLogin(BaseModel):
username:str
password:str
class Config:
schema_extra = {
'example':{
'username':'test',
'password':'password'
}
}
後は、ログイン時に必要な情報を渡して、その情報を元にJWT側で必要な件名などを設定してあげてtokenを発行してあげればいいだけです。
ユーザー名もしくはパスワードが間違ってればエラーを返すようにします。
@app.post('/login')
def login(user:UserLogin, Authorize:AuthJWT=Depends()):
for u in users:
if (u['username']==user.username) and (u['password']==user.password):
access_token = Authorize.create_access_token(subject=user.username)
return {'access_token': access_token}
raise HTTPException(status_code=401, detail="Invalid username or password")
access_tokenを持っていなければ、特定のエンドポイントにアクセスできないような設定を書いてみましょう。
今回は本当にシンプルで、access_tokenを持っていればアクセスしにきたユーザー名を返すようなエンドポイントを作成してあります。
これだけでjwt認証が実装できました。
@app.get('/protected')
def get_logged_in_user(Authorize:AuthJWT=Depends()):
try:
Authorize.jwt_required()
except Exception as e:
raise HTTPException(status_code=401, detail="Invalid token")
current_user = Authorize.get_jwt_subject()
return {'current_user': current_user}
実際に試す際には、swaggerUIの方で「POST:/login」へ別途作成してあるアカウントでaccess_tokenを発行します。
更にpostmanなどを使用して、headerにaccess_tokenを付与してアクセスしてみてください。
上手くいけば、下記のようにアクセスしたユーザー名が返ってきます。
注意点としては、keyにはAutorizationを追加して、valueには、「Bearer <access_token>」の形で付与する必要があるので注意です。BearerがJWTではない点に気をつけましょう。