25
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PythonでFirebase Authenticationのトークン取得とFastAPIでトークン検証

Last updated at Posted at 2020-06-06

やること

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を使用)で叩きます。すると次のような結果が得られます。

request
> GET / HTTP/1.1
> Host: localhost:5000
> User-Agent: insomnia/7.1.1
> Authorization: Bearer 
YOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKENYOURTOKEN
> Accept: */*
response
{
  "msg": "ok",
  "user": {
    "identities": {
      "email": [
        "test001@example.com"
      ]
    },
    "sign_in_provider": "password"
  }
}

最後に

FastAPIもっと流行ってほしい。

25
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?