• #Google OAuth
  • #Better Auth
  • #Cloudflare Workers
  • #認証

Google OAuth 設定ガイド

対象: Chat Story サービスへの Google ログイン実装
作成日: 2025年12月11日


概要

このドキュメントでは、Google Cloud Console で OAuth 2.0 認証情報(Client ID / Client Secret)を取得し、Better Auth と連携するまでの手順を解説します。


1. Google Cloud Console へのアクセス

Step 1: Google Cloud Console を開く

  1. 以下のURLにアクセス
    https://console.cloud.google.com/
    
  2. Googleアカウントでログイン(まだの場合)

2. プロジェクトの作成

Step 2: 新規プロジェクト作成

  1. 画面上部の プロジェクト選択 ドロップダウンをクリック
  2. 「新しいプロジェクト」 をクリック
  3. プロジェクト情報を入力:
    項目入力例
    プロジェクト名chat-story
    場所「組織なし」または所属組織
  4. 「作成」 をクリック
  5. 作成完了後、そのプロジェクトが選択されていることを確認

3. OAuth 同意画面の設定

Step 3: OAuth 同意画面を構成

※ この設定は必須です。先にこれをしないと認証情報を作成できません。

  1. 左側メニューから 「APIとサービス」「OAuth 同意画面」 を選択
  2. User Type を選択:
    タイプ説明推奨
    外部すべてのGoogleアカウントが対象◯ 一般公開サービス向け
    内部Google Workspace組織内のみ社内ツール向け

「外部」 を選択して 「作成」

  1. アプリ情報 を入力:
項目入力内容
アプリ名サービス名Chat Story
ユーザーサポートメール問い合わせ先support@example.com
アプリのロゴ(任意)ロゴ画像-
  1. アプリのドメイン(任意、本番環境用):
項目入力内容
アプリケーションのホームページhttps://chat-story.number55number55.workers.dev
プライバシーポリシーのリンクhttps://chat-story.../privacy
利用規約のリンクhttps://chat-story.../terms
  1. デベロッパーの連絡先情報
    • メールアドレスを入力
  2. 「保存して次へ」 をクリック

Step 4: スコープの設定

  1. 「スコープを追加または削除」 をクリック
  2. 以下のスコープを選択:
    ✅ .../auth/userinfo.email    (メールアドレス取得)
    ✅ .../auth/userinfo.profile  (プロフィール情報取得)
    ✅ openid                      (OpenID Connect)
    
  3. 「更新」 をクリック
  4. 「保存して次へ」 をクリック

Step 5: テストユーザーの追加(開発中のみ)

※ 公開ステータスが「テスト」の間は、ここに登録したユーザーのみログイン可能です。

  1. 「+ ADD USERS」 をクリック
  2. テストに使用するGoogleアカウントのメールアドレスを追加
    例: your-email@gmail.com
    
  3. 「保存して次へ」 をクリック
  4. 概要を確認して 「ダッシュボードに戻る」

4. OAuth 認証情報の作成

Step 6: 認証情報を作成

  1. 左側メニューから 「APIとサービス」「認証情報」 を選択
  2. 上部の 「+ 認証情報を作成」 をクリック
  3. 「OAuth クライアント ID」 を選択
  4. アプリケーションの種類 を選択:
    ウェブ アプリケーション
    
  5. 名前 を入力:
    Chat Story Web Client
    
  6. 承認済みの JavaScript 生成元(開発環境 + 本番環境):
    http://localhost:3000
    http://localhost:5173
    https://chat-story.number55number55.workers.dev
    
  7. 承認済みのリダイレクト URI(重要!):
    http://localhost:3000/api/auth/callback/google
    http://localhost:5173/api/auth/callback/google
    https://chat-story.number55number55.workers.dev/api/auth/callback/google
    

⚠️ 注意: Better Auth のデフォルトコールバックパスは /api/auth/callback/google です。 カスタマイズしている場合は適宜変更してください。

  1. 「作成」 をクリック

Step 7: 認証情報を取得

作成完了すると、以下の情報が表示されます:

