О нас Руководства Проекты Контакты
Админка
пожалуйста подождите

Введение

Социальная аутентификация упрощает онбординг пользователей за счёт использования существующих аккаунтов у таких провайдеров, как Google, Facebook, Apple и GitHub. Пользователям не нужно создавать ещё один пароль, а вы получаете доступ к подтверждённым адресам электронной почты. В этом руководстве рассматривается реализация социального входа на основе OAuth для основных провайдеров с практическими примерами на Laravel Socialite.

Обзор OAuth Flow

1. User clicks "Sign in with Google"
2. Redirect to Google's authorization URL
3. User authenticates with Google
4. Google redirects back with authorization code
5. Exchange code for access token
6. Fetch user profile with token
7. Create/login user in your system

Настройка Laravel Socialite

Установка

composer require laravel/socialite

Конфигурация

// config/services.php
return [
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_REDIRECT_URI'),
],
'facebook' => [
'client_id' => env('FACEBOOK_CLIENT_ID'),
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
'redirect' => env('FACEBOOK_REDIRECT_URI'),
],
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'redirect' => env('GITHUB_REDIRECT_URI'),
],
'apple' => [
'client_id' => env('APPLE_CLIENT_ID'),
'client_secret' => env('APPLE_CLIENT_SECRET'),
'redirect' => env('APPLE_REDIRECT_URI'),
],
];

Вход через Google

Настройка Google OAuth

  1. Перейдите в Google Cloud Console
  2. Создайте проект
  3. Включите "Google+ API" или "Google Identity Platform"
  4. Перейдите в Credentials → Create OAuth Client ID
  5. Настройте authorized redirect URIs: https://yourapp.com/auth/google/callback

Реализация Controller

// routes/web.php
Route::get('/auth/{provider}', [SocialAuthController::class, 'redirect']);
Route::get('/auth/{provider}/callback', [SocialAuthController::class, 'callback']);
// app/Http/Controllers/SocialAuthController.php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
use Illuminate\Support\Str;
class SocialAuthController extends Controller
{
protected $providers = ['google', 'facebook', 'github', 'apple'];
public function redirect($provider)
{
if (!in_array($provider, $this->providers)) {
abort(404);
}
return Socialite::driver($provider)->redirect();
}
public function callback($provider)
{
if (!in_array($provider, $this->providers)) {
abort(404);
}
try {
$socialUser = Socialite::driver($provider)->user();
} catch (\Exception $e) {
return redirect('/login')->with('error', 'Authentication failed');
}
$user = $this->findOrCreateUser($socialUser, $provider);
Auth::login($user, true);
return redirect()->intended('/dashboard');
}
protected function findOrCreateUser($socialUser, $provider)
{
// Проверьте, существует ли пользователь с этим провайдером
$socialAccount = \App\Models\SocialAccount::where('provider', $provider)
->where('provider_user_id', $socialUser->getId())
->first();
if ($socialAccount) {
return $socialAccount->user;
}
// Проверьте, существует ли пользователь с тем же email
$user = User::where('email', $socialUser->getEmail())->first();
if (!$user) {
// Создайте нового пользователя
$user = User::create([
'name' => $socialUser->getName(),
'email' => $socialUser->getEmail(),
'email_verified_at' => now(),
'password' => bcrypt(Str::random(24)),
'avatar' => $socialUser->getAvatar(),
]);
}
// Привяжите социальный аккаунт
$user->socialAccounts()->create([
'provider' => $provider,
'provider_user_id' => $socialUser->getId(),
'token' => $socialUser->token,
'refresh_token' => $socialUser->refreshToken,
]);
return $user;
}
}

Настройка базы данных

// Миграция для таблицы social_accounts
Schema::create('social_accounts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('provider');
$table->string('provider_user_id');
$table->text('token')->nullable();
$table->text('refresh_token')->nullable();
$table->timestamps();
$table->unique(['provider', 'provider_user_id']);
});
// app/Models/SocialAccount.php
class SocialAccount extends Model
{
protected $fillable = [
'user_id',
'provider',
'provider_user_id',
'token',
'refresh_token',
];
public function user()
{
return $this->belongsTo(User::class);
}
}
// app/Models/User.php
class User extends Authenticatable
{
public function socialAccounts()
{
return $this->hasMany(SocialAccount::class);
}
}

