System

Social Login with Laravel Socialite

မင်္ဂလာပါ။
ကျွန်တော်ကတော့ Spiceworks Myanmar မှာ Backend Developer အနေနဲ့ တာဝန်ယူလုပ်ကိုင်နေတဲ့ သုတယာမိုး ဖြစ်ပါတယ်။ ဒီတစ်ခေါက်မှာတော့ Laravel Socialite ကိုအသုံးပြုပြီး social login ထည့်သွင်းပုံကို ဝေမျှပေးချင်ပါတယ်။ Social Login ဆိုတာကတော့ Login ဝင်တဲ့အခါမှာ email, password တို့ကို ရိုက်ထည့်နေစရာမလိုဘဲ မိမိရဲ့ google account, facebook account တို့ကို အသုံးပြုကာ login ဝင်သည့်လမ်းစဥ်ကို ဆိုလိုတာဖြစ်ပါတယ်။

ပထမဆုံးအနေနဲ့ laravel project တစ်ခုကို create လုပ်ရမှာဖြစ်ပါတယ်။

composer create-project laravel/laravel social_login

Project တစ်ခုပြုလုပ်ပြီးပါက login, register နှင့် dashboard အစရှိတဲ့ pages တွေအတွက် လိုအပ်တဲ့ UI နဲ့ Authentication System တစ်ခုမှာပါဝင်တဲ့ routes, controllers, views အစရှိတာတွေကို ရေးထားပေးနှင့်ပြီးသားဖြစ်တဲ့ Laravel Breeze ကို install လုပ်ဖို့အတွက် အောက်ပါ command များကိုအသုံးပြုပေးပါ။

php artisan breeze:install
npm install
npm run dev

Fig(1) ကတော့ Laravel Breeze ကို install ပြုလုပ်ပြီး login route ကို သွားတဲ့အခါမှာ မြင်ရမယ့် tailwind css နဲ့ရေးထားတဲ့ login page ဖြစ်ပါတယ်။

Fig(1) Login Page

ထို့နောက် database အပိုင်းကို ဆက်သွားကြရအောင်။
Social Login ပြုလုပ်ဖို့အတွက် users table နှင့် providers table တို့ကို လိုအပ်မှာဖြစ်ပါတယ်။ users table မှာလိုအပ်တဲ့ column တွေကတော့ အောက်ပါအတိုင်းဖြစ်ပါတယ်။ Social Login မှာ password မလိုတဲ့အတွက် “password” column ကို nullable လုပ်ထားပေးဖို့ လိုအပ်ပါတယ်။ ကျန်ရှိတဲ့ column တွေကတော့ ပုံမှန် users table မှာရှိနေကြ column တွေပဲဖြစ်ပါတယ်။

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password')->nullable();
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
};

providers table မှာကတော့ provider_name, provider_id, user_id အစရှိတဲ့ column တွေကို လိုအပ်မှာဖြစ်ပါတယ်။ “provider_name” column ကတော့ user အသုံးပြုလိုက်တဲ့ provider ကိုထည့်ရမှာ ဖြစ်ပါတယ်။ ဥပမာ user က google ကိုအသုံးပြုပြီး sign up လုပ်လိုက်တယ်ဆိုရင် ထို column မှာ google ဆိုပြီး ထည့်သွင်းပေးရမှာဖြစ်ပါတယ်။ “provider_id” column ကတော့ user အသုံးပြုလိုက်တဲ့ provider ကနေ ပြန်လည်ပေးပို့လာတဲ့ ထို user ရဲ့ အချက်အလက်တွေထဲကမှ “id” ကိုထည့်သွင်းပေးရမှာ ဖြစ်ပါတယ်။ “user_id” column ထည့်သွင်းထားရတဲ့ ရည်ရွယ်ချက်ကတော့ user တစ်ယောက်က google provider ကိုအသုံးပြုပြီး sign up ပြုလုပ်နိုင်သလို အခြား providers တွေကိုလည်း အသုံးပြုနိုင်တာဖြစ်တဲ့အတွက် users နှင့် providers table ကို one to many relationship ချိတ်ပေးဖို့အတွက် ထည့်သွင်းထားရခြင်း ဖြစ်ပါတယ်။ “providers” table ကတော့ အောက်ပါအတိုင်း ဖြစ်ပါတယ်။

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('providers', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained('users')->onDelete('cascade')->onUpdate('cascade');
            $table->string('provider_name');
            $table->string('provider_id');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('providers');
    }
};

