Files
CFDivePlatform/app/Http/Controllers/API/SocialAuthController.php
T
a620906209 550e2fc97a feat:實作 Member Portal MVP 前端與後端整合
後端:
- 新增 DivingOffer Model / DivingOfferController(列表+詳情 API,支援搜尋/篩選/分頁)
- 修正 Google OAuth callback 改為 redirect 至前端(SocialAuthController)
- 新增 config/cors.php 允許前端 origin
- .gitignore 新增 frontend/ 排除規則

前端(frontend/):
- Vue 3 + Vite + Tailwind CSS + Pinia + Vue Router
- 頁面:首頁、課程列表、課程詳情、登入、註冊、個人資料、OAuth callback
- 整合至 Docker(multi-stage build,nginx 靜態服務於 port 5173)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-10 01:41:28 +08:00

115 lines
4.3 KiB
PHP

<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\MemberProfile;
use App\Models\SocialAccount;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Laravel\Socialite\Facades\Socialite;
class SocialAuthController extends Controller
{
/**
* 重定向到 Google 登入頁面
*/
public function redirectToGoogle()
{
return Socialite::driver('google')
->scopes(['openid', 'profile', 'email'])
->with(['access_type' => 'offline', 'prompt' => 'consent']) // 這裡要求 prompt=consent 才能每次都獲取 refresh token
->stateless()
->redirect();
}
/**
* 處理 Google 回調
*/
public function handleGoogleCallback(Request $request)
{
try {
// 獲取 Google 用戶資訊
$googleUser = Socialite::driver('google')->stateless()->user();
// 查找相關的社交帳號
$socialAccount = SocialAccount::where('provider', 'google')
->where('provider_id', $googleUser->getId())
->first();
if ($socialAccount) {
// 已存在社交帳號,直接獲取用戶
$user = $socialAccount->user;
// 如果用戶不是會員,拒絕登入
if ($user->role !== 'member') {
return response()->json([
'status' => false,
'message' => '只有會員可以使用 Google 登入'
], 403);
}
} else {
// 檢查是否有相同 email 的用戶
$user = User::where('email', $googleUser->getEmail())->first();
if ($user) {
// 已存在用戶,但沒有連結社交帳號
// 檢查是否為會員
if ($user->role !== 'member') {
return response()->json([
'status' => false,
'message' => '只有會員可以使用 Google 登入'
], 403);
}
} else {
// 建立新用戶
$user = User::create([
'name' => $googleUser->getName(),
'email' => $googleUser->getEmail(),
'password' => Hash::make(Str::random(24)),
'role' => 'member', // 強制為會員角色
'is_active' => true,
]);
// 建立會員資料
try {
MemberProfile::create([
'user_id' => $user->id,
// 可以選擇性地從 Google 獲取更多資訊
]);
} catch (\Exception $e) {
// 記錄錯誤,但不中斷整個登入流程
\Log::error('Google 登入建立會員資料失敗: ' . $e->getMessage());
}
}
// 建立社交帳號連結
$socialAccountData = [
'user_id' => $user->id,
'provider' => 'google',
'provider_id' => $googleUser->getId(),
'provider_email' => $googleUser->getEmail(),
'access_token' => $googleUser->token,
'expires_in' => $googleUser->expiresIn ?? null,
];
// 確保如果有 refreshToken 就正確地儲存
if (!empty($googleUser->refreshToken)) {
$socialAccountData['refresh_token'] = $googleUser->refreshToken;
}
$socialAccount = SocialAccount::create($socialAccountData);
}
// 生成 Sanctum token
$token = $user->createToken('google-auth')->plainTextToken;
return redirect(env('FRONTEND_URL') . '/auth/callback?token=' . $token);
} catch (\Exception $e) {
return redirect(env('FRONTEND_URL') . '/login?error=oauth_failed');
}
}
}