본문 바로가기
iaa.dev/Laravel8

Laravel 8 - 소셜로그인(Laravel Socialite) - 1

by chopper.kid 2022. 1. 14.

이전글들에서도 언급했습니다만 내장되어 있는 패키지로 구현이 가능하면 최대한 그 기능들을 이용하는 방향으로 설명하겠습니다.

하다보니까 , 느끼는게 기능 구현보다 각 소셜 사이트가서 앱 만들고 설정하고 하는게 더 시간걸리고 어렵네요. -,.-

되도록 그쪽 방향으로 설명하도록 하겠습니다.

 

설치 패키지

 

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 인증 소셜로그인은 이정도가 거의 전부입니다. 할거 별로 없죠?

오히려 작업하면서 각 플랫폼의 애플리케이션 만드는 부분에서 헤매는게 더 많은 느낌이었습니다.

 

각 플랫폼의 애플리케이션 생성부분은 따로 시간내서 만들어 보겠습니다.

 

반응형
SMALL

댓글