🍣

FastAPIでGoogle OAuthを使うには

に公開

はじめに

Webサービスを使っていると、よく、Googleでログインする画面を見かけると思います。
今回はそれを実装するやり方を説明します。

やり方

概要

・Google Consoleでログインページ

Google Cloudに承認情報をつくる。

Google Cloudにアクセスする。

htps://console.cloud.google.com/welcome?inv=1&invt=Ab0Qow&project=plenary-caster-460610-d6

APIとサービスを選択します。

左サイドメニューから認証情報をクリックします。

認証情報を作成から、OAuthクライアントIDをクリックします。

必要な情報を入力して作成します。

アクセス元、リダイレクト先を入力し、シークレットキーを作ってダウンロードしておきます。

テストユーザーを追加する。
APIとサービス > OAuth同意画面 > 対象 と進み、テストユーザを追加します。

pythonでWebAPIを作成する。

バックエンドを通さないとアクセスできる昨日は限られています。
PHPなど好きなバックエンド言語を使えばいいのですが、googleにつなぐライブラリの関係でpythonが一番簡単なようです。

以下のコードを使います。

メソッド名 説明
get_token ログイン後、各種操作を行うためのトークンを取得する。
request_show_google_login_page Google連携のログインページを開く
callback Googleからのログイン結果をともなってアクセスしてくるエンドポイントを提供する。
app/api/routes/auth.py
from fastapi import APIRouter, Request
from fastapi.responses import RedirectResponse, JSONResponse
from pathlib import Path
from google_auth_oauthlib.flow import Flow
from app.core.config import SCOPES, GOOGLE_CLIENT_SECRET_FILE, REDIRECT_URI

router = APIRouter()


@router.get("/get-token")
def get_token(request: Request):
    """
    セッションに保存済みのGoogle OAuth2 のアクセストークンを取得する。

    Args:
        request (Request): FastAPI のリクエストオブジェクト。

    Returns:
        アクセストークンのJSON
    """
    token = request.session.get("access_token")
    if token:
        return JSONResponse({"token": token})
    else:
        return JSONResponse({"error": "トークンが見つかりません"}, status_code=404)


@router.get("/login")
def request_show_google_login_page():
    """
    Google OAuth2 のログイン画面を表示するようにGoogleへ要求する。

    Args:
        void

    Returns:
        dict: 認証結果などを含むレスポンス。
    """
    flow = Flow.from_client_secrets_file(
        GOOGLE_CLIENT_SECRET_FILE,
        scopes=SCOPES,
        redirect_uri=REDIRECT_URI,
    )

    auth_url, _ = flow.authorization_url(
        access_type='offline',
        include_granted_scopes='true',
    )

    return RedirectResponse(auth_url)

@router.get("/api/auth/google-oauth/callback")
def callback(request: Request):
    """
    Google OAuth2 のコールバックエンドポイント。

    Args:
        request (Request): FastAPI のリクエストオブジェクト。

    Returns:
        リダイレクトレスポンス
    """

    # 認可コードを取得(クエリパラメータ)
    code = request.query_params.get("code")
    if not code:
        return JSONResponse(content={"error": "認可コードが見つかりません"}, status_code=400)

    # Flowを再生成(同じclient_secretとredirect_uriで)
    flow = Flow.from_client_secrets_file(
        GOOGLE_CLIENT_SECRET_FILE,
        scopes=SCOPES,
        redirect_uri=REDIRECT_URI,
    )

    # 認可コードを使ってトークンを取得
    flow.fetch_token(code=code)

    # アクセストークンなどの情報を取り出す
    credentials = flow.credentials
    token_info = {
        "access_token": credentials.token,
        "refresh_token": credentials.refresh_token,
        "token_uri": credentials.token_uri,
        "client_id": credentials.client_id,
        "client_secret": credentials.client_secret,
        "scopes": "," . join(credentials.scopes),
    }

    # セッションに保管する。
    request.session["access_token"] = token_info

    # フロントエンドに処理を返してダッシュボードへ画面遷移させる。
    return RedirectResponse("http://localhost:5173/dashboard")



具体的な設定値を記述します。

低数名 説明
GOOGLE_CLIENT_SECRET_FILE GoogleCloudがダウンロードするGoogleAPI専用の秘密鍵のようなもの
REDIRECT_URI GoogleCloudで設定したリダイレクトURIをコピーする。
SCOPES 使いたいGoogleAPIを指定する。
app/core/config.py
from pathlib import Path

# アプリケーションソースのルート。
APP_DIR = Path(__file__).resolve().parent.parent

# GoogleClientSecretFileの場所を指定する。
GOOGLE_CLIENT_SECRET_FILE = APP_DIR / "core" / "api_keys" / "client_secret.json"

# リダイレクトURI(Google Cloudの承認済みのリダイレクト URIと一致させる)
REDIRECT_URI = "http://localhost:8000/api/auth/google-oauth/callback"

# 認可スコープ
SCOPES = [
    "https://www.googleapis.com/auth/drive.readonly",
    "https://www.googleapis.com/auth/youtube"
]

フロントからログインのエンドポイントを叩く

最後に、location.hrefなどを使ってログインページにアクセスします。

page.tsx
import { FC } from 'react'

import { Container } from '@/shared/ui/container'
import { MainLayout } from '@/pages/layouts/main'
import { routes } from '@/shared/routes'
import { ExcecuteButton } from '@/shared/ui/button'

export const WelcomePage: FC = () => {
  return (
    <MainLayout title="ようこそ。">
      <Container>
        <ExcecuteButton onClick={() => {
          window.location.href = routes.API.GOOGLE_OAUTH;
        }}>
          Googleで認証
        </ExcecuteButton>
      </Container>
    </MainLayout>
  )
}

routes/indes.ts
export const routes = {
  WELCOME: '/',
  DASHBOARD: '/dashboard',

  API: {
    GOOGLE_OAUTH: 'http://localhost:8000/login',
    GET_GOOGLE_AOUTH_TOKEN: 'http://localhost:8000/get-token',
    GET_YOUTUBE_PLAYLIST: 'http://localhost:8000/get-playlists',
  },
}

おわりに

上手く行った場合はログイン画面が出て、トークンを取得できます。
そのあとはやりたい昨日ごとのエンドポイントにアクセスしてデータの読み書きを行う流れとなります。

株式会社ONE WEDGE

【Serverlessで世の中をもっと楽しく】 ONE WEDGEはServerlessシステム開発を中核技術としてWeb系システム開発、AWS/GCPを利用した業務システム・サービス開発、PWAを用いたモバイル開発、Alexaスキル開発など、元気と技術力を武器にお客様に真摯に向き合う価値創造企業です。
https://onewedge.co.jp

Discussion