やること
FirebaseとFastAPIを用いて、クライアント側でトークンを取得し、自前のAPIを叩くときにヘッダーに貼り付け、サーバーサイドで検証することでログインしているかどうかを判断します。ここではGoogle謹製のfirebase_admin
を用いてトークンを検証します。
トークンを取得する
FirebaseコンソールのAuthenticationでパスワードログインを有効にし、以下のような適当なアカウントを作ります。ここではこれを用いてログインします。もちろんTwitterなどの認可でも大丈夫です。
EMAIL = 'test001@example.com'
PASSWORD = 'password'
必要なものをインストールします。
$ pip install requests
firebaseコンソールから以下のようなJSONを取得し貼り付け良い感じに成形します。(実際に使うのはapiKey
のみ)
CONFIG = {
"apiKey": "YOUR API KEY",
"authDomain": "YOURPROJECTID.firebaseapp.com",
"databaseURL": "https://YOURPROJECTID.firebaseio.com",
"projectId": "YOUR PROJECT ID",
"storageBucket": "YOUR PROJECT ID.appspot.com",
"messagingSenderId": "YOUR MESSAGE SENDER ID",
"appId": "YOUR:APP:ID",
"measurementId": "YOUR MEASUREMENT ID"
}
Firebase AuthのREST APIを叩き、tokenを取得します。REST APIのドキュメントはここです。
api_key = CONFIG["apiKey"]
uri = f"https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key={api_key}"
data = {"email": EMAIL, "password": PASSWORD, "returnSecureToken": True}
result = requests.post(url=uri, json=data).json()
token = result['idToken']
このtokenを実行時に出力し、後で用います。
FastAPIでトークンを検証する
必要なものをインストールします。
$ pip install fastapi firebase_admin uvicorn
firebaseのコンソールから歯車→サービスアカウント→新しい秘密鍵の生成で秘密鍵をダウンロードし、読み込みます。
from firebase_admin import auth, credentials
cred = credentials.Certificate("path/to/cert.json")
firebase_admin.initialize_app(cred)
ヘッダーからトークンを取得し、デコードし、ユーザー情報を取得する関数を定義します。
実際はここでDBからユーザー情報を取得しても良いと思います。
FastAPIのドキュメントにはfastapi.security.OAuth2PasswordBearer
の使用例は書かれていたのですが、単純にBearerトークンを取得したい時のやり方は書かれておらず、コードを見るしかありませんでした。
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi import Depends, HTTPException, status
def get_current_user(cred: HTTPAuthorizationCredentials = Depends(HTTPBearer())):
try:
decoded_token = auth.verify_id_token(cred.credentials)
except:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail='Invalid authentication credentials',
headers={'WWW-Authenticate': 'Bearer'},
)
user = decoded_token['firebase']['identities']
return user
正当なBearerトークンでないとアクセスできないエンドポイントを定義します。
FastAPIはDependsや型アノテーションで欲しいものをInjectできるので本当に便利です。
from fastapi import FastAPI, Depends, HTTPException, status
app = FastAPI()
@app.get('/')
async def homepage(current_user=Depends(get_current_user)):
return {'msg': 'ok', 'user': current_user}
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host='localhost', port=5000)
実行する
ここまでのコードをGistにまとめました。
まず以下のコードを用いてトークンを取得し、コピーします。
https://gist.github.com/NKNaaS/241687ecb5219ae0ce633a884d8ab5bb
次に以下のコードを用いてサーバーを起動します。
https://gist.github.com/NKNaaS/a698fd679fb545cb2cfe792f0114938c
それを適当なRESTクライアント(私はInsomniaを使用)で叩きます。すると次のような結果が得られます。
> GET / HTTP/1.1
> Host: localhost:5000
> User-Agent: insomnia/7.1.1
> Authorization: Bearer
YOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKEN
> Accept: */*
{
"msg": "ok",
"user": {
"identities": {
"email": [
"test001@example.com"
]
},
"sign_in_provider": "password"
}
}
最後に
FastAPIもっと流行ってほしい。