PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ] ); } catch (PDOException $e) { logError('Database connection failed', ['error' => $e->getMessage()]); throw new Exception('データベース接続に失敗しました。しばらくしてから再度お試しください。'); } // エラーメッセージ初期化 $error = ''; // Googleからのコールバック処理 if (isset($_GET['code'])) { try { // CSRFチェック if (!isset($_GET['state']) || empty($_SESSION['oauth2state']) || $_GET['state'] !== $_SESSION['oauth2state']) { logError('Invalid state parameter', [ 'expected' => $_SESSION['oauth2state'] ?? 'NOT_SET', 'received' => $_GET['state'] ?? 'NOT_SET' ]); throw new logError('セキュリティチェックに失敗しました。もう一度お試しください。'); } // トークンエンドポイントにリクエスト $tokenUrl = 'https://oauth2.googleapis.com/token'; $postData = [ 'code' => $_GET['code'], 'client_id' => CLIENT_ID, 'client_secret' => CLIENT_SECRET, 'redirect_uri' => REDIRECT_URI, 'grant_type' => 'authorization_code' ]; $ch = curl_init($tokenUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($postData), CURLOPT_SSL_VERIFYPEER => true, CURLOPT_TIMEOUT => 10, ]); $response = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($status !== 200 || $curlError) { logError('Token request failed', [ 'status' => $status, 'response' => $response, 'curl_error' => $curlError ]); throw new logError('認証サーバーとの通信に失敗しました。'); } $tokenData = json_decode($response, true); if (json_last_error() !== JSON_ERROR_NONE) { logError('JSON decode error', ['response' => $response]); throw new logError('認証情報の処理に失敗しました。'); } $accessToken = $tokenData['access_token'] ?? null; if (!$accessToken) { logError('Access token missing', ['token_data' => $tokenData]); throw new logError('認証トークンの取得に失敗しました。'); } // ユーザー情報取得 $userInfoUrl = 'https://people.googleapis.com/v1/people/me?personFields=emailAddresses,names,photos'; $ch = curl_init($userInfoUrl); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $accessToken, 'Accept: application/json' ], CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_TIMEOUT => 10, ]); $userResponse = curl_exec($ch); $userStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE); $userCurlError = curl_error($ch); curl_close($ch); if ($userStatus !== 200 || $userCurlError) { logError('User info request failed', [ 'status' => $userStatus, 'response' => $userResponse, 'curl_error' => $userCurlError ]); throw new logError('ユーザー情報の取得に失敗しました。'); } $userData = json_decode($userResponse, true); if (json_last_error() !== JSON_ERROR_NONE) { logError('User info JSON decode error', ['response' => $userResponse]); throw new logError('ユーザー情報の処理に失敗しました。'); } // 必要なユーザーデータの検証 if (empty($userData['emailAddresses'][0]['value'])) { logError('Incomplete user data', ['user_data' => $userData]); throw new logError('必要なユーザー情報が取得できませんでした。'); } // ユーザー情報を準備 $googleId = $userData['resourceName'] ?? ''; $email = $userData['emailAddresses'][0]['value']; $googleName = $userData['names'][0]['displayName'] ?? 'Unknown'; $photoUrl = $userData['photos'][0]['url'] ?? ''; $currentDate = date('Y-m-d H:i:s'); $ipAddress = $_SERVER['REMOTE_ADDR'] ?? 'unknown'; // トランザクション開始 $pdo->beginTransaction(); try { // ユーザーが既に存在するか確認 $stmt = $pdo->prepare("SELECT * FROM user_data WHERE google_id = ?"); $stmt->execute([$googleId]); $existingUser = $stmt->fetch(); if ($existingUser) { // 既存ユーザー - ログイン履歴を更新 $history = json_decode($existingUser['history'], true) ?: []; $history[] = [ 'type' => 'login', 'date' => $currentDate, 'ip' => $ipAddress ]; $stmt = $pdo->prepare("UPDATE user_data SET last_login = ?, last_ip = ?, history = ? WHERE google_id = ?"); $stmt->execute([ $currentDate, $ipAddress, json_encode($history), $googleId ]); // セッションにユーザー情報を保存 $_SESSION['user_id'] = $googleId; $_SESSION['user_email'] = $email; $_SESSION['user_name'] = $existingUser['display_name'] ?? $googleName; $_SESSION['display_name'] = $existingUser['display_name'] ?? $googleName; $_SESSION['icon_url'] = $existingUser['icon_url'] ?? $photoUrl; $_SESSION['access_token'] = $accessToken; } else { // 新規ユーザー - データベースに登録 $history = [ [ 'type' => 'join', 'date' => $currentDate, 'ip' => $ipAddress ] ]; $stmt = $pdo->prepare("INSERT INTO user_data ( google_id, google_name, display_name, email, icon_url, created_at, history, last_login, last_ip ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([ $googleId, $googleName, $googleName, $email, $photoUrl, $currentDate, json_encode($history), $currentDate, $ipAddress ]); // セッションにユーザー情報を保存 $_SESSION['user_id'] = $googleId; $_SESSION['user_email'] = $email; $_SESSION['user_name'] = $googleName; $_SESSION['display_name'] = $googleName; $_SESSION['icon_url'] = $photoUrl; $_SESSION['access_token'] = $accessToken; } $pdo->commit(); // 元のページにリダイレクト $returnTo = $_SESSION['return_to'] ?? 'index.php'; unset($_SESSION['return_to'], $_SESSION['oauth2state']); header('Location: ' . $returnTo); exit; } catch (Exception $e) { $pdo->rollBack(); logError('Database operation failed', ['error' => $e->getMessage()]); throw new Exception('ユーザーデータの処理中にエラーが発生しました。'); } } catch (Exception $e) { $error = $e->getMessage(); logError('Authentication failed', ['error' => $error]); } } // ログインページ表示 $authUrl = 'https://accounts.google.com/o/oauth2/v2/auth?' . http_build_query([ 'client_id' => CLIENT_ID, 'redirect_uri' => REDIRECT_URI, 'response_type' => 'code', 'scope' => SCOPES, 'access_type' => 'online', 'state' => ($_SESSION['oauth2state'] = bin2hex(random_bytes(16))), 'prompt' => 'select_account' ]); // リダイレクト前のページを記録 if (!isset($_SESSION['return_to']) && isset($_SERVER['HTTP_REFERER'])) { $_SESSION['return_to'] = $_SERVER['HTTP_REFERER']; } } catch (Exception $e) { $error = $e->getMessage(); logError('System error', ['error' => $error]); } ?>
ログインすることで、当サービスの利用規約とプライバシーポリシーに同意するものとします。