Sobre nós Guias Projetos Contactos
Админка
please wait

Introdução

A autenticação social simplifica o onboarding de utilizadores ao tirar partido de contas existentes de fornecedores como Google, Facebook, Apple e GitHub. Os utilizadores evitam criar mais uma palavra-passe e obtêm acesso a endereços de e-mail verificados. Este guia aborda a implementação de login social baseado em OAuth nos principais fornecedores, com exemplos práticos usando Laravel Socialite.

Visão Geral do Fluxo OAuth

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

Configuração do Laravel Socialite

Instalação

composer require laravel/socialite

Configuração

// 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 Sign-In

Configurar Google OAuth

  1. Aceda à Google Cloud Console
  2. Crie um projeto
  3. Ative a «Google+ API» ou a «Google Identity Platform»
  4. Vá a Credentials → Create OAuth Client ID
  5. Configure os authorized redirect URIs: https://yourapp.com/auth/google/callback

Implementação do 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)
{
// Verificar se o utilizador existe com este fornecedor
$socialAccount = \App\Models\SocialAccount::where('provider', $provider)
->where('provider_user_id', $socialUser->getId())
->first();
if ($socialAccount) {
return $socialAccount->user;
}
// Verificar se o utilizador existe com o mesmo e-mail
$user = User::where('email', $socialUser->getEmail())->first();
if (!$user) {
// Criar novo utilizador
$user = User::create([
'name' => $socialUser->getName(),
'email' => $socialUser->getEmail(),
'email_verified_at' => now(),
'password' => bcrypt(Str::random(24)),
'avatar' => $socialUser->getAvatar(),
]);
}
// Associar conta social
$user->socialAccounts()->create([
'provider' => $provider,
'provider_user_id' => $socialUser->getId(),
'token' => $socialUser->token,
'refresh_token' => $socialUser->refreshToken,
]);
return $user;
}
}

Configuração da Base de Dados

// Migração para a tabela 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 Login

Configurar Facebook OAuth

  1. Aceda a Facebook Developers
  2. Crie uma app
  3. Adicione o produto Facebook Login
  4. Configure os Valid OAuth Redirect URIs
  5. Obtenha o App ID e o App Secret em Settings

Scopes Adicionais

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

Apple Sign-In

Configurar Apple OAuth

  1. Aceda à Apple Developer Console
  2. Crie um App ID com a capacidade Sign in with Apple
  3. Crie um Services ID
  4. Configure os Return URLs
  5. Gere um client secret (requer chave privada)

Tratamento Específico da Apple

A Apple requer tratamento especial porque só devolve informação do utilizador na primeira autenticação:

public function callback($provider)
{
if ($provider === 'apple') {
return $this->handleAppleCallback();
}
// ... tratamento padrão
}
protected function handleAppleCallback()
{
$socialUser = Socialite::driver('apple')->user();
// A Apple só envia o nome na primeira autenticação
$name = $socialUser->getName();
if (!$name) {
// Tentar obter a partir de dados previamente guardados ou pedir
$name = request('user.name.firstName') . ' ' . request('user.name.lastName');
}
// O e-mail pode ser um private relay
$email = $socialUser->getEmail();
// ... criar/encontrar utilizador
}

GitHub Login

Configurar GitHub OAuth

  1. Aceda a GitHub Developer Settings
  2. Crie uma OAuth App
  3. Defina o Authorization callback URL
  4. Obtenha o Client ID e o Client Secret

Pedir Scopes Adicionais

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

Integração no Frontend

Botões de Login

<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>

Componente 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>

Autenticação SPA/API

Para single-page applications, use OAuth stateless:

// Devolver token em vez de sessão
public function callback($provider)
{
$socialUser = Socialite::driver($provider)->stateless()->user();
$user = $this->findOrCreateUser($socialUser, $provider);
// Criar token de API
$token = $user->createToken('social-auth')->plainTextToken;
// Redirecionar para o frontend com token
return redirect(config('app.frontend_url') . '/auth/callback?token=' . $token);
}

Considerações de Segurança

Parâmetro State

O Socialite trata automaticamente a proteção CSRF com o parâmetro state. Para pedidos stateless:

// Gerar e verificar state manualmente para SPAs
$state = Str::random(40);
session(['oauth_state' => $state]);
return Socialite::driver($provider)
->with(['state' => $state])
->redirect();

Verificação de E-mail

Os fornecedores sociais normalmente verificam e-mails, mas confirme as garantias do fornecedor:

$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']);
}

Associação de Contas

Permita que os utilizadores associem vários fornecedores:

public function link($provider)
{
// Tem de estar autenticado
$user = auth()->user();
$socialUser = Socialite::driver($provider)->user();
// Verificar se já está associado a outra conta
$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');
}

Tratamento de Erros

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.');
}
// ... continuar com a criação do utilizador
}

Integração Direta com SDK JavaScript

Nos casos em que precisa de mais controlo ou não está a usar Laravel Socialite, pode integrar diretamente com os SDKs do fornecedor.

Google Sign-In com 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>

Validação do Token Google (Backend PHP)

use Illuminate\Support\Facades\Http;
$token = $request->input('token');
// Validar token com o 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();
// Validar campos obrigatórios
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 com 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>

Validação do Token Facebook (Backend PHP)

$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)),
]
);
// Guardar o ID do Facebook para logins futuros
$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 com JavaScript

O Apple Sign-In requer tratamento especial, uma vez que a informação do utilizador só é fornecida na primeira autenticação:

<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>

Validação do Token Apple (Backend PHP)

use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
$token = $request->input('token');
$fullName = $request->input('name');
$clientId = config('services.apple.client_id');
// Obter as chaves públicas da 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);
}
// Validar claims do 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
]);

Conclusão

A autenticação social melhora a experiência do utilizador e pode aumentar as taxas de conversão. Implemente vários fornecedores para dar escolha aos utilizadores, trate casos limite como conflitos de e-mail de forma elegante e considere sempre as implicações de segurança. Quer utilize Laravel Socialite quer integração direta com SDK, os padrões neste guia fornecem uma base para uma autenticação social robusta nos principais fornecedores.

 
 
 
Языки
Темы
Copyright © 1999 — 2026
ZK Interactive