┌─────────────────────────────────────────────────────┐
│  OAuth クライアントを作成しました                      │
│                                                      │
│  クライアント ID:                                     │
│  123456789-abcdefg.apps.googleusercontent.com       │
│                                                      │
│  クライアント シークレット:                           │
│  GOCSPX-xxxxxxxxxxxxxxxxxxxxxxxx                    │
│                                                      │
│  [JSON をダウンロード]  [OK]                          │
└─────────────────────────────────────────────────────┘

この情報を安全な場所に保存してください!

  • Client ID: GOOGLE_CLIENT_ID として使用
  • Client Secret: GOOGLE_CLIENT_SECRET として使用

5. 環境変数の設定

Cloudflare Workers の場合

方法1: wrangler.toml に記載(開発用)

# wrangler.toml
[vars]
GOOGLE_CLIENT_ID = "123456789-abcdefg.apps.googleusercontent.com"

# シークレットは vars に書かない!

方法2: Wrangler CLI でシークレット設定(推奨)

# Client Secret を安全に設定
npx wrangler secret put GOOGLE_CLIENT_SECRET
# プロンプトが出たら値を入力

# 確認
npx wrangler secret list

方法3: Cloudflare Dashboard から設定

  1. https://dash.cloudflare.com/ にアクセス
  2. Workers & Pages → 対象のWorker を選択
  3. SettingsVariables タブ
  4. Environment Variables セクションで追加:
Variable nameValueEncrypt
GOOGLE_CLIENT_ID123456789-xxx.apps.googleusercontent.comNo
GOOGLE_CLIENT_SECRETGOCSPX-xxxYes

6. Better Auth での設定

auth.ts の設定

import { betterAuth } from "better-auth";

export const auth = betterAuth({
  database: /* your database adapter */,
  
  // メール/パスワード認証
  emailAndPassword: {
    enabled: true,
  },
  
  // Google OAuth
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
      // オプション: 追加スコープ
      // scope: ["email", "profile", "openid"],
    },
  },
});

Cloudflare Workers (Hono) での設定

// src/index.ts
import { Hono } from "hono";
import { auth } from "./auth";

type Bindings = {
  GOOGLE_CLIENT_ID: string;
  GOOGLE_CLIENT_SECRET: string;
  DB: D1Database;
};

const app = new Hono<{ Bindings: Bindings }>();

// Better Auth のハンドラーを設定
app.on(["GET", "POST"], "/api/auth/*", (c) => {
  return auth.handler(c.req.raw);
});

export default app;
// src/auth.ts (Cloudflare Workers 用)
import { betterAuth } from "better-auth";
import { D1Adapter } from "better-auth/adapters/d1";

export function createAuth(env: {
  GOOGLE_CLIENT_ID: string;
  GOOGLE_CLIENT_SECRET: string;
  DB: D1Database;
}) {
  return betterAuth({
    database: D1Adapter(env.DB),
    emailAndPassword: {
      enabled: true,
    },
    socialProviders: {
      google: {
        clientId: env.GOOGLE_CLIENT_ID,
        clientSecret: env.GOOGLE_CLIENT_SECRET,
      },
    },
  });
}

7. クライアント側の実装

Google ログインボタン

// components/GoogleLoginButton.tsx
import { authClient } from "@/lib/auth-client";

export function GoogleLoginButton() {
  const handleGoogleLogin = async () => {
    await authClient.signIn.social({
      provider: "google",
      callbackURL: "/dashboard", // ログイン後のリダイレクト先
    });
  };

  return (
    <button
      onClick={handleGoogleLogin}
      className="flex items-center gap-2 px-4 py-2 border rounded-lg hover:bg-gray-50"
    >
      <GoogleIcon />
      Googleでログイン
    </button>
  );
}

function GoogleIcon() {
  return (
    <svg className="w-5 h-5" viewBox="0 0 24 24">
      <path
        fill="#4285F4"
        d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
      />
      <path
        fill="#34A853"
        d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
      />
      <path
        fill="#FBBC05"
        d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
      />
      <path
        fill="#EA4335"
        d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
      />
    </svg>
  );
}

ログインページ全体の例