Вход через Facebook

Настройка Facebook OAuth

  1. Перейдите в Facebook Developers
  2. Создайте приложение
  3. Добавьте продукт Facebook Login
  4. Настройте Valid OAuth Redirect URIs
  5. Получите App ID и App Secret в Settings

Дополнительные Scopes

public function redirect($provider)
{
$driver = Socialite::driver($provider);
if ($provider === 'facebook') {
$driver->scopes(['email', 'public_profile']);
}
return $driver->redirect();
}

Вход через Apple

Настройка Apple OAuth

  1. Перейдите в Apple Developer Console
  2. Создайте App ID с возможностью Sign in with Apple
  3. Создайте Services ID
  4. Настройте Return URLs
  5. Сгенерируйте client secret (требуется private key)

Обработка, специфичная для Apple

Apple требует особой обработки, поскольку возвращает информацию о пользователе только при первой аутентификации:

public function callback($provider)
{
if ($provider === 'apple') {
return $this->handleAppleCallback();
}
// ... стандартная обработка
}
protected function handleAppleCallback()
{
$socialUser = Socialite::driver('apple')->user();
// Apple отправляет имя только при первой аутентификации
$name = $socialUser->getName();
if (!$name) {
// Попробуйте получить его из ранее сохранённых данных или запросить
$name = request('user.name.firstName') . ' ' . request('user.name.lastName');
}
// Email может быть приватным relay
$email = $socialUser->getEmail();
// ... создайте/найдите пользователя
}

Вход через GitHub

Настройка GitHub OAuth

  1. Перейдите в GitHub Developer Settings
  2. Создайте OAuth App
  3. Укажите Authorization callback URL
  4. Получите Client ID и Client Secret

Запрос дополнительных Scopes

public function redirect($provider)
{
$driver = Socialite::driver($provider);
if ($provider === 'github') {
$driver->scopes(['user:email', 'read:user']);
}
return $driver->redirect();
}

Интеграция с фронтендом

Кнопки входа

<div class="social-login">
<a href="/auth/google" class="btn btn-google">
<img src="/icons/google.svg" alt="">
Continue with Google
</a>
<a href="/auth/facebook" class="btn btn-facebook">
<img src="/icons/facebook.svg" alt="">
Continue with Facebook
</a>
<a href="/auth/apple" class="btn btn-apple">
<img src="/icons/apple.svg" alt="">
Continue with Apple
</a>
<a href="/auth/github" class="btn btn-github">
<img src="/icons/github.svg" alt="">
Continue with GitHub
</a>
</div>

Компонент Vue.js

<template>
<div class="social-auth">
<button
v-for="provider in providers"
:key="provider.name"
@click="authenticate(provider.name)"
:class="`btn-${provider.name}`"
:disabled="loading"
>
<component :is="provider.icon" />
{{ loading ? 'Connecting...' : `Continue with ${provider.label}` }}
</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const loading = ref(false);
const providers = [
{ name: 'google', label: 'Google' },
{ name: 'facebook', label: 'Facebook' },
{ name: 'apple', label: 'Apple' },
];
function authenticate(provider) {
loading.value = true;
window.location.href = `/auth/${provider}`;
}
</script>

Аутентификация SPA/API

Для одностраничных приложений используйте stateless OAuth:

// Верните token вместо session
public function callback($provider)
{
$socialUser = Socialite::driver($provider)->stateless()->user();
$user = $this->findOrCreateUser($socialUser, $provider);
// Создайте API-токен
$token = $user->createToken('social-auth')->plainTextToken;
// Сделайте redirect на фронтенд с token
return redirect(config('app.frontend_url') . '/auth/callback?token=' . $token);
}

Соображения безопасности

Параметр state

Socialite автоматически обрабатывает защиту от CSRF с помощью параметра state. Для stateless-запросов:

// Сгенерируйте и проверьте state вручную для SPA
$state = Str::random(40);
session(['oauth_state' => $state]);
return Socialite::driver($provider)
->with(['state' => $state])
->redirect();

