استخدام Passport لإعداد خادوم Oauth على Laravel


محمد أحمد العيل

لطالما وفّر إطار العمل Laravel آليات سهلة للاستيثاق Authentication بالطرق التقليدية مثل استمارات Forms تسجيل الدخول في واجهة الوِب؛ إلا أن الأمر لم يكن سهلا بما فيه الكفاية بالنسبة للاستيثاق من الطلبات القادمة عبر واجهة تطبيقات برمجية. يأتي الإصدار 5.3 بحزمة جديدة، تُسمّى Passport، تهتم بتيسير استخدام Laravel لضبط خادوم Oauth وإعداده. يتناول هذا الدرس كيفية إعداد Laravel ليوفّر وظيفة خادوم Oauth اعتمادا على حزمة Passport.

laravel.png

خادوم Oauth

من المحتمل جدّا أنه سبق لك التعامل مع خادوم Oauth؛ مثلا عند استخدام حسابات على الشبكات الاجتماعية (فيس بوك، تويتر، … إلخ) للتسجيل في موقع على الشبكة. الفكرة التي تنبني عليها آلية الاستيثاق هذه هي تفويض عمليّة الاستيثاق إلى خادوم Oauth. نفترض مثلا أن لديك موقعا يستخدم هذه الآلية؛ عند تسجيل دخول الموقع عن طريق الحساب على فيس بوك فإن ما يحدُث هو التالي:

  • يلج المستخدم إلى الموقع ويختار طريقة تسجيل الدخول (فيس بوك).
  • يُنقَل المستخدم إلى صفحة تسجيل الدخول في فيس بوك حيثُ يُطلَب منه تسجيل الدخول إن لم يكن سبق له ذلك ثم يؤكّد سماحه لموقعك باستخدام حسابه على فيس بوك.
  • يُعاد توجيه المستخدم إلى الموقع مع تسجيل دخوله إليه.

يفترض هذا الدرس أن لديك إلماما بكيفية عمل خواديم Oauth للتصريح بالدخول. يمكن الاطّلاع على مقال مدخل إلى OAuth 2 للمزيد عن هذا الموضوع. إن كنتَ تبحث عن كيفية تسجيل الدخول إلى تطبيقك باستخدام حساب على موقع يوفّر وظيفة Oauth فراجع درس تسجيل الدخول عبر الشبكات الاجتماعية في Laravel باستخدام Socialite.

إعداد خادوم Oauth على Laravel 5.3

يحتاج إعداد Passport على Laravel لتنفيذ بضعة أوامر وتحرير ملفات إعداد؛ إلا أن العملية عموما سهلة. ابدأ بتثبيت الإصدار 5.3 من Laravel:

 
composer create-project --prefer-dist laravel/laravel laravel53oauth "5.3.*"

نستخدم composer لإضافة حزمة Passport إلى متطلبات المشروع:

 
composer require laravel/passport

نسجّل مزوّد الخدمة Passport في ملفّ الإعداد config/app.php. افتح الملف ثم أضف السطر التالي إلى مصفوفة providers:

 
Laravel\Passport\PassportServiceProvider::class,

تأكّد من إعدادات قاعدة البيانات ثمّ نفّذ أمر التهجير Migration التالي:

 
php artisan migrate

ستلاحظ تنفيذ تهجيرات إضافية علاوة على تلك التي تأتي مبدئيّا مع Laravel:

Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2016_06_01_000001_create_oauth_auth_codes_table
Migrated: 2016_06_01_000002_create_oauth_access_tokens_table
Migrated: 2016_06_01_000003_create_oauth_refresh_tokens_table
Migrated: 2016_06_01_000004_create_oauth_clients_table
Migrated: 2016_06_01_000005_create_oauth_personal_access_clients_table

يأتي مزوّد الخدمة Passport مع تهجيرات لإنشاء جداول تخزّن عملاء Client التطبيق ومعلومات رموز الوصول Access tokens. ثم ننفّذ الأمر التالي الذي سيولّد مفاتيح التعميّة Encryption keys الخاصّة بمشروع كما أنه ينشئ رموز الوصول ويخزّنها في جدول البيانات المناسب:

 
php artisan passport:install

ننتقل الآن إلى نموذج المستخدم User ونضيف إليه السّمة Trait المسمّاة HasApiTokens. يُصبح النموذج على النحو التالي (بعد نزع التعليقات للاختصار):

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
// استيراد السّمة
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    // استخدام السمة
    use HasApiTokens, Notifiable;

    protected $fillable = [
        'name', 'email', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];
}

توفّر هذه السّمة مجموعة من الدوال المساعدة التي تتيح الحصول على رموز الوصول الخاصّة بالمستخدمين ومدى الصلاحيّات Permissions scope المتاح للعميل.

الخطوة المواليّة هي استدعاء الدالة Passport::routes ضمن الدالة boot الموجودة في مزوّد خدمة الاستيثاق app/Providers/AuthServiceProvider.php:

public function boot()
{
    $this->registerPolicies();

    Passport::routes();
}