// pages/login.tsx
import { GoogleLoginButton } from "@/components/GoogleLoginButton";
import { authClient } from "@/lib/auth-client";
import { useState } from "react";

export function LoginPage() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");

  const handleEmailLogin = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      await authClient.signIn.email({ email, password });
      window.location.href = "/dashboard";
    } catch (err) {
      setError("ログインに失敗しました");
    }
  };

  return (
    <div className="max-w-md mx-auto mt-20 p-6">
      <h1 className="text-2xl font-bold mb-6">ログイン</h1>

      {/* Google ログイン */}
      <GoogleLoginButton />

      <div className="my-6 flex items-center">
        <hr className="flex-1" />
        <span className="px-4 text-gray-500">または</span>
        <hr className="flex-1" />
      </div>

      {/* メール/パスワード ログイン */}
      <form onSubmit={handleEmailLogin} className="space-y-4">
        {error && <p className="text-red-500">{error}</p>}
        
        <input
          type="email"
          placeholder="メールアドレス"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          className="w-full px-4 py-2 border rounded"
        />
        
        <input
          type="password"
          placeholder="パスワード"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          className="w-full px-4 py-2 border rounded"
        />
        
        <button
          type="submit"
          className="w-full px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
        >
          ログイン
        </button>
      </form>
    </div>
  );
}

8. 本番公開時の設定

OAuth 同意画面を「本番」に変更

開発中は「テスト」モードですが、本番公開時は以下の手順が必要です:

  1. Google Cloud ConsoleOAuth 同意画面
  2. 「アプリを公開」 をクリック
  3. Google による審査(通常数日〜数週間)
    • プライバシーポリシーページが必要
    • 利用規約ページが必要
    • ドメインの所有権確認が必要な場合あり

審査を避ける方法(小規模サービス向け)

以下の条件を満たす場合、審査なしで公開可能:

  • 機密性の高いスコープを使用しない
    • email, profile, openid のみならOK
    • Gmail API や Drive API などは審査必要
  • ユーザー数が100人未満

9. トラブルシューティング

エラー: "redirect_uri_mismatch"

原因: 設定したリダイレクトURIとアプリからのURIが一致しない

解決方法:

  1. Google Cloud Console で 承認済みのリダイレクト URI を確認
  2. 末尾のスラッシュ / の有無を確認
  3. httphttps の違いを確認
  4. ポート番号の確認
❌ http://localhost:3000/api/auth/callback/google/
✅ http://localhost:3000/api/auth/callback/google

エラー: "access_denied"

原因: テストモード中に未登録ユーザーがアクセス

解決方法:

  1. OAuth 同意画面 → テストユーザーに追加
  2. または「アプリを公開」で本番モードに

エラー: "invalid_client"

原因: Client ID または Client Secret が間違っている

解決方法:

  1. 環境変数が正しく設定されているか確認
  2. コピペ時の余分な空白がないか確認
  3. 認証情報を再作成

ログインしてもユーザー情報が取得できない

原因: スコープが不足している

解決方法:

socialProviders: {
  google: {
    clientId: env.GOOGLE_CLIENT_ID,
    clientSecret: env.GOOGLE_CLIENT_SECRET,
    scope: ["email", "profile", "openid"], // 明示的に指定
  },
},

10. セキュリティ チェックリスト

✅ Client Secret は環境変数で管理(コードに直書きしない)
✅ Client Secret は暗号化して保存(Cloudflare の Encrypt オプション)
✅ 本番環境では HTTPS のみを許可
✅ 不要になった認証情報は削除
✅ 定期的に認証情報をローテーション
✅ OAuth 同意画面の情報を最新に保つ

参考リンク

リソースURL
Google Cloud Consolehttps://console.cloud.google.com/
OAuth 2.0 ドキュメントhttps://developers.google.com/identity/protocols/oauth2
Better Auth - Google Providerhttps://www.better-auth.com/docs/authentication/social-providers
Cloudflare Workers Secretshttps://developers.cloudflare.com/workers/configuration/secrets/

このドキュメントは Chat Story サービスの Google OAuth 設定ガイドとして作成されました。