تسجيل الدخول عبر الشبكات الاجتماعية في Laravel باستخدام Socialite


Muhammad Saied

في هذا الدرس سنتعرف على طريقة إضافة زر يسمح للمستخدم بالدخول إلى تطبيق الويب المكتوب بإطار العمل Laravel دون الحاجة للمرور بخطوة إنشاء حساب يدوياً.

social-login-laravel.png.b2b91d07dbfe69f

سنعتمد في شرحنا هذا على إطار العمل Laravel 5.2 وسوف نأخذ فيس بوك كمثال عن شبكة اجتماعية للسماح لمستخدمي تطبيقنا بالدخول عبرها. لكن يمكنك تطبيق الخطوات نفسها المشروحة في هذا الدرس لإنشاء زر دخول عبر تويتر أو GitHub أو Google أو Bitbucket أو LinkedIn.

تم اختبار خطوات هذا الدرس على Laravel 5.1 وأيضاً 5.2.

مبدأ الدخول عبر الشبكات الاجتماعية

الغالب أنك تعرف خطوات الدخول إلى المواقع باستخدام حسابك على فيس بوك أو تويتر، فالعديد من المواقع والتطبيقات توفر ميزة الدخول عبر الشبكات الاجتماعية لتسريع عملية الدخول إلى التطبيق بدلاً من الطلب من المستخدم إنشاء حساب جديد يدوياً. ومع ذلك دعنا نسرد هذه الخطوات سريعاً:

  • عندما تريد الدخول إلى موقع يحتاج لتسجيل الدخول يعرض لك استمارة الدخول؛
  • تجد ضمن الاستمارة السابقة زر "دخول عبر فيس بوك" (أو شبكة اجتماعية أخرى)؛
  • عند النقر على هذا الزر تنتقل إلى موقع فيس بوك (أو تفتح نافذة جديدة صغيرة للمتصفح ويفتح موقع فيس بوك داخلها)؛
  • يطلب منك فيس بوك منح صلاحيات مُحدّدة للتطبيق الخاص بالموقع (مثلاً السماح للتطبيق بالاطلاع على معلوماتك الشخصية العامة أو بريدك الإلكتروني) بعد الموافقة يعاد توجيهك إلى الموقع الأصلي وتجد نفسك قد دخلت إلى الموقع وكأنك تملك حساب.

الفكرة هي أن فيس بوك يمرر للموقع الآخر معلومات معينة عن حسابك بحيث يتمكن هذا الموقع من إنشاء حساب لك دون الحاجة لتعبئة أي معلومات يدوياً، كما ويمكن استخدام هذه المعلومات في عمليات الدخول اللاحقة للتعرف عليك والسماح لك بالوصول إلى حسابك نفسه (وليس إنشاء حساب جديد مرة أخرى!) دون أن تكتب كلمة سر للدخول.

تبادل المعلومات هذا بين المواقع له معايير. المعيار الأكثر شهرة والذي نعتمد عليه في هذا المقال هو معيار OAuth بنسختيه 1.0 و2.0.

بيئة العمل

سنفترض أن لديك تطبيق Laravel 5.1 سابق، وتريد إضافة ميزة الدخول عبر الشبكات الاجتماعية إليه.

إذا لم يكن إطار العمل Laravel مثبتاً لديك راجع درس تثبيت Laravel: تثبيت Laravel وإعداده على ويندوز وأوبنتو.

بالنسبة لي أستخدم نظام ويندوز مع XAMPP للعمل، لكن يمكنك تطبيق الخطوات على لينكس أو أي نظام تشغيل آخر بالطبع. 

سنحتاج أيضاً لتثبيت Socialite، وهي حزمة تسهل علينا عملية الاستيثاق عبر بروتوكول OAuth كثيراً. لتثبيت Socialite أضف السطر التالي إلى ملف composer.json في مشروع Laravel الخاص بك:

"require": {
    ...
    "laravel/socialite": "~2.0"
}

أو بدلاً من ذلك يمكنك استخدام الأمر:

composer require laravel/socialite

بعد ذلك نفذ الأمر التالي لتنزيل الملفات المطلوبة:

 composer update 

بعد انتهاء عملية التنزيل، عليك تعديل ملف الضبط config/app.php وإضافة السطر التالي إلى مصفوفة Service Providers (اسمها providers ضمن الملف):

