Laravel Sanctum ve Passport ile API Kimlik Doğrulama
Laravel'de API kimlik doğrulama için iki resmi paket bulunur: Sanctum ve Passport. İkisi de token tabanlı çalışır ama farklı ihtiyaçlara hitap eder. Bu rehberde hangi paketi ne zaman seçeceğinizi, her ikisinin de kurulum ve kullanım adımlarını ve güvenlik açısından dikkat edilmesi gereken noktaları ele alıyoruz.
Önkoşullar ve Kapsam
- Laravel 10 veya üzeri bir proje (Sanctum ve Passport bu sürümlerde birinci taraf paket olarak gelir).
- Composer ve
php artisankomutlarını çalıştırabiliyor olmak. userstablosu ve standart Laravel kimlik doğrulama iskeleti.- API rotalarının
routes/api.phpüzerinden tanımlandığı varsayılır.
Sürüm numarası vermediğim yerlerde paketlerin güncel kararlı sürümünü kastettiğimi unutmayın; kurulumdan önce Composer'ın çözdüğü sürümü kontrol edin.
Sanctum mu, Passport mu?
İki paketin temel farkı kapsam ve karmaşıklık düzeyidir.
Sanctum, hafif bir kimlik doğrulama paketidir. İki senaryoyu destekler:
- SPA (Single Page Application) kimlik doğrulaması: Aynı alan adındaki birinci taraf frontend'ler için çerez tabanlı oturum kimlik doğrulaması.
- Basit API token'ları: Mobil uygulama veya üçüncü taraf istemciler için kullanıcıya bağlı, veritabanında saklanan token'lar.
Passport, tam kapsamlı bir OAuth2 sunucusudur. Yetkilendirme kodu, istemci kimlik bilgileri, kişisel erişim token'ları, token yenileme ve kapsam (scope) yönetimi gibi OAuth2 akışlarını destekler.
Pratik seçim kuralı:
- Yalnızca kendi SPA'nız veya mobil uygulamanız için token üretiyorsanız Sanctum yeterlidir ve daha az bakım gerektirir.
- Üçüncü taraf uygulamaların sizin API'niz üzerinden kullanıcı adına işlem yapmasını istiyorsanız, OAuth2 akışlarına (yetkilendirme kodu, refresh token) ihtiyacınız varsa Passport gerekir.
Çoğu iç proje için Sanctum doğru başlangıçtır; Passport'u yalnızca gerçekten OAuth2 gerektiğinde ekleyin.
Sanctum ile API Token Kimlik Doğrulama
Kurulum
Sanctum'u Composer ile ekleyin:
composer require laravel/sanctum
Ardından migration dosyalarını yayınlayıp çalıştırın:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
HasApiTokens trait'ini kullanıcı modelinize ekleyin:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
// ...
}
Token Üretme
Kullanıcı giriş yaptıktan sonra token üretmek için:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
Route::post('/login', function (Request $request) {
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = \App\Models\User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['Giriş bilgileri hatalı.'],
]);
}
$token = $user->createToken('api-token')->plainTextToken;
return response()->json(['token' => $token]);
});
createToken metodu bir token nesnesi döner. plainTextToken özelliği yalnızca bu anda erişilebilir; veritabanında token'ın hash'lenmiş hâli saklanır. Bu değeri istemciye bir kez gönderip kaybettiğinizde yeniden üretmeniz gerekir.
Token Yetenekleri (Abilities)
Token'a yetenek atayarak erişimi sınırlandırabilirsiniz:
$token = $user->createToken('api-token', ['posts:read', 'posts:write'])->plainTextToken;
Rota tarafında bu yetenekleri kontrol edin:
Route::middleware('auth:sanctum')->get('/posts', function (Request $request) {
if (! $request->user()->tokenCan('posts:read')) {
abort(403);
}
// ...
});
Rotaları Korumak
routes/api.php içinde korumalı rotaları auth:sanctum middleware'i ile işaretleyin:
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', fn (Request $request) => $request->user());
Route::post('/logout', function (Request $request) {
$request->user()->currentAccessToken()->delete();
return response()->noContent();
});
});
Beklenen Davranış
- İstemci,
Authorization: Bearer <token>başlığıyla istek gönderir. - Geçerli token varsa
$request->user()ilgili kullanıcıyı döner. - Geçersiz veya silinmiş token'da istek
401 Unauthorizedile reddedilir. currentAccessToken()->delete()yalnızca o isteği yapan token'ı siler;tokens()->delete()ise kullanıcının tüm token'larını iptal eder.
Passport ile OAuth2
Kurulum
composer require laravel/passport
php artisan migrate
php artisan passport:install
passport:install komutu şifreleme anahtarlarını üretir ve iki adet OAuth2 istemcisi (biri kişisel erişim, biri şifre hibesi için) oluşturur. Üretilen client_id ve client_secret değerlerini güvenli biçimde saklayın.
Kullanıcı modeline HasApiTokens trait'ini ekleyin:
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
}
config/auth.php içinde api guard'ının sürücüsünü passport yapın:
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Token Üretme (Şifre Hibesi)
use Illuminate\Support\Facades\Http;
$response = Http::asForm()->post(config('app.url') . '/oauth/token', [
'grant_type' => 'password',
'client_id' => config('passport.password_client_id'),
'client_secret' => config('passport.password_client_secret'),
'username' => $request->email,
'password' => $request->password,
'scope' => '',
]);
return response()->json($response->json());
Bu istek başarılı olduğunda access_token, refresh_token ve expires_in alanlarını içeren bir yanıt döner. client_secret değerini asla istemci tarafına göndermeyin; bu akış sunucu-sunucu veya güvenilir istemci senaryosu içindir.
Rotaları Korumak
Route::middleware('auth:api')->get('/user', fn (Request $request) => $request->user());
Kapsamlar (Scopes)
Passport'ta izinler kapsam üzerinden tanımlanır. AuthServiceProvider içinde kapsamları kaydedin:
use Laravel\Passport\Passport;
public function boot(): void
{
Passport::tokensCan([
'posts:read' => 'Gönderileri oku',
'posts:write' => 'Gönderi oluştur ve güncelle',
]);
}
Rota tarafında kapsam kontrolü:
Route::middleware(['auth:api', 'scope:posts:read'])->get('/posts', /* ... */);
Beklenen Davranış
- İstemci,
Authorization: Bearer <access_token>başlığıyla istek gönderir. - Token süresi dolduğunda
401döner; istemcirefresh_tokenile yeni bir access token alır. - Kapsam dışı bir rotaya erişim
403ile reddedilir.
Yaygın Hatalar
- Plain text token'ı kaydetmek: Sanctum'da
plainTextTokenyalnızca üretim anında elde edilir. Veritabanında saklanan değer hash'tir; karşılaştırma içinplainTextToken'ı kullanmayın. client_secret'ı istemciye gömmek: Passport'ta istemci sırrı sunucu tarafında kalmalıdır. Mobil veya tarayıcı uygulamasına gömmek sızıntıya yol açar.- Sanctum'u Passport ile karıştırmak: İki paketi aynı projede aynı amaçla kullanmak guard çakışmalarına ve kafa karışıklığına neden olur. Birini seçin.
- Token iptalini unutmak: Çıkış (logout) akışında
currentAccessToken()->delete()çağırmazsanız token geçerli kalır. - SPA için yanlış yöntem: Aynı alan adındaki SPA'da Bearer token yerine Sanctum'un çerez tabanlı (stateful) akışını kullanmak CSRF koruması açısından daha doğrudur.
Güvenlik Notları
- HTTPS zorunlu: Bearer token'lar düz HTTP üzerinden gönderilirse ağ üzerinde ele geçirilebilir. Tüm API trafiği TLS üzerinden akmalıdır.
- Token ömrü: Sanctum token'ları varsayılan olarak süresizdir.
config/sanctum.phpiçindekiexpirationayarıyla ömür tanımlayın. Passport'ta access token ömrüPassport::tokensExpireIn()ile ayarlanır. - Kapsam en az yetki ilkesi: Token'lara yalnızca gereken yetenekleri/kapsamları verin. Geniş yetkili token sızarsa etki alanı büyür.
- Rate limiting: Giriş ve token uç noktalarına
throttlemiddleware'i ekleyin; kaba kuvvet denemelerini sınırlar. - Token saklama: İstemci tarafında token'ları
localStorageyerine mümkünse güvenli çerez veya platformun güvenli depolama mekanizmasında tutun.
Sonuç
Sanctum, kendi uygulamalarınız için token üretip yönetmenin en sade yoludur; SPA ve mobil senaryoların büyük kısmını karşılar. Passport ise üçüncü taraf entegrasyonları veya standart OAuth2 akışları gerektiğinde devreye girer. Seçimi ihtiyacınıza göre yapın, token ömrü ve kapsamları baştan tanımlayın ve tüm trafiği HTTPS üzerinden taşıyın.