W dzisiejszych czasach wiele aplikacji oferuje użytkownikom możliwość zalogowania się za pomocą ich kont w popularnych serwisach społecznościowych takich jak Facebook. Dzięki oficjalnej paczce Laravel Socialite dodanie takiej możliwości do aplikacji napisanej w Laravelu jest bardzo proste.
W tym wpisie postawimy od zera aplikację Laravela, w której oprócz rejestracji i logowania za pomocą adresu e-mail i hasła pozwolimy użytkownikom rejestrować i logować się za pomocą ich konta Facebook i/lub Google.
Laravel new
Zacznijmy od utworzenia nowej aplikacji Laravela za pomocą instalatora:
laravel new laravel-socialite-example
W celu rejestrowania użytkowników, będziemy potrzebowali bazy danych. Utwórzmy bazę danych laravel-socialite-example
i ustawmy parametry dostępu do niej w pliku .env
. Ja używam Laravel Valet, w związku z czym bazę tworzę bezpośrednio na swoim komputerze. Moje parametry wyglądają tak:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel-socialite-example
DB_USERNAME=root
DB_PASSWORD=
Zmigrujmy naszą bazę danych:
php artisan migrate
Następnie użyjmy komendy php artisan make:auth
w celu wygenerowanie ścieżek, kontrolerów i widoków potrzebnych do obsługi standardowej rejestracji i logowania. Użyjmy komendy php artisan serve
, żeby odpalić lokalny serwer PHP i odwiedźmy naszą stronę w przeglądarce pod adresem http://localhost:8000
. Spróbujmy się zarejestrować:
Jeśli wszystko działa, przejdźmy do zainstalowania i konfiguracji Socialite.
Laravel Socialite
Dodajmy Socialite do naszego projektu za pomocą komendy:
composer require laravel/socialite
Klucze konfiguracyjne od Facebooka i Google
Zanim użyjemy w aplikacji Socialite, musimy uzyskać i dodać klucze konfiguracyjne od dostawców (Facebook i Google). Zacznijmy od dodania do tablicy w pliku config/services.php
dwóch wpisów:
'facebook' => [
'client_id' => env('FACEBOOK_CLIENT_ID'),
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
'redirect' => '/login/facebook/callback',
],
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => '/login/google/callback',
],
Używamy tutaj funkcji globalnej env()
, która pobiera klucze z pliku .env
znajdującego się w głównym katalogu naszej aplikacji. Dodajmy w nim potrzebne klucze:
FACEBOOK_CLIENT_ID=
FACEBOOK_CLIENT_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
Warto też dodać odpowiadające klucze w pliku .env.example
. Ten plik, w przeciwieństwie do pliku .env
, powinien być zapisywany do repozytorium. Jest on swego rodzaju instrukcją, informującą jakie klucze powinny się znaleźć w pliku .env
.
W polu redirect
ustawiamy adres, pod który Facebook i Google mają wrócić, gdy użytkownik się już zaloguje. Możemy tutaj użyć ścieżki względnej, Socialite sam utworzy z tego pełny adres URL.
Następnym krokiem jest zdobycie kluczy client_id
i client_secret
od Facebooka i Google.
Zacznijmy od Facebooka. Przejdźmy do tego linku: https://developers.facebook.com/ i wykonajmy po kolei kroki grafiki znajdującej się pod tym linkiem: instrukcja dla Facebooka.
W pliku .env
przypiszmy wartość z pola App ID
do klucza FACEBOOK_CLIENT_ID
, a wartość z pola App Secret
do klucza FACEBOOK_CLIENT_SECRET
.
Teraz przejdźmy do procesu uzyskania kluczy od Google. Przejdźmy do tego linku: https://console.developers.google.com/projectcreate. Uzyskanie kluczy wymaga stworzenia nowego projektu, włączenia interfejsu Google+ API i utworzenie danych logowania. Jest z tym trochę roboty, a interfejs Google Console nie jest zbyt przyjazny, dlatego podobnie jak w przypadku Facebooka, stworzyłem grafikę, która przedstawia w kolejnych screenshotach jak uzyskać te klucze: instrukcja dla Google.
W pliku .env
przypiszmy wartość z pola Identyfikator klienta
do klucza GOOGLE_CLIENT_ID
a wartość z pola Tajny klucz klienta
do klucza GOOGLE_CLIENT_SECRET
.
Ważna uwaga: przy ustawianiu konfiguracji Facebooka pomineliśmy ustawienie URI przekierowania, które ustawiliśmy przy okazji konfiguracji Google. Wygląda na to, że gdy przekierowujemy do Facebooka z adresu http://localhost:8000, nie ma to znaczenia. Przed opublikowaniu aplikacji, będziemy jednak musieli ustawić poprawny adres w polu Valid OAuth Redirect URIs
w panelu ustawień Facebook Login
.
Ok, klucze mamy ustawione, jesteśmy gotowi do logowania użytkowników.
Ścieżki Socialite
Żeby obsłużyć proces logowania za pomocą Socialite, potrzebujemy dwóch ścieżek. Jednej, która przekieruje użytkownika do dostawcy i drugiej, do której użytkownik wróci po zalogowaniu się za pomocą swojego konta u dostawcy. Dodajmy w plik routes/web.php
następujące ścieżki:
Route::get('login/{provider}', 'Auth\LoginController@redirectToProvider');
Route::get('login/{provider}/callback', 'Auth\LoginController@handleProviderCallback');
Następnie dodajmy odpowiadające im metody w pliku app/Http/Controllers/Auth/LoginController
:
/**
* @param $provider
* @return \Illuminate\Http\Response
*/
public function redirectToProvider($provider)
{
return Socialite::driver($provider)->redirect();
}
/**
* @param $provider
* @return \Illuminate\Http\Response
*/
public function handleProviderCallback($provider)
{
$user = Socialite::driver($provider)->user();
dd($user);
}
Pamiętajmy by na górze pliku zaimportować Socialite: use Laravel\Socialite\Facades\Socialite;
.
Używamy tutaj dwóch metod fasady Socialite. Metoda redirect()
Socialite wysyła użytkownika do dostawcy, metoda user()
zwraca dane użytkownika po powrocie od dostawcy.
To tyle jeśli chodzi o konfiguracje Socialite. Nasza aplikacja przekierowuje poprawnie użytkownika do Facebooka lub Google i potrafi odebrać dane o użytkowniku z powrotem. Następnym krokiem jest zarejestrowanie lub zalogowanie użytkownika, na podstawie tych danych.
Modyfikacja migracji tabeli users
Zacznijmy od modyfikacji naszej tabeli users. Dodajmy pole facebook_id
oraz google_id
, w których będziemy zapisywać id użytkownika w systemie dostawcy. Jako, że użytkownik będzie się rejestrować za pomocą tylko jednego z dwóch dostawców, oba te pola muszą być nullable
. Oba powinny też być unikalne. Powinniśmy też zmienić definicję pól email
oraz password
. Oba te pola będą musiałby być nullable
. Zmodyfikujmy treść metody up()
w pliku database/migrations/2014_10_12_000000_create_users_table.php
w następujący sposób:
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('facebook_id')->nullable()->unique();
$table->string('google_id')->nullable()->unique();
$table->string('name');
$table->string('email')->nullable()->unique();
$table->string('password')->nullable();
$table->rememberToken();
$table->timestamps();
});
}
Ważna uwaga: Facebook pozwala rejestrować się bez użycia adresu email. Dlatego niektórzy użytkownicy, po zalogowaniu się na Facebooku, wrócą do naszej aplikacji bez pola email. Bierzemy to pod uwagę i dlatego ustawiamy pole email
jako nullable
.
Nie zapomnijmy dodać pól facebook_id
i google_id
do tablicy przypisanej do pola $fillable
w naszym modelu User
:
// app/User.php
protected $fillable = [
'name', 'email', 'password', 'facebook_id', 'google_id',
];
Zalogowanie lub zarejestrowanie użytkownika
Ostatnim etapem jest zarejestrowanie lub zalogowanie użytkownika na podstawie danych od dostawcy. Ten proces najłatwiej będzie wytłumaczyć patrząc na kod:
/**
* @param $provider
* @return \Illuminate\Http\Response
*/
public function handleProviderCallback($provider)
{
$socialiteUser = Socialite::driver($provider)->user();
// Znajdź lub stwórz użytkownika na podstawie danych do dostawcy
$user = $this->firstOrCreateUser($socialiteUser, $provider);
// Zaloguj uzyskanego użytkownika
Auth::login($user);
// Przekieruj użytkownika tam gdzie chciał się dostać lub do domyślnej ścieżki
return redirect()->intended($this->redirectPath());
}
/**
* @param $socialiteUser
* @param $provider
* @return \App\User
*/
protected function firstOrCreateUser($socialiteUser, $provider)
{
// Jeśli istnieje użytkownik o podanym facebook_id
// lub google_id, zwróć tego użytkownika
if ($user = User::where("{$provider}_id", $socialiteUser->getId())->first()) {
return $user;
}
// Jeśli istnieje użytkownik o podanym adresie e-mail przypisz facebook_id
// lub google_id do jego konta, a następnie zwróc tego użytkownika
if (!is_null($socialiteUser->getEmail()) && $user = User::where('email', $socialiteUser->getEmail())->first()) {
$user->update(["{$provider}_id" => $socialiteUser->getId()]);
return $user;
}
// Jeśli powyższe warunki nie zostały spełnione, to mamy do czynienia
// z rejestracją, więc utwórz nowego użytkownika na podstawie
// danych od dostawcy i zwróc tego użytkownika
return User::create([
"{$provider}_id" => $socialiteUser->getId(),
'email' => $socialiteUser->getEmail(),
'name' => $socialiteUser->getName(),
]);
}
Na górze pliku zaimportujmy model User
i fasadę Auth
:
use App\User;
use Illuminate\Support\Facades\Auth;
W metodzie firstOrCreateUser()
w pierwszej kolejności szukamy w naszej bazie użytkownika po polu facebook_id
lub google_id
. Jeśli go znajdziemy, to znaczy że użytkownik już się wcześniej zarejestrował za pomocą danego dostawcy. Zwracamy znalezionego użytkownika.
Jeśli ten warunek nie został spełniony to przechodzimy do następnego warunku. Sprawdzamy czy dostawca zwrócił adres e-mail. Jeśli tak, to szukamy w naszej bazie użytkownika po polu email
. Jeśli go znajdziemy, to znaczy że użytkownik już się wcześniej zarejestrował za pomocą innego dostawcy lub "ręcznie" za pomocą loginu i hasła. W takim przypadku przypisujemy facebook_id
lub google_id
do jego konta, by mógł w przyszłości używać danego dostawcy i zwracamy znalezionego użytkownika.
Jeśli żaden z powyższych warunków też nie został spełniony, to znaczy nie znaleźliśmy użytkownika po żadnym z pól facebook_id
, google_id
ani email
to znaczy, że mamy do czynienia z nowym użytkownikiem. Wykorzystujemy dane od dostawcy by stworzyć użytkownika i zwracamy go.
W metodzie handleProviderCallback()
dostajemy użytkownika z metody firstOrCreateUser()
i logujemy go za pomocą metody login()
fasady Auth
. Następnie używamy kodu return redirect()->intended($this->redirectPath());
, żeby przekierować użytkownika tam gdzie chciał się dostać zanim przeszedł do ekranu logowania za pomocą metody intended()
. Jako jej parametr podajemy domyślną ścieżkę, która zostanie używa, jeśli użytkownik po prostu przeszedł do ekranu logowania.
Dodajmy jeszcze przyciski w widoku logowania:
// resources/views/auth/login.blade.php
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<a href="/login/google" class="btn btn-outline-primary">
Zaloguj się przez Google
</a>
<a href="/login/facebook" class="btn btn-outline-primary">
Zaloguj się przez Facebook
</a>
</div>
</div>
Oraz w widoku rejestracji:
// resources/views/auth/register.blade.php
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<a href="/login/google" class="btn btn-outline-primary">
Zarejestruj się przez Google
</a>
<a href="/login/facebook" class="btn btn-outline-primary">
Zarejestruj się przez Facebook
</a>
</div>
</div>
To wszystko, od terazu użytkownicy mogą się w naszej aplikacji rejestrować i logować za pomocą swoich kont na Facebooku i Google.
Warto jeszcze dodać, że Socialite oficjalnie obsługuje logowanie za pomocą następujących serwisów: Facebook, Google, Twitter, Linkedin, Github i Bitbucket. Nieoficjalna lista innych wspieranych serwisów znajduje się tutaj: https://github.com/SocialiteProviders/Providers.
Jeśli macie jakieś wątpliwości, pytania lub problemy - piszcie w komentarzach!