🔹 概要
FastAPIを使用して,Google OAuth2.0を利用した認証方法を実装する.OAuth2.0を活用することによって,Googleアカウントを使ったログイン機能が可能となる.
🔹 主な手順
① Google CloudでクライアントIDを取得
↓
② FastAPIのセットアップ
↓
③ OAuth2の設定
↓
④ エンドポイントの実装
①Google CloudでクライアントIDを取得
-
Google Cloud Console にアクセスする
-
画面上部の 「プロジェクトの選択」 をクリックする
-
新しくプロジェクトを作成する
-
左側のメニューから 「APIとサービス」 → 「OAuth同意画面」 を選択する
-
左側のメニューから 「APIとサービス」 → 「認証情報」 を選択する
-
左側のメニューから 「APIとサービス」 → 「OAuth同意画面」 → 「対象」 を選択し、テストするユーザーを追加する
②FastAPIのセットアップ
必要なパッケージのインストール
以下のコマンドを実行して,FastAPIとOAuth2認証に必要なパッケージをインストールする.
pip install fastapi google-auth Jinja2 httpx python-dotenv uvicorn
fastapi
: Webフレームワーク
google-auth
: Google 認証および OAuth2.0 に関連する認証処理を提供
Jinja2
: テンプレートエンジン(HTMLのレンダリングに使用)
httpx
: HTTPクライアント
python-dotenv
: 環境変数の管理
uvicorn
: 非同期ASGIサーバー
.env
ファイルの作成
プロジェクトのルートディレクトリに.envファイルを作成し,以下のようにGoogle OAuthのクライアント情報を設定する.
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_REDIRECT_URI=your-google-redirect-url
your-google-client-id
: OAuth クライアント ID の作成時に得たクライアントID
your-google-client-secret
: OAuth クライアント ID の作成時に得たクライアントシークレット
your-google-redirect-url
:OAuth クライアント ID の作成時に入力した承認済みのリダイレクト URI
③OAuth2の設定
Google OAuth2.0の認証情報を取得する
Google OAuth 2.0 認証に必要な設定を環境変数から取得し、定数として定義する.os.getenv
関数を使用して,環境変数 GOOGLE_CLIENT_ID
.GOOGLE_CLIENT_SECRET
,および GOOGLE_REDIRECT_URI
の値を先ほど作成した.env
から取得する.
また,GOOGLE_AUTH_URL
,GOOGLE_TOKEN_URL
,および GOOGLE_USER_INFO_URL
は,Googleの認証,トークン取得,およびユーザ情報取得のためのエンドポイントURLを定義している.これにより、Google OAuth 2.0 認証フローを実装する際に必要な情報が一箇所にまとめられ,コードの可読性と保守性が向上する.
import os
import httpx
from fastapi import APIRouter, Request, HTTPException
from fastapi.responses import RedirectResponse
from google.oauth2 import id_token
from google.auth.transport import requests
from fastapi.security import OAuth2AuthorizationCodeBearer
from twilio.rest import Client
from dotenv import load_dotenv
load_dotenv()
router = APIRouter(prefix="/auth", tags=["authentication"])
# Google OAuth2.0の設定
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
GOOGLE_REDIRECT_URI = os.getenv("GOOGLE_REDIRECT_URI")
GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/auth"
GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token"
GOOGLE_USER_INFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo"
ユーザーの認証とトークンの取得
OAuth2AuthorizationCodeBearer
クラスのインスタンスを作成し,oauth2_scheme
に代入している.このクラスは,OAuth2の認証コードフローを使用してベアラートークンを取得するための認証スキームを提供する.リクエストヘッダに含まれるベアラートークンを検証し,認証が成功した場合にトークンを返す.これにより,APIエンドポイントへのアクセス制御が可能になるのだ.具体的には,Google OAuth2.0の認証エンドポイントとトークンエンドポイントを使用することで,ユーザの認証とトークンの取得を行う.
authorizationUrl
と tokenUrl
の引数には,それぞれ GOOGLE_AUTH_URL
と GOOGLE_TOKEN_URL が渡されている.これらのURLは,GoogleのOAuth2.0認証フローに必要なエンドポイントを指しており,GOOGLE_AUTH_URL
はユーザが認証を行うためのURL,GOOGLE_TOKEN_URL
は認証コードをトークンに交換するためのURLである.
oauth2_scheme = OAuth2AuthorizationCodeBearer(
authorizationUrl=GOOGLE_AUTH_URL,
tokenUrl=GOOGLE_TOKEN_URL,
)
④エンドポイントの実装
Google OAuth 2.0認証のためのログインエンドポイントを定義
このエンドポイントにアクセスすると,login
関数が非同期に実行される.login
関数ではGoogleの認証URLを構築している.そして,RedirectResponse
を使用して,ユーザをGoogleの認証ページにリダイレクトする.
RedirectResponse
は,指定されたURLにリダイレクトするHTTPレスポンスを生成する.これにより,ユーザはGoogleの認証ページに移動し,認証を行うことが可能となる.認証が成功すると,指定されたリダイレクトURIに認証コードが返される.
@router.get("/login")
async def login():
google_auth_url = f"{GOOGLE_AUTH_URL}?client_id={GOOGLE_CLIENT_ID}&redirect_uri={GOOGLE_REDIRECT_URI}&response_type=code&scope=openid%20email&access_type=offline&prompt=consent"
return RedirectResponse(google_auth_url)
Google OAuth 2.0 認証フローのコールバックエンドポイントを定義
ユーザがGoogleの認証ページで認証を行った後,Google認証コードをこのエンドポイントにリダイレクトする.このエンドポイントは,認証コードを受け取り,アクセストークンとIDトークンを取得するために使用される.
このエンドポイントにアクセスすると,callback
関数が非同期に実行される.この関数の引数は,認証コードとリクエストオブジェクトである.認証コードはGoogleからリダイレクトされた際にクエリパラメータとして渡される.
次に,認証コードを使用してGoogleのトークンエンドポイントにPOSTリクエストを送信する.リクエストのデータには,認証コード,クライアントID,クライアントシークレット,リダイレクトURI,およびグラントタイプ(アクセス権の付与の方法)が含まれている.そして,httpx.AsyncClient
を使用して非同期にリクエストを送信し,レスポンスを受け取る.レスポンスが成功したかどうかを確認し,成功した場合はレスポンスのJSONデータを取得する.
取得したトークンレスポンスからIDトークンを抽出し,IDトークンが存在しない場合はHTTP 400エラーを発生させる.IDトークンが存在する場合は,id_token.verify_oauth2_token
関数を使用してトークンを検証し,ユーザ情報を取得する.ユーザーの名前をセッションに保存し,/v1/helloworldエンドポイントにリダイレクトする.
@router.get("/callback")
async def callback(code: str, request: Request):
data = {
"code": code,
"client_id": GOOGLE_CLIENT_ID,
"client_secret": GOOGLE_CLIENT_SECRET,
"redirect_uri": GOOGLE_REDIRECT_URI,
"grant_type": "authorization_code",
}
async with httpx.AsyncClient() as client:
response = await client.post(GOOGLE_TOKEN_URL, data=data)
response.raise_for_status()
token_response = response.json()
id_token_value = token_response.get("id_token")
if id_token_value is None:
raise HTTPException(status_code=400, detail="id_tokenが取得できませんでした")
try:
id_info = id_token.verify_oauth2_token(id_token_value, requests.Request(), GOOGLE_CLIENT_ID)
name = id_info.get("name")
request.session["user_name"] = name
return RedirectResponse(url="/v1/helloworld")
except ValueError as e:
raise HTTPException(status_code=400, detail=f"id_tokenの検証に失敗しました: {str(e)}")
except Exception as e:
raise HTTPException(status_code=400, detail=f"エラーが発生しました: {str(e)}")
🔹 まとめ
・Google CloudでOAuth2.0のクライアントIDとシークレットを取得
・FastAPIをセットアップし,OAuth2の認証エンドポイントを作成
・Google認証のリダイレクトを処理し,ユーザ情報を取得
これにより,Googleアカウントでログインできるようになる.
🔹 参考文献
以下の文献を参考に実装を行いました.