Верификация email

Социальные провайдеры обычно подтверждают email, но проверьте гарантии провайдера:

$user = User::create([
'email' => $socialUser->getEmail(),
'email_verified_at' => $this->providerVerifiesEmail($provider)
? now()
: null,
]);
protected function providerVerifiesEmail($provider)
{
return in_array($provider, ['google', 'apple', 'github']);
}

Связывание аккаунтов

Разрешите пользователям привязывать нескольких провайдеров:

public function link($provider)
{
// Требуется аутентификация
$user = auth()->user();
$socialUser = Socialite::driver($provider)->user();
// Проверьте, не привязано ли уже к другому аккаунту
$existing = SocialAccount::where('provider', $provider)
->where('provider_user_id', $socialUser->getId())
->first();
if ($existing && $existing->user_id !== $user->id) {
return back()->with('error', 'This account is linked to another user');
}
$user->socialAccounts()->updateOrCreate(
['provider' => $provider],
[
'provider_user_id' => $socialUser->getId(),
'token' => $socialUser->token,
]
);
return back()->with('success', 'Account linked successfully');
}

Обработка ошибок

public function callback($provider)
{
try {
$socialUser = Socialite::driver($provider)->user();
} catch (\Laravel\Socialite\Two\InvalidStateException $e) {
return redirect('/login')
->with('error', 'Session expired. Please try again.');
} catch (\GuzzleHttp\Exception\ClientException $e) {
return redirect('/login')
->with('error', 'Authentication was denied.');
} catch (\Exception $e) {
Log::error('Social auth error', [
'provider' => $provider,
'error' => $e->getMessage(),
]);
return redirect('/login')
->with('error', 'Authentication failed. Please try again.');
}
// ... продолжите создание пользователя
}

Прямая интеграция через JavaScript SDK

В случаях, когда нужен больший контроль или вы не используете Laravel Socialite, можно интегрироваться напрямую с SDK провайдера.

Google Sign-In с JavaScript

<div id="g_id_signin"></div>
<script>
const clientId = '<google client ID>'
const backendUrl = '<oauth backend URL>'
var script = document.createElement('script')
script.defer = true
script.async = true
script.src = 'https://accounts.google.com/gsi/client'
script.addEventListener("load", function() {
google.accounts.id.initialize({
client_id: clientId,
callback: (response) => {
if (response?.credential) {
fetch(backendUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: response.credential }),
})
.then(res => res.json())
.then(data => {
// Process user authentication
localStorage.setItem('auth_token', data.token);
window.location.href = '/dashboard';
})
.catch(err => console.error('Auth failed:', err));
}
}
});
google.accounts.id.renderButton(
document.getElementById('g_id_signin'),
{ theme: 'outline', size: 'large', logo_alignment: 'center' }
);
google.accounts.id.prompt(); // Enable One Tap
});
document.head.appendChild(script);
</script>

Валидация Google Token (PHP Backend)

use Illuminate\Support\Facades\Http;
$token = $request->input('token');
// Проверьте token через Google
$response = Http::post("https://oauth2.googleapis.com/tokeninfo", [
'id_token' => $token,
]);
if ($response->status() !== 200) {
return response()->json(['error' => 'Invalid token'], 401);
}
$userInfo = $response->json();
// Проверьте обязательные поля
if (empty($userInfo['sub']) || empty($userInfo['email'])) {
return response()->json(['error' => 'Missing user info'], 400);
}
$user = User::firstOrCreate(
['email' => $userInfo['email']],
[
'name' => $userInfo['name'],
'email_verified_at' => now(),
'password' => bcrypt(Str::random(24)),
]
);
$token = $user->createToken('auth-token')->plainTextToken;
return response()->json(['token' => $token, 'user' => $user]);

Facebook Login с JavaScript SDK