Laravel\Socialite\SocialiteServiceProvider::class,

والسطر التالي إلى مصفوفة Aliases:

'Socialite' => Laravel\Socialite\Facades\Socialite::class,

الآن أصبح موقعك جاهزًا للتعامل مع الشبكات الاجتماعية. كما ذكرنا في بداية هذا الدرس هذه الطريقة تعمل مع العديد من الشبكات الاجتماعية التي توفر ميزة الاستيثاق عبر OAuth، إذ أن حزمة Socialite تدعم ست شبكات اجتماعية حالياً (فيس بوك وتويتر ولينكدإن و Google وGitHub وBitbucket).

الخطوة الأولى: إنشاء مسارات التوجيه

سنحتاج في عملية الدخول هذه إلى استدعائين:

  1. عندما يطلب المستخدم الدخول إلى الموقع، وهنا سوف نحوله إلى موقع فيس بوك للموافقة على منح الصلاحيات لتطبيقنا.
  2. بعد أن يوافق المستخدم على منح الصلاحيات للتطبيق سيقوم فيس بوك بإعادة توجيه المستخدم إلى موقعنا لإتمام عملية الدخول. 

بالنسبة لي فقد اخترت أن يكون هذان الاستدعائان على مسار واحد (ولكن يمكنك فصلهما إلى مسارين)، واخترت أن يكون المسار هو auth/facebook. لقد أضفت السطر التالي إلى ملف routes.php لدي:

Route::get('auth/facebook', 'SocialAuthController@facebookLogin');

كما تلاحظ فقد طلبت استخدام المتحكم SocialAuthController لمعالجة الطلبات الواردة إلى هذا المسار. 

هذا المتحكم عبارة عن مُتحكّم Controller عادي يرث من الصف App\Http\Controllers\Controller:

<?php

namespace App\Http\Controllers;
use App\Http\Controllers\Controller;

class SocialAuthController extends Controller
{}

الآن دعنا نعرف الدالة facebookLogin. ما الذي نريد أن يحدث عندما يصل المستخدم إلى المسار auth/facebook؟ في الواقع نحن نريد إضافة زر دخول عبر فيس بوك، وهذا الزر سيوجه المستخدم إلى هذا المسار وهنا نريد أن ينتقل المستخدم إلى موقع فيس بوك ليمنح تطبيقنا الصلاحيات اللازمة.

تعليمة توجيه المستخدم إلى فيس بوك هي التالية، فقط أضفها ضمن دالة facebookLogin:

return Socialite::with('facebook')->redirect();

طبعاً ستحتاج عدد من التعديلات قبل أن تعمل هذه التعليمة عندك:

  • عليك التأكد من تضمين Socialite في ترويسة الصف:

use Laravel\Socialite\Facades\Socialite;
  • أين هو تطبيق الفيس بوك الخاص بك؟ عليك إنشاؤه الآن أو التوجه إلى صفحة إعدادات التطبيق الخاص بك إذا كنت تملك تطبيقاً بالفعل. لإنشاء تطبيق جديد أو إدارة تطبيقاتك السابقة على فيس بوك توجه إلى المسار التالي: https://developers.facebook.com/apps على موقع فيس بوك عليك العثور على خيار Callback Url واستخدام نفس المسار المحلي الذي اخترته، انظر كيف كانت القيمة لدي: 

callback-url.thumb.png.8492bec6a67a11a24

  • من موقع فيس بوك عليك العثور على معلومات تطبيقك: نحتاج للحصول على معرف التطبيق (Client ID) وأيضاً الجملة السرية (Client Secret).
  • الآن اذهب إلى config/services.php وأضف ما يلي إلى الملف:
'facebook' => [
    'client_id'     => 'معرف التطبيق الذي حصلت عليه من فيس بوك',
    'client_secret' => 'الجملة السرية الخاصة بتطبيقك على فيس بوك',
    'redirect'      => 'رابط التحويل الذي سيتجه إليه المستخدم بعد منح الصلاحيات اللازمة على فيس بوك',
],

تحذير: لا تستخدم الدالة المساعدة url ضمن ملف services.php. هذا يؤدي لتوقف أداة artisan عن العمل.

يمكنك بدلاً من كتابة المتغيرات السابقة ضمن ملف services أن تستخدم السطور التالية وبعدها تعرف المتغيرات ضمن ملف env. لديك:

'facebook' => [
    'client_id'     => env('FACEBOOK_CLIENT_ID'),
    'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
    'redirect'      => env('FACEBOOK_REDIRECT_URL'),
],

الميزة هنا هي أن ملف services يضاف غالباً إلى مستودع التّطبيق، بينما ملف.env يبقى على الجهاز المحلي وهذا يفيد إذا لم تكن تريد السماح للمطورين الآخرين بالإطلاع على هذه المعلومات لسبب ما.

بالنسبة لي فقد اخترت أيضاً نقل قيمة redirect إلى ملف env. لأن هذا الرابط سيتغير حتماً ما بين بيئة التطوير وبيئة الإنتاج (Production)، وبالتالي من المفيد وضعه مع بقية الإعدادات الخاصة بكل بيئة.

الآن إذا حاولت الوصول إلى الرابط auth/facebook يجب أن تنتقل إلى موقع فيس بوك ويطلب منك الموافقة على منح الصلاحيات لتطبيقك هناك:

app-authorization.thumb.png.fa2cadbc87f6

بعد الموافقة، ستجد غالباً أن متصفح الوب لديك سيعطيك خطأ ويشتكي أن الصفحة فيها مشكلة في إعادة التوجيه. 

لا تقلق، أنت على الطريق الصحيح.

ما الذي حدث؟ عندما طلب المستخدم المسار auth/facebook تحول إلى موقع فيس بوك ووافق على منح الصلاحيات، بعدها أعاد فيس بوك توجيهه إلى موقعنا على نفس المسار ولكن هذه المرة هناك متغيرات مع الطلب.

المشكلة هنا هي أن موقعنا لا يعالج عملية استقبال المستخدم بعد عودته من فيس بوك وإنما يعيد توجيهه إلى فيس بوك مرة أخرى والذي يعيد توجيهه إلى موقعنا وهكذا في حلقة لا نهائية. علينا الآن معالجة الحالة التي يصل فيها المستخدم إلى المسار auth/facebook ومعه المتغيرات اللازمة للسماح له بالدخول.

الخطوة الثانية: التعرف على المستخدم

دعنا نعدل دالة facebookLogin لتصبح كالتالي:

public function facebookLogin(Request $request)
{
  // توجيه المستخدم إلى فيس بوك إذا لم يكن لديه كود الدخول
  if( ! $request->has('code') )
    return Socialite::with('facebook')->redirect();

  // وإلا علينا التعرف على المستخدم وتسجيل دخوله إلى الموقع
  $oauthUser = Socialite::with('facebook')->user();
  dd($oauthUser);
}

حالياً إذا نجحت في الخطوات السابقة يجب أن تطبع لديك كافة معلومات المستخدم الذي حصلنا عليها من فيس بوك. 

تهانينا! لم يبق إلا البحث في قاعدة بيانات موقعنا لنرى إذا كان المستخدم موجوداً من قبل أم لا؛ فإذا عثرنا عليه سمحنا له بالدخول، وإلا فسوف ننشئ حساباً جديداً له ونسمح له بالدخول على حسابه الجديد.

هناك حقول كثيرة يعطينا إياها فيس بوك، وهذه الحقول قد تختلف في الواقع بين شبكة اجتماعبة وأخرى، لكن هناك حقول ثابتة دوماً:

  • id: رقم تعريف المستخدم لدى فيس بوك. ثابت دوماً ولا يتغير ولذلك سنعتمد عليه في التعرف على المستخدم.
  • name: اسم المستخدم الحقيقي (وليس username). قد يتغير إذا غيره المستخدم على موقع فيس بوك.
  • nickname: لقب المستخدم. فيس بوك يعيد null هنا حسب ما لاحظت، ولكن تويتر يعطي قيمة username.
  • avatar و avatar_original: رابط صورة حساب المستخدم. عادة تكون صورة original أكبر حجماً.
  • email: البريد الإلكتروني للمستخدم. لاحظ أنه قد تحصل على قيمة البريد الإلكتروني للمستخدم وقد تحصل على قيمة null، ولذلك لا يمكنك الاعتماد على البريد الإلكتروني لمعرفة المستخدم إذا كان جديداً أو مستخدمًا سابقًا لموقعك. السبب هو أن بعض الشبكات الاجتماعية مثل فيس بوك تسمح للمستخدم بالتسجيل عبر رقم الهاتف وقد لا يملك فيس بوك عنوان البريد الإلكتروني للمستخدم، أو قد يختار المستخدم عدم الإفصاح عن عنوان بريده الإلكتروني لتطبيقك أثناء مرحلة اختيار الصلاحيات ومنحها للتطبيق.