لا تنس استدعاء الصنف Laravel\Passport\Passport في مزوّد خدمة الاستيثاق:

 
use Laravel\Passport\Passport;

يمكنك ملاحظة المسارات الجديدة التي أضافتها الدالة Passport::routes بتنفيذ أمر Artisan التالي الذي يسرُد لائحة بالمسارات الموجودة في المشروع:

 
php artisan route:list

الخطوة الأخيرة هي تعديل ملف الإعداد config/auth.php وتحديد قيمة driver في المصفوفة api لتصبح passport بدلا من token:

'api' => [
    'driver' => 'passport',
    'provider' => 'users',
],

يعتمد Laravel مبدئيا على رموز للاستيثاق من الطلبات القادمة عبر واجهة التطبيقات البرمجية؛ إلا أن هذه الرموز المبدئية لا تتيح إلا القليل من الخيارات، عكس ما يوفّره Passport.

بهذا نكون أنهينا إعداد Laravel لأداء وظيفة خادوم Oauth.

اختبار خادوم Oauth

المرحلة المواليّة بعد إعداد Laravel ليعمل خادوم Oauth هي اختبار ما أعددناه. سنحتاج لعميل نجرّب عن طريقه الاستيثاق من الخادوم. نعيد النظر، قبل الانتقال للجانب التطبيقي من الاختبار، في كيفية الاستيثاق من مستخدم في خادوم Oauth.

تضمّ العمليّة ثلاثة أطراف: المستخدِم، الخادوم والعميل. يريد المستخدِم تسجيل الدخول إلى العميل بالاعتماد على معلوماته الموجودة في الخادوم:

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

إنشاء مستخدمين للاختبار

نبدأ بإنشاء مستخدمين لتجربة الاستيثاق. ننفذ أمر Artisan التالي لإنشاء صنف بذر Seed جديد:

 
php artisan make:seeder UsersTableSeeder

ثم نعدّل الدالة run لإنشاء مستخدمَيْن جديديْن:

public function run()
{

    $user1 = [
    'name' => 'Med Ahmed Eyil',
    'email' => 'medeyil@laravel.com',
    'password' => Hash::make('1234'),
    ];
    $user2 = [
    'name' => 'Fatima Benziane',
    'email' => 'fbenziane@laravel.com',
    'password' => Hash::make('4567'),
    ];

    User::create($user1);
    User::create($user2);
}

لا تنس استيراد النموذج User:

 
use App\User;

ثم نعتمد صنف البذر بتعديل الدالة run في الصنف DatabaseSeeder:

public function run()
{
    $this->call(UsersTableSeeder::class);
}

ثم ننفذ أمر البذر:

 
php artisan db:seed

جهّزنا الآن طرفيْن من الأطراف الثلاثة التي تحدثنا عنها، بقي العميل.

إعداد العميل

يوفّر Laravel بواسطة Passport مسارات من بينها المسار oauth/token/ الذي يمكّن من الحصول على رمز وصول. ينتظر المسار المعطيَات التاليّة:

  • طريقة منح الصلاحيّة grant_type.
  • معرّف المستخدم client_id.
  • عبارة سر العميل client_secret.

يحدّد المعطى grant_type طريقةَ توليد رموز الوصول. بما أننا نريد أن يطلُب الخادوم من المستخدم بريده الإلكتروني وكلمة السّر لكي يولّد رمز وصول للتطبيق العميل، فإن هذا المعطى سيأخذ القيمة password. يمكن لهذا المعطى أن يأخذ قيما أخرى حسب الطريقة التي تريد للتطبيقات العميلة أن تصل بها إلى موارد محميّة على خادومك. يعيّن المعطى الثاني معرّف العميل الذي نريد الاستيثاق منه؛ أما عبارة السّر فمهمتها إثبات أن العميل لديه ترخيص للاستيثاق بخادوم Oauth الذي أعددناه. سنحتاج أيضا لتمرير بريد المستخدم الإلكتروني وكلمة سره، بما أننا اخترنا طريقة password لتوليد الرموز.

نعود لمشروع Laravel حيث يوجد الخادوم ثم ننفذ الأمر التالي:

php artisan passport:client --password
    What should we name the password grant client? [Laravel Password Grant Client]:
    > Academy Password Client

    Password grant client created successfully.
    Client ID: 3
    Client Secret: 17ZNjWn5A1I3mYOhGXZQFNACTdyt9Nwxup9jXG4M

يطلُب الأمر أعلاه إنشاء تصريح لعميل للاستيثاق بخادوم Oauth ويحدّد طريقة توليد رموز الوصول بـpassword. يطلُب الأمر اسمًا للعميل ثم بعد كتابته وتأكيده يمنحنا معرّفا للعميل وعبارة سرّ.

ملحوظة: رمز الوصول المولَّد فريد Unique، القيمة التي ستظهر لديك مختلفة حتما عن القيمة الظاهرة هنا.

نعرّج قليلا، قبل الانتقال إلى العميل، على قاعدة البيانات وننظر في الجدول oauth_clients.

01_oauth_clients.png