<div class="fb-login-button"
data-size="large"
data-button-type="login_with"
data-scope="email"
></div>
<div id="fb-root"></div>
<script>
const appId = '<facebook app ID>'
const backendUrl = '<oauth backend URL>'
window.fbAsyncInit = function() {
FB.init({
appId: appId,
cookie: false,
xfbml: true,
version: 'v18.0',
});
FB.Event.subscribe('auth.authResponseChange', function(response) {
if (response?.status === 'connected') {
fetch(backendUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: response.authResponse.accessToken }),
})
.then(res => res.json())
.then(data => {
localStorage.setItem('auth_token', data.token);
window.location.href = '/dashboard';
});
}
});
FB.AppEvents.logPageView();
};
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = 'https://connect.facebook.net/en_US/sdk.js';
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>

Валидация Facebook Token (PHP Backend)

$token = $request->input('token');
$response = Http::get("https://graph.facebook.com/me", [
'access_token' => $token,
'fields' => 'id,name,email',
]);
if ($response->status() !== 200) {
return response()->json(['error' => 'Invalid token'], 401);
}
$userInfo = $response->json();
$user = User::firstOrCreate(
['email' => $userInfo['email']],
[
'name' => $userInfo['name'],
'email_verified_at' => now(),
'password' => bcrypt(Str::random(24)),
]
);
// Сохраните Facebook ID для будущих входов
$user->socialAccounts()->updateOrCreate(
['provider' => 'facebook'],
['provider_user_id' => $userInfo['id'], 'token' => $token]
);
return response()->json([
'token' => $user->createToken('auth-token')->plainTextToken,
'user' => $user
]);

Apple Sign-In с JavaScript

Apple Sign-In требует особой обработки, так как информация о пользователе предоставляется только при первой аутентификации:

<div id="appleid-signin" class="signin-button"
data-color="black" data-border="true" data-type="sign-in"></div>
<script>
const clientId = '<apple service ID identifier>'
const backendUrl = '<oauth backend URL>'
var script = document.createElement('script')
script.src = 'https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js'
script.addEventListener("load", function() {
window.AppleID.auth.init({
clientId: clientId,
scope: 'name email',
redirectURI: window.location.href,
state: 'csrf' + Math.random(),
usePopup: true,
});
document.addEventListener('AppleIDSignInOnSuccess', (event) => {
fetch(backendUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token: event.detail?.authorization?.id_token,
// Apple only provides name on first auth
name: event.detail?.user?.name?.firstName + ' ' +
event.detail?.user?.name?.lastName,
}),
})
.then(res => res.json())
.then(data => {
localStorage.setItem('auth_token', data.token);
window.location.href = '/dashboard';
});
});
});
document.head.appendChild(script);
</script>

Валидация Apple Token (PHP Backend)

use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
$token = $request->input('token');
$fullName = $request->input('name');
$clientId = config('services.apple.client_id');
// Получите публичные ключи Apple
$keysResponse = Http::get("https://appleid.apple.com/auth/keys");
$publicKeys = $keysResponse->json();
try {
$userInfo = (array) JWT::decode($token, JWK::parseKeySet($publicKeys));
} catch (\Exception $e) {
return response()->json(['error' => 'Invalid token'], 401);
}
// Проверьте claims token
if ($userInfo['exp'] < time() ||
$userInfo['aud'] !== $clientId ||
$userInfo['iss'] !== 'https://appleid.apple.com') {
return response()->json(['error' => 'Token validation failed'], 401);
}
$user = User::firstOrCreate(
['email' => $userInfo['email']],
[
'name' => $fullName ?: 'Apple User',
'email_verified_at' => now(),
'password' => bcrypt(Str::random(24)),
]
);
return response()->json([
'token' => $user->createToken('auth-token')->plainTextToken,
'user' => $user
]);

Заключение

Социальная аутентификация улучшает пользовательский опыт и может повысить конверсию. Реализуйте несколько провайдеров, чтобы дать пользователям выбор, корректно обрабатывайте пограничные случаи вроде конфликтов email и всегда учитывайте последствия для безопасности. Независимо от того, используете ли вы Laravel Socialite или прямую интеграцию через SDK, паттерны из этого руководства дают основу для надёжной социальной аутентификации у основных провайдеров.

 
 
 
Языки
Темы
Copyright © 1999 — 2026
Зетка Интерактив