2
2

More than 1 year has passed since last update.

FastAPIでかんたんFirebaseAuthorization+かんたんにテスト

Last updated at Posted at 2022-01-26

これまで他の言語で作っていたAPIを書き直しています。以前のAPIはFirebaseAuthorizationで認証していたので、FastAPIでもFirebaseAuthorizationでユーザー認証したい。さて、どうするか。

公式ライブラリでなんとかする

セキュリティ的にこれが無難だと思う。しかし、account_key.json のような認証鍵をファイルとして渡す必要があり、Dockerコンテナ化する場合はボリュームのマウントがめんどくさくなってしまう。環境変数に全部乗せることができないだろうか?

サードパーティライブラリでなんとかする

実は、本当にFirebase Authorizationの認証だけがしたいならProjectIDさえあれば、署名が有効かどうかは 特定のエンドポイントにパラメータを添えてGETするだけで良い。(最悪IDトークン本体が1時間有効なJWTなので、本体をパースするだけでも確認できてしまうが、それが無効化されているかどうかを念の為Google鯖に確認しに行っているというだけ) それをすごくシンプルにできるようにしてるのがこのFastAPI-CloudAuth。本家firebase_adminと違い、深い依存関係が追加されることもないので、セキュア度が高くない用途であれば、このサードパーティでも良いと思う。今回作っているのは軽いゲームなので、今回はこっちを採用。

検証の実装例

非常にシンプルにできる仕様なので、実装も何もという感じだったが紹介。

from fastapi_cloudauth.firebase import FirebaseCurrentUser, FirebaseClaims


router = FastAPI()
get_current_user = FirebaseCurrentUser(project_id=os.environ["PROJECT_ID"])


@router.post(
    "/users",
    responses={
        200: {"description": "OK"},
        400: {"description": "Bad Request"},
        401: {"description": "Unauthorized"},
        409: {"description": "Conflict"},
    },
    tags=["users"],
    summary="Add a user",
)
async def add_user(
    auth: FirebaseClaims = Depends(get_current_user),
) -> None:
    """Add specified new user to server"""
    # 認証できていればこの中にたどり着く
    # できていなければ401が既に返されている
    return auth.user_id # user_idが手に入ればあとは好きにどうぞ

テスト用のスタブ例

Headerに入ったBearer 以降の部分をそのままユーザーIDとして扱うことで、テスト時はトークン関係なしに直接試せるようにする。もっとやりようはあるが、愚直にやったらこうなった。あとはテストをしようとしたら、毎回認証がスキップされて本体だけのテストを行うことができる。

seurity_api.py
dependsHeader = Header(None)

async def get_current_user_stub(
    authorization: Optional[str] = dependsHeader,
) -> FirebaseClaims:
    if authorization is None:
        raise HTTPException(status_code=401, detail="Not authenticated")
    separated_authorization = authorization.split("Bearer ")
    if len(separated_authorization) != 2:
        raise HTTPException(status_code=401, detail="Not verified")
    DUMMY_USER["user_id"] = separated_authorization[1]
    return DUMMY_USER


get_current_user = FirebaseCurrentUser(project_id=os.environ["PROJECT_ID"])
conftest.py
from src.security_api import get_current_user, get_current_user_stub

@pytest.fixture
def app() -> FastAPI:
    # ここにDependsの関数名をキーとして 別の関数を入れると オーバーライドができる
    application.dependency_overrides[get_current_user] = get_current_user_stub
    return application  # type: ignore

やってみた感想

Firebase Authorizationは楽にできてすごく良い

2
2
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
2
2