البرمجة متوسط 16 دقيقة

كيفية بناء واجهة REST API في Laravel باستخدام Sanctum

Laravel Sanctum هو أخف طريقة للمصادقة القائمة على الرموز (tokens). بعكس Passport، يأتي Sanctum مدمجاً مع Laravel ولا يحتاج إلى بنية OAuth — فقط جدول واحد وسمة (trait). هو الخيار الصحيح لتطبيقات SPA، والعملاء من الأجهزة المحمولة، وأي تكامل خارجي تتحكم في طرفيه.

يأخذك هذا الدليل من البداية إلى واجهة API محمية بالكامل: موارد JSON، رموز استجابة HTTP صحيحة، وإلغاء الرموز. كل خطوة تفترض أن لديك تطبيق Laravel يعمل وقاعدة بيانات مضبوطة.

الخطوات

  1. 1

    تثبيت Sanctum وضبطه

    Sanctum مدمج في Laravel منذ الإصدار 11. للإصدارات الأقدم ثبّته عبر Composer. بعد التثبيت انشر ملفات الضبط والـ migration لإنشاء جدول personal_access_tokens.

    bash
    composer require laravel/sanctum
    
    php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
    
    php artisan migrate
  2. 2

    إضافة HasApiTokens إلى User

    أضف الـ trait اسمه HasApiTokens إلى نموذج User. هذا يمنح النموذج القدرة على إنشاء الرموز وإلغائها. يجب أن يكون الـ trait بجانب الـ traits الأخرى مثل HasFactory وNotifiable.

    php
    <?php
    
    namespace App\Models;
    
    use Illuminate\Foundation\Auth\User as Authenticatable;
    use Illuminate\Notifications\Notifiable;
    use Laravel\Sanctum\HasApiTokens;
    
    class User extends Authenticatable
    {
        use HasApiTokens, HasFactory, Notifiable;
    }
  3. 3

    تعريف مسارات API

    مسارات API توجد في routes/api.php. المسارات العامة — كتسجيل الدخول والتسجيل — تكون خارج أي middleware. المسارات المحمية تُلف داخل مجموعة middleware اسمها auth:sanctum. Laravel يضيف تلقائياً البادئة /api لكل مسار في هذا الملف.

    php
    <?php
    
    use App\Http\Controllers\Api\AuthController;
    use App\Http\Controllers\Api\UserController;
    use Illuminate\Support\Facades\Route;
    
    // Public
    Route::post('/register', [AuthController::class, 'register']);
    Route::post('/login',    [AuthController::class, 'login']);
    
    // Protected
    Route::middleware('auth:sanctum')->group(function () {
        Route::get('/user',  [UserController::class, 'show']);
        Route::post('/logout', [AuthController::class, 'logout']);
        Route::apiResource('/posts', PostController::class);
    });
  4. 4

    بناء نقطة نهاية تسجيل الدخول

    نقطة نهاية تسجيل الدخول تتحقق من البيانات، تنشئ رمزاً عبر createToken()، ثم تعيده في الاستجابة. اسم الرمز اختياري — استخدم شيئاً يعرّف العميل المُستهلك. عند الفشل أعد 401 لا 422؛ كلمة المرور الخاطئة خطأ مصادقة لا خطأ تحقق.

    php
    <?php
    
    namespace App\Http\Controllers\Api;
    
    use App\Models\User;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Hash;
    use Illuminate\Validation\ValidationException;
    
    class AuthController extends Controller
    {
        public function login(Request $request)
        {
            $request->validate([
                'email'    => 'required|email',
                'password' => 'required',
            ]);
    
            $user = User::where('email', $request->email)->first();
    
            if (! $user || ! Hash::check($request->password, $user->password)) {
                throw ValidationException::withMessages([
                    'email' => ['The provided credentials are incorrect.'],
                ]);
            }
    
            $token = $user->createToken($request->device_name ?? 'api')->plainTextToken;
    
            return response()->json(['token' => $token], 200);
        }
    
        public function logout(Request $request)
        {
            $request->user()->currentAccessToken()->delete();
    
            return response()->json(['message' => 'Logged out'], 200);
        }
    }
  5. 5

    إنشاء API Resource

    الـ API Resources تحوّل نماذج Eloquent إلى JSON. تتيح لك تحديد الحقول التي يتلقاها العميل — وتفصل مخطط قاعدة البيانات عن عقد الـ API. أنشئها بـ make:resource وأعِد كتابة طريقة toArray.

    bash
    php artisan make:resource UserResource
  6. 6

    تحديد شكل الـ Resource

    داخل toArray() اذكر فقط الحقول التي يجب أن تكشفها الـ API. لا تمرر النموذج كاملاً أبداً — ستكشف هاشات كلمات المرور والأعلام الداخلية وكل شيء آخر موجود على النموذج. استخدم ResourceCollection للقوائم.

    php
    <?php
    
    namespace App\Http\Resources;
    
    use Illuminate\Http\Resources\Json\JsonResource;
    
    class UserResource extends JsonResource
    {
        public function toArray($request): array
        {
            return [
                'id'         => $this->id,
                'name'       => $this->name,
                'email'      => $this->email,
                'created_at' => $this->created_at->toISOString(),
            ];
        }
    }
  7. 7

    إعادة رموز HTTP الصحيحة

    رموز حالة HTTP جزء من العقد. استخدمها بدقة: 200 للقراءة الناجحة، 201 للإنشاء الناجح، 422 لأخطاء التحقق (Laravel يفعل هذا تلقائياً)، 404 عند عدم وجود السجل، و401 للطلبات غير المصادق عليها. لا تعد 200 أبداً لخطأ.

    php
    <?php
    
    namespace App\Http\Controllers\Api;
    
    use App\Http\Resources\PostResource;
    use App\Models\Post;
    use Illuminate\Http\Request;
    
    class PostController extends Controller
    {
        public function index()
        {
            return PostResource::collection(Post::latest()->paginate(20));
            // 200 implicit
        }
    
        public function store(Request $request)
        {
            $data = $request->validate([
                'title'   => 'required|string|max:255',
                'content' => 'required|string',
            ]);
    
            $post = Post::create($data);
    
            return new PostResource($post); // 201
            // return (new PostResource($post))->response()->setStatusCode(201);
        }
    
        public function show(Post $post)
        {
            return new PostResource($post); // 200
        }
    
        public function destroy(Post $post)
        {
            $post->delete();
    
            return response()->json(null, 204); // No Content
        }
    }
  8. 8

    اختبار تدفق المصادقة

    أرسل الرمز في ترويسة Authorization كـ Bearer token في كل طلب لمسار محمي. سيحل Sanctum المستخدم تلقائياً عبر middleware اسمه auth:sanctum ويجعله متاحاً عبر $request->user().

    bash
    # 1. Login and capture the token
    curl -s -X POST https://example.com/api/login \
      -H 'Accept: application/json' \
      -H 'Content-Type: application/json' \
      -d '{"email":"user@example.com","password":"secret","device_name":"curl"}'
    
    # Response: {"token":"1|abc123..."}
    
    # 2. Use the token
    curl -s https://example.com/api/user \
      -H 'Accept: application/json' \
      -H 'Authorization: Bearer 1|abc123...'
  9. 9

    إلغاء الرموز

    إلغاء رمز واحد (تسجيل الخروج) يحذف الرمز الحالي. إلغاء جميع الرموز يُخرج المستخدم من كل الأجهزة — مفيد بعد تغيير كلمة المرور أو حادثة أمنية. كلتا العمليتين مجرد حذف Eloquent على جدول personal_access_tokens.

    php
    // Revoke current token (single device logout)
    $request->user()->currentAccessToken()->delete();
    
    // Revoke all tokens (sign out everywhere)
    $request->user()->tokens()->delete();