app/Models/User.php မှာပါဝင်တာတွေကတော့ အောက်ပါအတိုင်းဖြစ်ပါတယ်။ providers function ကတော့ providers table နှင့် ချိတ်ဆက်ထားတဲ့ has many relationship ဖြစ်ပါတယ်။

<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'email_verified_at'
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function providers(): HasMany {
        return $this->hasMany(Provider::class,'user_id','id');
    }
}

app/Models/Provider.php မှာပါဝင်တာတွေကတော့ အောက်ပါအတိုင်းဖြစ်ပါတယ်။ user function ကတော့ users table နှင့် ချိတ်ဆက်ထားတဲ့ belongs to relationship ဖြစ်ပါတယ်။

<php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Provider extends Model
{
    use HasFactory;

    protected $fillable = ['user_id', 'provider_id', 'provider_name'];

    public function user(): BelongsTo {
        return $this->belongsTo(User::class);
    }
}

Database မှာ လိုအပ်တဲ့ tables နှင့် columns တွေကို ထည့်သွင်းပြီးပါက အောက်ပါ command ကို အသုံးပြုပြီး migrate ပြုလုပ်ပါ။

php artisan migrate

Database နှင့်ပတ်သက်တာတွေ ပြီးသွားပြီဖြစ်တဲ့အတွက် routing အပိုင်းကို ဆက်သွားပါမယ်။ OAuth providers တွေကို အသုံးပြုဖို့အတွက် အဓိကအားဖြင့် route နှစ်ခု လိုအပ်ပါတယ်။ ပထမတစ်ခုကတော့ မိမိ website ကနေ user အသုံးပြုမယ့် provider တွေဆီကို ညွှန်ပြပေးရမယ့် redirect route ဖြစ်ပါတယ်။ ဒုတိယ တစ်ခုကတော့ ထို user အသုံးပြုလိုက်တဲ့ provider ကနေ ပြန်လည်ပေးပို့လာတဲ့ အချက်အလက်တွေကို လက်ခံမယ့် callback route ဖြစ်ပါတယ်။ လွယ်လွယ်ပြောရရင်တော့ provider ဆီကို သွားမယ့် route နှင့် provider ကနေပြန်လာမယ့် route တို့ပဲ ဖြစ်ပါတယ်။ ထို route တွေကိုတော့ routes/web.php မှာအောက်ပါအတိုင်း ရေးပေးရမှာဖြစ်ပါတယ်။

<php

use App\Http\Controllers\SocialLoginController;
use Illuminate\Support\Facades\Route;

Route::get('auth/{provider}/redirect', [SocialLoginController::class, 'redirect']);
Route::get('auth/{provider}/callback', [SocialLoginController::class, 'callback']);

လိုအပ်တဲ့ route တွေ ထည့်သွင်းပြီးရင်တော့ ထို route တွေကို handle လုပ်ဖို့အတွက် controller တစ်ခု create လုပ်ပေးပါ။ ကျွန်တော်ကတော့ SocialLoginController ဆိုပြီး controller တစ်ခု create လုပ်ထားပါတယ်။ app/Http/Controllers/SocialLoginController မှာပါဝင်တာတွေကတော့ အောက်ပါအတိုင်းဖြစ်ပါတယ်။

<php

namespace App\Http\Controllers;

use App\Models\Provider;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Laravel\Socialite\Facades\Socialite;

class SocialLoginController extends Controller
{
    public function redirect(string $provider) {
        return Socialite::driver($provider)->redirect();
    }

