Laravel 8 - 소셜로그인(Laravel Socialite) - 1
이전글들에서도 언급했습니다만 내장되어 있는 패키지로 구현이 가능하면 최대한 그 기능들을 이용하는 방향으로 설명하겠습니다.
하다보니까 , 느끼는게 기능 구현보다 각 소셜 사이트가서 앱 만들고 설정하고 하는게 더 시간걸리고 어렵네요. -,.-
되도록 그쪽 방향으로 설명하도록 하겠습니다.
설치 패키지
Laravel 에서 오피셜로 제공하는 Laravel Socialite 와 기본제공 프로바이더들 이외에 한국에서 많이쓰는 kakao , naver 의 관련 패키지들을 이용합니다.
기본제공 프로바이더(Facebook, Twitter, LinkedIn, Google, GitHub, GitLab, Bitbucket)는 아래의 Laravel Socialite 외에 따로 설치할 필요는 없습니다.
Laravel Socialite - Laravel - The PHP Framework For Web Artisans
Laravel Socialite Introduction In addition to typical, form based authentication, Laravel also provides a simple, convenient way to authenticate with OAuth providers using Laravel Socialite. Socialite currently supports authentication with Facebook, Twitte
laravel.com
기본제공외의 다른 프로바이더들은 https://socialiteproviders.com/ 여기에 가서 내가 필요한 것들만 따로 인스톨하시면됩니다.
Socialite Providers
socialiteproviders.com
Route
로그인 페이지에서 해당 소셜 로그인 버튼을 클릭했을때의 리다이렉트와 갔다가 돌아오는 콜백 두개의 Route를 설정합니다. Laravel 공식 문서에서도 언급되어있습니다. https://laravel.com/docs/8.x/socialite#routing
Laravel Socialite - Laravel - The PHP Framework For Web Artisans
Laravel Socialite Introduction In addition to typical, form based authentication, Laravel also provides a simple, convenient way to authenticate with OAuth providers using Laravel Socialite. Socialite currently supports authentication with Facebook, Twitte
laravel.com
Route::get('/social/callback/{provider}', [
'as' => 'social.callback',
'uses' => 'App\Http\Controllers\Auth\SocialController@callback',
]);
Route::get('/social/{provider}', [
'as' => 'social.login',
'uses' => 'App\Http\Controllers\Auth\SocialController@login',
]);
테이블 생성
users 테이블에 붙여서 만들까 했는데 , 그냥 여러 플랫폼을 사용한다는 전제하에 테이블을 따로 뺐습니다.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateSocialAccountsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('social_accounts', function (Blueprint $table) {
$table->id();
$table->bigInteger('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->string('provider_name')->nullable();
$table->string('provider_id')->nullable();
$table->string('token')->nullable();
// OAuth 2.0 providers...
$table->string('refresh_token')->nullable();
$table->string('expires_in')->nullable();
// OAuth 1.0 providers...
$table->string('token_secret')->nullable();
$table->string('nickname')->nullable();
$table->string('name')->nullable();
$table->string('email')->nullable();
$table->string('avatar')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('social_accounts');
}
}
SocialAccount.php 모델 작성
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class SocialAccount extends Model
{
use HasFactory;
protected $fillable = [
'user_id', 'provider_name', 'provider_id',
'name','email','token', 'refresh_token'
];
public function user(){
return $this->belongsTo(User::class);
}
}
User.php 모델 수정
public function socialAccounts()
{
return $this->hasMany(SocialAccount::class);
}
위 메소드 추가
config/services.php 에 추가
'facebook' => [
'client_id' => env('FACEBOOK_CLIENT_ID'),
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
'redirect' => env('FACEBOOK_REDIRECT_URI'),
],
'twitter' => [
'client_id' => env('TWITTER_CLIENT_ID'),
'client_secret' => env('TWITTER_CLIENT_SECRET'),
'redirect' => env('TWITTER_REDIRECT_URI'),
],
'linkedin' => [
'client_id' => env('LINKEDIN_CLIENT_ID'),
'client_secret' => env('LINKEDIN_CLIENT_SECRET'),
'redirect' => env('LINKEDIN_REDIRECT_URI'),
],
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_REDIRECT_URI'),
],
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'redirect' => env('GITHUB_REDIRECT_URI'),
],
'gitlab' => [
'client_id' => env('GITLAB_CLIENT_ID'),
'client_secret' => env('GITLAB_CLIENT_SECRET'),
'redirect' => env('GITLAB_REDIRECT_URI'),
],
'naver' => [
'client_id' => env('NAVER_CLIENT_ID'),
'client_secret' => env('NAVER_CLIENT_SECRET'),
'redirect' => env('NAVER_REDIRECT_URI'),
],
'kakao' => [
'client_id' => env('KAKAO_CLIENT_ID'),
'client_secret' => env('KAKAO_CLIENT_SECRET'),
'redirect' => env('KAKAO_REDIRECT_URI')
],
config/app_frontend.php 작성
<?php
return [
'supported_social_login' => [
'facebook',
'twitter',
'github',
'gitlab',
'naver',
'kakao',
'google',
],
];
컨트롤러
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\SocialAccount;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
class SocialController extends Controller
{
public function login(string $provider)
{
if (!array_key_exists($provider, config('services'))) {
return redirect('login')->with('error', $provider . ' is not supported.');
}
return Socialite::driver($provider)
->redirect();
}
public function callback(String $provider)
{
try{
$socialUser = Socialite::driver($provider)->user();
$socialAccount = SocialAccount::where([
'provider_name' => $provider,
'provider_id' => $socialUser->id
])->first();
// If Social Account Exist then Find User and Login
if($socialAccount){
$socialAccount->update([
'token' => $socialUser->token,
]);
Auth::login($socialAccount->user);
return redirect()->route('dashboard');
}
// Find User
$user = User::where([
'email'=> $socialUser->getEmail()
])->first();
if(!$user){
if ($socialUser->getName()){
$name = $socialUser->getName();
} else {
$name = $socialUser->getNickName();
}
$user = User::create([
'email'=>$socialUser->getEmail(),
'name' =>$name,
'profile_photo_path' => $socialUser->getAvatar(),
'email_verified_at' => now(),
]);
}
// Create Social Accounts
$user->socialAccounts()->create([
'provider_name' => $provider,
'name' => $socialUser->name,
'email' => $socialUser->email,
'provider_id' => $socialUser->id,
'token' => $socialUser->token,
]);
Auth::login($user);
return redirect()->route('dashboard');
}catch(\Exception $e){
return redirect()->route('login');
}
}
}
resources/views/auth/login.blade.php 에 소셜 로그인 링크 추가
//form 태그 밑에 다음 추가
<hr class=" mt-4">
<div class="block mt-4">
<div class="col-12 text-center">
@foreach(config('app_frontend.supported_social_login') as $social)
<a href="{{ route('social.login',$social ) }}" class="btn btn-primary">Login with {{ $social }}</a>
@endforeach
</div>
</div>
.env 파일 수정
SOCIAL_CALLBACK="${APP_URL}/social/callback"
FACEBOOK_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxx
FACEBOOK_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxx
FACEBOOK_REDIRECT_URI="${SOCIAL_CALLBACK}/facebook"
각 로그인별로 위 세개를 추가해서 작성
Kakao 프로바이더 설치
composer require socialiteproviders/kakao
app/Providers/EventServiceProvider.php 에 추가
protected $listen = [
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
\SocialiteProviders\Kakao\KakaoExtendSocialite::class.'@handle',
],
];
Naver 프로바이더 설치
composer require socialiteproviders/naver
app/Providers/EventServiceProvider.php 에 추가
protected $listen = [
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
\SocialiteProviders\Naver\NaverExtendSocialite::class.'@handle',
],
];
OAuth 인증 소셜로그인은 이정도가 거의 전부입니다. 할거 별로 없죠?
오히려 작업하면서 각 플랫폼의 애플리케이션 만드는 부분에서 헤매는게 더 많은 느낌이었습니다.
각 플랫폼의 애플리케이션 생성부분은 따로 시간내서 만들어 보겠습니다.