نصائح ومحاذير

  • أرسل دائماً <code>Accept: application/json</code> من العميل — بدونها يعيد Laravel صفحات HTML للأخطاء بدلاً من JSON.
  • حدّد صلاحيات الرمز عند إنشائه: <code>createToken('mobile', ['posts:read', 'posts:write'])</code>. تحقق منها بـ <code>$request->user()->tokenCan('posts:write')</code>.
  • اضبط مدة انتهاء الرمز في <code>config/sanctum.php</code> عبر <code>expiration</code> (بالدقائق). ادمجها مع نمط refresh-token للجلسات الطويلة.
  • لا تضع منطقاً حساساً في المسارات — أبقِ الـ controllers نحيفة وادفع قواعد الأعمال إلى service classes أو action classes.
  • استخدم <code>Route::apiResource()</code> بدلاً من <code>Route::resource()</code> — فهي تتخطى مسارات HTML (<code>create</code>، <code>edit</code>) التي لا تحتاجها الـ API.

خاتمة

Sanctum يمنحك مصادقة بالرموز جاهزة للإنتاج في أقل من ساعة. أبقِ الـ resources محددة، ورموز الحالة صحيحة، ومدة صلاحية الرموز قصيرة — وستحصل على واجهة API آمنة وسهلة الاستهلاك.

#Laravel #API #Sanctum
العودة إلى جميع الأدلة

هل تحتاج مساعدة في مشروعك؟

احجز استشارة مجانية لمدة 30 دقيقة لمناقشة تحدياتك التقنية واستكشاف الحلول معًا.