عليك الآن التعديل على قاعدة البيانات الخاصة بالموقع وإضافة عمود اسمه facebook_id إلى جدول المستخدمين، سيكون الحقل unique ويسمح بقيم null (لأن هناك مستخدمين قد يستخدمون عملية التسجيل التقليدية للدخول وبالتالي لا يملك النظام معرف حسابهم على فيس بوك).

الآن نعود إلى دالة facebookLogin وبدلاً من تعليمة dd سنضع السطور التالية:

$user =  $this->findOrCreateUser($oauthUser);
Auth::login($user, true);
return redirect('home');

ما فعلناه هو استدعاء دالة ضمن الصف نفسه هي دالة findOrCreateUser تأخذ معلومات حساب المستخدم على فيس بوك وتعيد لنا كائنًا من نموذج User ‏(User Model)، الذي استدعينا عليه دالة Auth::login لتسجيل دخوله في الجلسة الحالية ومررنا قيمة true كمتغير ثان حتى نتذكر المستخدم (remember me).

أما عن دالة findOrCreateUser فهي كما يلي:

private function findOrCreateUser($oauthUser)
{
    if ($user = User::where('facebook_id', $oauthUser->id)->first())
        return $user;

    return User::create([
        'name' => $oauthUser->name,
        'email' => $oauthUser->email,
        'facebook_id' => $oauthUser->id
    ]);
}

دالة بسيطة جدًا، تبحث عن المستخدم ضمن جدول المستخدمين اعتمادًا على رقم تعريف حسابه لدى فيس بوك، وإذا لم تعثر عليه تنشئ مستخدمًا جديدًا بالاعتماد على المعلومات التي حصلنا عليها من فيس بوك.

لا تنس التعديل على User.php بما يناسب. إذا استخدمت دالة إنشاء المستخدم هذه ستحتاج إضافة الحقل facebook_id إلى مصفوفة fillable.

الآن، يمكنك اختبار العملية بشكل كامل: ضع رابطًا في مكان مناسب يشير إلىauth/facebook وانقر عليه وتابع خطوات عملية التسجيل، في نهاية العملية يجب أن يعاد توجيهك إلى صفحة homeبعد دخولك إلى الموقع.

ملاحظات

  • إذا أردت تطبيق هذه الخطوات على تويتر أو أي شبكة أخرى فالعملية نفسها تماماً، عدا أن تويتر يعتمد بروتوكول OAuth 1.0 وليس 2، لذلك عليك فحص وجود متغير اسمه oauth_token بدلاً من code؛ أما الخطوات الأخرى فلا يوجد أي اختلافات.

  • إذا أردت تذكّر الصفحة التي طلبها المستخدم قبل ظهور استمارة تسجيل الدخول وتوجيهه إليها بعد الدخول بدلاً من توجيهه إلى home في كل مرة، فقط استبدل السطر التالي:

return redirect()->intended('defaultpage');

بالسطر القديم:

return redirect('home');
  • لا تقيد نفسك بالطريقة المذكورة هنا، يمكنك إعادة تنظيم الشيفرات السابقة بعدة أساليب. مثلاً يمكنك وضع الدوال السابقة ضمن  متحكم Auth\AuthController بدلاً من إنشاء متحكم جديد، أو إنشاء خدمة (Service) ووضعها فيها، الخ.

أخطاء واجهتني

أثناء تطبيق هذه الخطوات واجهتني عدة أخطاء سأذكرها هنا مع حلولها.

المشكلة 0

FatalErrorException in AbstractProvider.php line 134:
Call to a member function set() on null

هذه المشكلة ظهرت عندما حاولت تطبيق هذه الخطوات لأول مرة على Laravel 5.2 ولم أنتبه إلى أنني وضعت تعريف المسار auth/facebook خارج middleware المسمى "web". هذا يعني أن Session غير متاحة في هذا النطاق، ولذلك واجهني هذا الخطأ.