    public function callback(string $provider) {
        DB::beginTransaction();
        try {
            $socialUser = Socialite::driver($provider)->user();

            //check provider exists or not
            $providerModel = Provider::where('provider_id', $socialUser->id)->where('provider_name', $provider)->first();
            if ($providerModel) {
                //login
                Auth::login($providerModel->user);
                return redirect('/dashboard');
            }

            //check user exists or not
            $user = User::where('email', $socialUser->email)->first();
            //if user does not exist, create a user.
            if (empty($user)) {
                $user = User::create([
                    'name' => $socialUser->name ?? $socialUser->nickname,
                    'email' => $socialUser->email,
                    'email_verified_at' => Carbon::now()
                ]);
            }

            //create a provider
            Provider::create([
                'user_id' => $user->id,
                'provider_id' => $socialUser->id,
                'provider_name' => $provider
            ]);

            //login
            Auth::login($user);
            DB::commit();
            return redirect('/dashboard');
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }
}

redirect() function မှာကတော့ အထက်မှာပြောခဲ့တဲ့အတိုင်း user က အသုံးပြုလိုက်တဲ့ provider အပေါ်အခြေခံပြီး လိုအပ်တဲ့အချက်အလက်တွေဖြစ်တဲ့ client_id, client_secret တို့ကို မိမိ website ကနေ ထို provider ဆီသို့ ပို့ပေးတဲ့ function ဖြစ်ပါတယ်။
callback() function ကတော့ user က provider တစ်ခုခုကနေ approved ဖြစ်ပြီး ပြန်လည်ပေးပို့လာတဲ့ အချက်အလက်တွေကို handle လုပ်တဲ့ function ဖြစ်ပါတယ်။

OAuth Provider တွေရဲ့ client_id နှင့် client_secret တို့ကို ဘယ်လိုယူရမလဲ ဆိုတဲ့အပိုင်းကို ဆက်လက်ပြောပြသွားပါမယ်။ အခု ဘလော့ဂ်မှာတော့ Google နှင့် Github providers တွေဆီကနေပဲ ယူပြသွားပါမယ်။ Google ကနေဆိုရင်တော့ ပထမဆုံးအနေနဲ့ google console ကိုသွားပေးပါ။ ထို့နောက် project တစ်ခု create လုပ်ပါ။ create လုပ်ပြီးပါက Fig(2) မှာပြထားတဲ့အတိုင်း sidebar မှ “credentials” menu ကိုနှိပ်ပါ။ ထို့နောက် “create credentials” ဆိုတဲ့ button နှိပ်ပြီး ပေါ်လာသော dropdown မှ “OAuth Client ID” menu ကိုနှိပ်ပြီး OAuth app တစ်ခု create လုပ်ပါ။

Fig(2) OAuth client ID

ထို့နောက် Fig(3) မှာကဲ့သို့ oauth client create လုပ်တဲ့ page ကိုမြင်ရမှာဖြစ်ပါတယ်။ Authorized redirect URLS အပိုင်းမှာ provider မှတဆင့် ပြန်လည်ပေးပို့လာတဲ့ အချက်အလက်တွေကိုလက်ခံမယ့် web.php မှာ ရေးထားတဲ့ callback route ကိုညွှန်းပေးရမှာဖြစ်ပါတယ်။ ကျွန်တော်က callback url ကို web.php မှာ “‘/auth/{provider}/callback” အခုလိုရေးထားသည့်အတွက် “http://domain.com/auth/google/callback” လို့ထည့်သွင်းထားတာ ဖြစ်ပါတယ်။ ထို့နောက် save လုပ်လိုက်ပါက Fig(4) မှာပြထားသည့်အတိုင်း client_id နှင့် client_secret ကိုရရှိမှာဖြစ်ပါတယ်။

Fig(4) Client ID and Client secret from Google

Github ကနေ client_id နှင့် client_secret တို့ကိုရယူဖို့အတွက် GitHub developers link ကိုသွားပေးပါ။ ထို page မှ “New OAuth App” (သို့) “Register a new application” button ကိုနှိပ်ပြီး OAuth app တစ််ခု create လုပ်ပါ။ create လုပ်ပြီးပါက Fig(5) မှာကဲ့သို့ လိုအပ်တဲ့ အချက်အလက်များကို ဖြည့်ပါ။ Homepage URL နေရာမှာတော့ မိမိ website url ကိုထည့်သွင်းပေးပါ။ “Authorization callback URL” မှာတော့ web.php မှာ ရေးထားတဲ့ callback route ကိုညွှန်းပေးရမှာဖြစ်ပါတယ်။ ကျွန်တော်က callback url ကို web.php မှာ “‘/auth/{provider}/callback” လို့ရေးထားတာ ဖြစ်တဲ့အတွက် “http://domain.com/auth/github/callback” လို့ထည့်သွင်းထားတာဖြစ်ပါတယ်။ ထို့နောက် “register application” button ကိုနှိပ်ပါက client id ကို ရရှိမှာဖြစ်ပါတယ်။ client_secret ကိုတော့ မိမိ create လုပ်လိုက်တဲ့ OAuth app ထဲသို့သွား၍ Fig(6) မှာပြထားသည့်အတိုင်း “Generate a new client secret” button ကိုနှိပ်ပြီး ရယူနိုင်ပါတယ်။

Fig(5) Create OAuth App
Fig(6) Client ID and Client secret from GitHub

လိုအပ်တဲ့ credentials တွေကိုရယူပြီးပါက .env file မှာ အောက်ပါအတိုင်းထည့်သွင်းပေးပါ။

GOOGLE_CLIENT_ID="client_id from Google"
GOOGLE_CLIENT_SECRET="client_secret from Google"
GOOGLE_CALLBACK_URL="http://domain.com/auth/google/callback" (Google callback url)

GITHUB_CLIENT_ID="client_id from GitHub"
GITHUB_CLIENT_SECRET="client_secret from GitHub"
GITHUB_CALLBACK_URL="http://domain.com/auth/github/callback" (GitHub callback url)

ထို့နောက် config/services.php မှာ .env file မှာ credentials တွေကိုခေါ်သုံးနိုင်ဖို့အတွက် အောက်ပါအတိုင်းထည့်သွင်းပေးပါ။

'google' => [
        'client_id' => env('GOOGLE_CLIENT_ID'),
        'client_secret' => env('GOOGLE_CLIENT_SECRET'),
        'redirect' => env('GOOGLE_CALLBACK_URL'),
 ],

'github' => [
        'client_id' => env('GITHUB_CLIENT_ID'),
        'client_secret' => env('GITHUB_CLIENT_SECRET'),
        'redirect' => env('GITHUB_CALLBACK_URL'),
 ]

ထို့နောက် third-party providers တို့ကိုအသုံးပြုပြီး login ဝင်ရန်အတွက် “login.blade.php” မှာ ui ထည့်ပေးရမှာဖြစ်ပါတယ်။


အခုဘလော့ဂ်မှာတော့ Google နှင့် Github ကို အသုံးပြုမှာဖြစ်တဲ့အတွက် ထို providers နှစ်ခုအတွက် ui ကိုပဲ ထည့်သွင်းထားပါတယ်။ <a> tag ရဲ့ “href” attribute မှာတော့ web.php မှာ ရေးထားတဲ့ redirect route ကို ထည့်သွင်းပေးရမှာဖြစ်ပါတယ်။ ထို့နောက် Fig(7) ကဲ့သို့ login page ကိုတွေ့မြင်နိုင်မှာဖြစ်ပါတယ်။

Fig(7) Sign in with Google and Sign in with GitHub

အခုဆိုရင်တော့ Google account နှင့် GitHub account တို့ကိုအသုံးပြုကာ login ဝင်ရောက်နိုင်ပြီဖြစ်ပါတယ်။ Laravel Socialite အကြောင်းကို ပိုမိုလေ့လာချင်ပါက https://laravel.com/docs/10.x/socialite မှတဆင့် လေ့လာနိုင်ပါတယ်။ အဆုံးထိဖတ်ရှုပေးတဲ့အတွက် ကျေးဇူးတင်ပါတယ် ခင်ဗျ။

Hello

Leave a Reply

Your email address will not be published. Required fields are marked *