OAuth Direct Setup - knpy/yuka-app GitHub Wiki

Google OAuth 直接設定ガイド

概要

NextAuth.jsを使わずに、Google OAuthを直接実装する場合の設定手順。

Google Cloud Console設定

1. プロジェクトの準備

  1. Google Cloud Consoleにアクセス
  2. 新しいプロジェクトを作成、または既存プロジェクトを選択

2. APIs & Services有効化

  1. 「APIs & Services」→「Library」
  2. 以下のAPIを有効化:
    • Google+ API
    • Google Calendar API
    • People API

3. OAuth 2.0認証情報作成

  1. 「APIs & Services」→「Credentials」
  2. 「+ CREATE CREDENTIALS」→「OAuth 2.0 Client IDs」
  3. Application type: Web application
  4. Authorized JavaScript origins:
    http://localhost:3000
    https://your-domain.com
    
  5. Authorized redirect URIs:
    http://localhost:3000/auth/callback
    https://your-domain.com/auth/callback
    

4. OAuth Consent Screen設定

  1. 「OAuth consent screen」タブ
  2. User Type: External
  3. App information:
  4. Scopes:
    ../auth/userinfo.email
    ../auth/userinfo.profile
    ../auth/calendar.readonly
    

フロントエンド実装

1. 環境変数設定

GOOGLE_CLIENT_ID=your_client_id
GOOGLE_REDIRECT_URI=http://localhost:3000/auth/callback

2. OAuth認証開始

const initiateGoogleAuth = () => {
  const clientId = process.env.GOOGLE_CLIENT_ID;
  const redirectUri = process.env.GOOGLE_REDIRECT_URI;
  const scope = [
    'openid',
    'email',
    'profile',
    'https://www.googleapis.com/auth/calendar.readonly'
  ].join(' ');
  
  const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?` +
    `client_id=${clientId}&` +
    `redirect_uri=${encodeURIComponent(redirectUri)}&` +
    `response_type=code&` +
    `scope=${encodeURIComponent(scope)}&` +
    `access_type=offline&` +
    `prompt=consent`;
    
  window.location.href = authUrl;
};

3. コールバック処理

// pages/auth/callback.tsx
import { useEffect } from 'react';
import { useRouter } from 'next/router';

export default function AuthCallback() {
  const router = useRouter();
  
  useEffect(() => {
    const { code } = router.query;
    if (code) {
      exchangeCodeForTokens(code as string);
    }
  }, [router.query]);
  
  const exchangeCodeForTokens = async (code: string) => {
    try {
      const response = await fetch('/api/auth/token', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ code })
      });
      
      const tokens = await response.json();
      // トークンを安全に保存
      localStorage.setItem('accessToken', tokens.access_token);
      router.push('/dashboard');
    } catch (error) {
      console.error('Token exchange failed:', error);
    }
  };
  
  return <div>認証中...</div>;
}

バックエンド実装

1. トークン交換API

// pages/api/auth/token.ts
export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }
  
  const { code } = req.body;
  
  try {
    const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        client_id: process.env.GOOGLE_CLIENT_ID,
        client_secret: process.env.GOOGLE_CLIENT_SECRET,
        code,
        grant_type: 'authorization_code',
        redirect_uri: process.env.GOOGLE_REDIRECT_URI
      })
    });
    
    const tokens = await tokenResponse.json();
    res.json(tokens);
  } catch (error) {
    res.status(500).json({ error: 'Token exchange failed' });
  }
}

2. Calendar API呼び出し

// pages/api/calendar/events.ts
export default async function handler(req, res) {
  const { authorization } = req.headers;
  
  if (!authorization) {
    return res.status(401).json({ error: 'Authorization required' });
  }
  
  const accessToken = authorization.replace('Bearer ', '');
  
  try {
    const calendarResponse = await fetch(
      'https://www.googleapis.com/calendar/v3/calendars/primary/events',
      {
        headers: { Authorization: `Bearer ${accessToken}` }
      }
    );
    
    const events = await calendarResponse.json();
    res.json(events);
  } catch (error) {
    res.status(500).json({ error: 'Calendar API call failed' });
  }
}

セキュリティ考慮事項

1. トークン管理

  • アクセストークンは適切に暗号化して保存
  • リフレッシュトークンを使用して自動更新
  • トークンの有効期限チェック

2. CORS設定

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/api/:path*',
        headers: [
          { key: 'Access-Control-Allow-Origin', value: 'your-domain.com' },
          { key: 'Access-Control-Allow-Methods', value: 'GET,POST,OPTIONS' }
        ]
      }
    ];
  }
};

3. 環境変数保護

  • クライアントサイドに秘密情報を露出しない
  • HTTPS通信の強制
  • CSRFトークンの実装

トラブルシューティング

よくある問題

  1. Invalid redirect URI

    • Google Cloud Consoleで正確なURIを設定
  2. Access denied

    • OAuth consent screenの設定確認
    • 必要なスコープの設定確認
  3. Token expired

    • リフレッシュトークンの実装
    • トークン更新ロジックの追加

参考資料

⚠️ **GitHub.com Fallback** ⚠️