لاحظ في الجدول وجود العميل الذي أنشأناه للتو، إضافة إلى عميليْن آخرين؛ أنشأهما الأمر passport:install عندما نفّذناه. تمكن ملاحظة أن قيمة العمود password_client بالنسبة للعميل الذي أنشأناه هي 1 للدلالة على أنه من نوع password.

نأتي الآن للعميل، الذي يمكن أن يكون تطبيق وِب، تطبيقا للأجهزة المحمولة أو غيره. بالنسبة لي سأختار إضافة Postman على متصفح Chrome، وهي إضافة تمكّن من اختبار واجهات التطبيقات البرمجية.

إرسال الطلبات

نشغّل خادوم Passport بتنفيذ الأمر:

 
php artisan serve

ثم نفتح واجهة Postman ونعطيه المسار oauth/token/ ونمرّر له المعطيات التي تحدّثنا عنها سابقا. نحدّد نوع الإجراء بـ POST، ونختار تبويب Body لأننا في صدد ذكر بيانات جسم الطلب، ونختار x-www-form-urlencode للترميز. يؤدي استخدام الترميز x-www-form-urlencode إلى إرسال البيانات بنفس الطريقة التي تُرسَل بها بيانات استمارة Form على موقع وِب باستخدام الإجراء POST.

02_postman_get_token.png

إن كنتَ تُفضّل سطر الأوامر فبإمكانك تنفيذ أمر curl التالي والذي يؤدي نفس المهمّة (الخيار d- لاستخدام الترميز x-www-form-urlencode):

 
curl -d "grant_type=password&client_id=3&client_secret=17ZNjWn5A1I3mYOhGXZQFNACTdyt9Nwxup9jXG4M&username=medeyil@laravel.com&password=1234" http://localhost:8000/oauth/token

نحصُل بعد ذكر المعطيات الصحيحة وتنفيذ الطلب على الرد التالي:

{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token":"رمز طويل هنا" ,
"refresh_token": "رمز طويل هنا"
}

03_postman_get_token.png

تتضمن الإجابة أربعة حقول:

  • نوعية الرمز token_type. يعني النوع bearer أن بإمكان الرمز منح الوصول إلى الخادوم دون الحاجة لذكر معلومات إضافية (مثل مفاتيح التعمية).
  • مدة صلاحيّة الرمز expires_in. يحدّد مدّة صلاحيّة الرمز، بالثواني. يحدّد Laravel مبدئيا مدة طويلة (حوالي سنة)؛ إلا أنه يمكن تغيير هذه المدة.
  • رمز الوصول access_token.
  • رمز التحديث refresh_token. يُستخدَم لطلب رمز وصول جديد عند انتهاء مدة صلاحية رمز الوصول الحالي.

حصلنا على رمز الوصول؛ سنرى الآن كيفية استخدامه للوصول إلى موارد على الخادوم.

يُرسِل العميل من الآن فصاعدا رمزَ الوصول الذي حصل عليه مع كل طلب موجّه للخادوم يحتاج للاستيثاق. إن نظرت في ملفّ مسارات واجهة التطبيقات البرمجية routes/api.php فستجد أن المسار user/ محميّ بـ auth:api:

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:api');

يعني هذا أنه يتوجّب على المستخدم تقديم بيانات الاستيثاق الخاصّة بواجهة التطبيقات، التي جعلناها تعتمد على التعريف passport، حتى يتمكن من الحصول على المورِد (الذي هو في المثال أعلاه بيانات المستخدم الذي أرسل الطلب).

نعود لـPostman لطلب المسار api/user/ مع تقديم رمز الوصول الذي حصلنا عليه سابقا.

ملحوظة: تنبغي إضافة api/ إلى جميع المسارات المعرّفة في الملف routes/api.php.

نحدّد في خانة الترويسة Headers طبيعة المعلومات التي نريد تقديمها إلى المسار والإجراء GET المعرَّف في المسار user/ الخاص بواجهة التطبيقات. الترويسة التي نريد تحديد قيمتها هي Authorization (طلب استيثاق) وقيمتها هي:

 
Bearer access_token

حيث access_token رمز الوصول الذي حصلنا عليه في الخطوة السابقة، مسبوقا بنوع الرمز (Bearer). نحصُل على معلومات المستخدِم الذي قدّم الطلب.

03_postman_use_token.png

يمكن الحصول على نفس النتيجة بأمر curl التالي:

 
curl -H "Authorization: Bearer access_token" http://localhost:8000/api/user

يعني الخيار H- أننا نريد تضمين الترويسة التاليّة له في الطلب.

نلاحظ أن الإجابات التي نحصُل عليها من خادوم Passport مهيّأة بصيغة JSON، وهو ما يجعل التعامل معها سهلا في التطبيقات العميلة على الأجهزة المحمولة والمتصفّحات.

الخلاصة هي أننا استخدمنا بيانات المستخدم (البريد وكلمة السر) للحصول على رمز وصول من خادوم Passport ثم استخدمنا رمز الوصول هذا للاطلاع على موارد محميّة على الخادوم.





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


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



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

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

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


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

تسجيل الدخول

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


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