الحل: بكل بساطة نقلت تعليمة Route إلى داخل مجموعة التوجيه الخاصة بـ web" middleware" كالتالي:

Route::group(['middleware' => 'web'], function () {
    ...

    Route::get('/auth/facebook', 'SocialAuthController@facebookLogin');
});

المشكلة 1

RequestException in CurlFactory.php line 187:
cURL error 60: SSL certificate problem: unable to get local issuer certificate

ما الذي تقوله هذه الرسالة؟! لا يمكن التحقق من شهادة SSL الخاصة بموقع فيس بوك! يا للكارثة شهادة SSL لديهم فيها مشكلة… كيف يمكنني إخبارهم بهذا؟

الواقع أنني استغربت ظهور هذه الرسالة عند محاولة الدخول عبر فيس بوك فقط ولم تظهر عند الدخول عبر تويتر. المشكلة هي أن إضافة cURL لدي ليس لديها  معلومات ناشري شهادات SSL حتى تتمكن من التحقق من صلاحية شهادة فيس بوك، وكان الحل بتنزيل هذه المعلومات وتعديل ملف php.ini بحيث تتمكن cURL من قراءة هذه المعلومات والتحقق من شهادة فيس بوك.

هناك حل ثان وهو تعطيل خيار التحقق من شهادة SSL، ولكن هذا الحل لا ينصح به.

قد لا تواجهك هذه المشكلة، فهي متعلقة بطريقة ضبط php لديك، على أي حال إذا كنت تستخدم XAMPP على ويندوز مثلي وواجهتك هذه المشكلة إليك الطريقة  التي استخدمتها لتجاوزها:

أولاً حصلت على ملف cacert.pem الذي يحوي معلومات CA (أي Certificate Authorities وهي الهيئات التي تصادق على شهادات SSL لمواقع الوب) من هذا الرابط: http://curl.haxx.se/docs/caextract.html.

بعد ذلك نسخته إلى مجلد xampp/php وأخيراً عدلت ملف php.ini وأضفت ما يلي إليه:

[cURL] 
curl.cainfo="C:\xampp\php\cacert.pem"

تستخدم cURL هذا المتغير للبحث عن ملف معلومات CA وقراءته، بعد ذلك ستتمكن من التحقق من سلامة شهادة فيس بوك بنجاح وستختفي هذه المشكلة.

تنبيه: يجب إعادة تشغيل خادوم الوب حتى تأخذ هذه التغييرات مفعولها.

المشكلة 2

InvalidStateException in AbstractProvider.php line 191

وهذه نفسها تعطي أحيانًا رسالة Client Error.

تظهر هذه المشكلة إذا استدعيت التعليمة التالية أكثر من مرة في تطبيقك لسبب أو لآخر:

 Socialite::with('facebook')->user() 

لا يمكنك استدعاء هذه التعليمة سوى مرة واحدة فقط بعد توجيه المستخدم إلى فيس بوك عبر تعليمة:

Socialite::with('facebook')->redirect()

والحصول على كود الدخول.

الحل: عليك الاحتفاظ بالكائن oauthUser$ الذي حصلت عليه من هذه التعليمة في متغير وتمريره إلى الدوال الأخرى إذا كنت تحتاجه بدلاً من طلبه كل مرة عبر Socialite.

المشكلة 3

عند إضافة زر "دخول عبر تويتر" استمرت الصفحة في الدخول في حلقة إعادة توجيه لا نهائية رغم فحص وجود المتغير code في الطلب. تبين لي بعدها أن تويتر يستخدم النسخة 1.0 من بروتوكول OAuth ولذلك فهو يمرر متغيرات مختلفة.

الحل: غيرت الشرط بحيث يفحص وجود المتغير oauth_token بدلاً من code.

الختام

في هذا الدرس اطلعنا على حزمة Socialite الخاصة بإطار Laravel التي تسمح بالاستيثاق من هوية المستخدمين بسهولة تامة عبر حساباتهم على الشبكات الاجتماعية.

يمكنك تنزيل مشروع Laravel المستخدم في هذا الدرس والاطلاع على الشفرة البرمجية فيه.

إذا واجهتك مشكلة في تطبيق هذا الدرس فلا تتردد في طرح سؤالك في تعليق أو على منصة الأسئلة والأجوبة التابعة لأكاديمية حسوب.





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن