Facebook OAuth Authentication Implementation for Websites
Facebook OAuth is used via Facebook Login SDK or standard OAuth2 flow. Facebook audience in Russia has shrunk but remains significant for international products and platforms with broad age demographics.
Creating App in Meta Developer Console
- developers.facebook.com → My Apps → Create App
- App type: Consumer (for public login)
- Add product Facebook Login → Web
- In Facebook Login settings, specify Valid OAuth Redirect URIs
- Save App ID and App Secret
In development mode, app is available only to added test users. For public access, pass App Review.
Laravel Socialite
// config/services.php
'facebook' => [
'client_id' => env('FACEBOOK_APP_ID'),
'client_secret' => env('FACEBOOK_APP_SECRET'),
'redirect' => env('FACEBOOK_REDIRECT_URI'),
],
class FacebookAuthController extends Controller
{
public function redirect(): RedirectResponse
{
return Socialite::driver('facebook')
->scopes(['email', 'public_profile'])
->redirect();
}
public function callback(): RedirectResponse
{
try {
$fbUser = Socialite::driver('facebook')->user();
} catch (\Exception $e) {
return redirect('/login')->withErrors(['facebook' => 'Facebook authorization error']);
}
$user = User::updateOrCreate(
['facebook_id' => $fbUser->getId()],
[
'name' => $fbUser->getName(),
'email' => $fbUser->getEmail(),
'email_verified_at' => $fbUser->getEmail() ? now() : null,
'avatar' => $fbUser->getAvatar(),
]
);
Auth::login($user, remember: true);
return redirect()->intended('/dashboard');
}
}
Facebook Features
Email may be absent. If user registered via phone or hid email from apps—getEmail() returns null. Handle explicitly:
if (!$fbUser->getEmail()) {
session(['pending_facebook_id' => $fbUser->getId()]);
return redirect('/auth/complete-profile');
}
Avatar: Facebook returns avatar link but it's temporary. Better to download and save locally on first login.
App Review: email scope is sufficient. Additional data (friends, posts) requires App Review with Meta.
Facebook JavaScript SDK
Alternative to redirect flow—button via JS SDK:
<script>
window.fbAsyncInit = function() {
FB.init({ appId: '{{ config("services.facebook.client_id") }}', version: 'v19.0' });
};
</script>
<button onclick="fbLogin()">Login with Facebook</button>
<script>
function fbLogin() {
FB.login(function(response) {
if (response.authResponse) {
fetch('/auth/facebook/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken },
body: JSON.stringify({ access_token: response.authResponse.accessToken }),
}).then(r => r.json()).then(data => {
window.location.href = data.redirect;
});
}
}, { scope: 'email,public_profile' });
}
</script>
// Verify access_token via Graph API
public function handleToken(Request $request): JsonResponse
{
$response = Http::get('https://graph.facebook.com/me', [
'access_token' => $request->access_token,
'fields' => 'id,name,email,picture',
]);
if ($response->failed()) {
return response()->json(['error' => 'Invalid token'], 401);
}
$fbData = $response->json();
$user = User::updateOrCreate(
['facebook_id' => $fbData['id']],
['name' => $fbData['name'], 'email' => $fbData['email'] ?? null]
);
Auth::login($user);
return response()->json(['redirect' => '/dashboard']);
}
Timeline
Standard OAuth2 flow—1–2 days. With JS SDK, missing email handling, and Data Deletion Callback—3 days.







