isGoogleLoginConfigured()) { return redirect()->route('login')->withErrors([ 'google' => 'Login Google belum dikonfigurasi. Isi GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, dan GOOGLE_REDIRECT_URI terlebih dulu.', ]); } $state = Str::random(40); $request->session()->put('google_oauth_state', $state); $query = http_build_query([ 'client_id' => (string) config('services.google.client_id'), 'redirect_uri' => (string) config('services.google.redirect'), 'response_type' => 'code', 'scope' => 'openid profile email', 'prompt' => 'select_account', 'access_type' => 'offline', 'state' => $state, ]); return redirect()->away('https://accounts.google.com/o/oauth2/v2/auth?'.$query); } public function callback(Request $request): RedirectResponse { $storedState = (string) $request->session()->pull('google_oauth_state', ''); $returnedState = (string) $request->string('state'); if ($request->filled('error')) { return redirect()->route('login')->withErrors([ 'google' => 'Login Google dibatalkan atau ditolak oleh pengguna.', ]); } if ($storedState === '' || ! hash_equals($storedState, $returnedState)) { return redirect()->route('login')->withErrors([ 'google' => 'Sesi login Google tidak valid. Coba ulangi lagi.', ]); } if (! $this->isGoogleLoginConfigured()) { return redirect()->route('login')->withErrors([ 'google' => 'Login Google belum dikonfigurasi di server.', ]); } $tokenResponse = Http::asForm() ->timeout(20) ->post('https://oauth2.googleapis.com/token', [ 'code' => (string) $request->string('code'), 'client_id' => (string) config('services.google.client_id'), 'client_secret' => (string) config('services.google.client_secret'), 'redirect_uri' => (string) config('services.google.redirect'), 'grant_type' => 'authorization_code', ]); if (! $tokenResponse->ok() || ! $tokenResponse->json('access_token')) { return redirect()->route('login')->withErrors([ 'google' => 'Google tidak mengembalikan access token yang valid.', ]); } $googleProfile = Http::withToken((string) $tokenResponse->json('access_token')) ->timeout(20) ->get('https://openidconnect.googleapis.com/v1/userinfo'); if (! $googleProfile->ok()) { return redirect()->route('login')->withErrors([ 'google' => 'Profil akun Google tidak bisa diambil saat ini.', ]); } $profile = $googleProfile->json(); $googleId = trim((string) data_get($profile, 'sub', '')); $email = Str::lower(trim((string) data_get($profile, 'email', ''))); if ($googleId === '' || $email === '') { return redirect()->route('login')->withErrors([ 'google' => 'Data akun Google tidak lengkap. Pastikan email tersedia di akun Google Anda.', ]); } $user = User::query() ->where('google_id', $googleId) ->orWhere('email', $email) ->first(); if (! $user) { $user = User::create([ 'name' => trim((string) data_get($profile, 'name', Str::before($email, '@'))), 'email' => $email, 'google_id' => $googleId, 'email_verified_at' => data_get($profile, 'email_verified') ? Carbon::now() : null, 'password' => Hash::make(Str::random(40)), ]); } else { $user->forceFill([ 'name' => $user->name ?: trim((string) data_get($profile, 'name', Str::before($email, '@'))), 'google_id' => $user->google_id ?: $googleId, ]); if (! $user->email_verified_at && data_get($profile, 'email_verified')) { $user->email_verified_at = Carbon::now(); } $user->save(); } Auth::login($user, true); $request->session()->regenerate(); return redirect()->intended(RouteServiceProvider::HOME); } private function isGoogleLoginConfigured(): bool { return filled(config('services.google.client_id')) && filled(config('services.google.client_secret')) && filled(config('services.google.redirect')); } }