Understanding Laravel Passport
Laravel Passport provides a full OAuth2 server implementation for your Laravel application. It's ideal when you need to support third-party OAuth authentication, multiple grant types, or when building complex authentication systems.
What is OAuth2?
OAuth2 is an authorization framework that enables applications to obtain limited access to user accounts on an HTTP service. Key concepts:
- Resource Owner: The user who authorizes access to their account
- Client: The application requesting access to the user's account
- Authorization Server: Issues access tokens after successful authentication
- Resource Server: Hosts the protected user resources
- Access Token: A credential used to access protected resources
- Refresh Token: Used to obtain new access tokens when they expire
Note: Use Passport when you need full OAuth2 functionality. For simpler use cases, Sanctum is lighter and easier to implement.
Installing Passport
Install Passport via Composer:
composer require laravel/passport
php artisan migrate
php artisan passport:install
The passport:install command creates encryption keys and generates OAuth2 clients.
Configuring Passport
Add the HasApiTokens trait to your User model:
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
// ... rest of model
}
Register Passport routes in AuthServiceProvider:
use Laravel\Passport\Passport;
public function boot(): void
{
Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
}
Set the API guard driver to passport in config/auth.php:
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
OAuth2 Grant Types
Passport supports multiple grant types for different use cases:
1. Password Grant (Username & Password)
For first-party applications where you trust the client with user credentials:
// Create a password grant client
php artisan passport:client --password
// Request token
POST /oauth/token
{
"grant_type": "password",
"client_id": "client-id",
"client_secret": "client-secret",
"username": "user@example.com",
"password": "password",
"scope": "*"
}
Implement password grant in your controller:
use Illuminate\Support\Facades\Http;
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$response = Http::post(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' => '*',
]);
if ($response->failed()) {
return response()->json([
'success' => false,
'message' => 'Invalid credentials'
], 401);
}
return response()->json([
'success' => true,
'data' => $response->json()
]);
}
Tip: Store client ID and secret in your .env file for security.
2. Authorization Code Grant
For third-party applications that need user authorization:
// Create an authorization code client
php artisan passport:client
// Step 1: Redirect user to authorization URL
GET /oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=code&scope=*
// Step 2: User approves, receives authorization code
// Step 3: Exchange code for access token
POST /oauth/token
{
"grant_type": "authorization_code",
"client_id": "client-id",
"client_secret": "client-secret",
"redirect_uri": "https://your-app.com/callback",
"code": "authorization-code"
}
3. Client Credentials Grant
For machine-to-machine authentication without user involvement:
// Create a client credentials client
php artisan passport:client --client
// Request token
POST /oauth/token
{
"grant_type": "client_credentials",
"client_id": "client-id",
"client_secret": "client-secret",
"scope": "*"
}
4. Personal Access Tokens
For user-generated API tokens (similar to Sanctum):
$token = $user->createToken('Token Name')->accessToken;
// With scopes
$token = $user->createToken('Token Name', ['post-read', 'post-write'])->accessToken;
Protecting Routes with Passport
Use the auth:api middleware:
// routes/api.php
Route::middleware('auth:api')->group(function () {
Route::get('/user', function (Request $request) {
return $request->user();
});
Route::apiResource('posts', PostController::class);
});
Token Scopes
Define scopes in AuthServiceProvider:
use Laravel\Passport\Passport;
public function boot(): void
{
Passport::tokensCan([
'post-read' => 'Read posts',
'post-write' => 'Create and edit posts',
'post-delete' => 'Delete posts',
'user-read' => 'Read user profile',
'user-write' => 'Update user profile',
]);
Passport::setDefaultScope([
'post-read',
'user-read',
]);
}
Check scopes in your routes:
// Require specific scope
Route::middleware(['auth:api', 'scope:post-write'])
->post('/posts', [PostController::class, 'store']);
// Require any of the scopes
Route::middleware(['auth:api', 'scopes:post-read,post-write'])
->get('/posts', [PostController::class, 'index']);
// Check scopes in controller
public function store(Request $request)
{
if (!$request->user()->tokenCan('post-write')) {
return response()->json([
'error' => 'Insufficient permissions'
], 403);
}
// Create post...
}
Refreshing Tokens
Request new access tokens using refresh tokens:
POST /oauth/token
{
"grant_type": "refresh_token",
"refresh_token": "the-refresh-token",
"client_id": "client-id",
"client_secret": "client-secret",
"scope": "*"
}
Handle token refresh in your application:
public function refresh(Request $request)
{
$request->validate([
'refresh_token' => 'required',
]);
$response = Http::post(url('/oauth/token'), [
'grant_type' => 'refresh_token',
'refresh_token' => $request->refresh_token,
'client_id' => config('passport.password_client_id'),
'client_secret' => config('passport.password_client_secret'),
'scope' => '*',
]);
return response()->json($response->json());
}
Revoking Tokens
Allow users to revoke access tokens:
public function logout(Request $request)
{
$request->user()->token()->revoke();
return response()->json([
'success' => true,
'message' => 'Successfully logged out'
]);
}
// Revoke all tokens for a user
$user->tokens->each(function ($token) {
$token->revoke();
});
Managing OAuth Clients
Passport provides routes for managing OAuth clients:
// List all clients
GET /oauth/clients
// Create new client
POST /oauth/clients
{
"name": "My Application",
"redirect": "https://example.com/callback"
}
// Update client
PUT /oauth/clients/{client-id}
{
"name": "Updated Name",
"redirect": "https://example.com/new-callback"
}
// Delete client
DELETE /oauth/clients/{client-id}
Personal Access Tokens via UI
Passport includes Vue components for managing personal access tokens:
// In your Blade view
<passport-clients></passport-clients>
<passport-authorized-clients></passport-authorized-clients>
<passport-personal-access-tokens></passport-personal-access-tokens>
// Register components in app.js
import Vue from 'vue';
Vue.component('passport-clients', require('./components/passport/Clients.vue').default);
Vue.component('passport-authorized-clients', require('./components/passport/AuthorizedClients.vue').default);
Vue.component('passport-personal-access-tokens', require('./components/passport/PersonalAccessTokens.vue').default);
Customizing Token Lifetimes
Configure token expiration times:
// In AuthServiceProvider
public function boot(): void
{
// Access tokens expire in 15 days
Passport::tokensExpireIn(now()->addDays(15));
// Refresh tokens expire in 30 days
Passport::refreshTokensExpireIn(now()->addDays(30));
// Personal access tokens expire in 6 months
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
// Or never expire
Passport::tokensExpireIn(null);
}
Customizing User Provider
Use a custom model or guard for OAuth:
// Use different model
Passport::useClientModel(CustomClient::class);
Passport::useTokenModel(CustomToken::class);
// Use custom guard
Route::middleware('auth:custom-guard')->group(function () {
// Protected routes
});
Testing with Passport
Test authenticated routes in your tests:
use Laravel\Passport\Passport;
public function test_user_can_view_posts()
{
$user = User::factory()->create();
Passport::actingAs($user);
$response = $this->getJson('/api/posts');
$response->assertStatus(200);
}
public function test_user_with_scope_can_create_post()
{
$user = User::factory()->create();
Passport::actingAs($user, ['post-write']);
$response = $this->postJson('/api/posts', [
'title' => 'Test Post',
'content' => 'Test content',
]);
$response->assertStatus(201);
}
Rate Limiting OAuth Endpoints
Protect OAuth endpoints from abuse:
// In RouteServiceProvider
RateLimiter::for('oauth', function (Request $request) {
return Limit::perMinute(5)
->by($request->ip())
->response(function () {
return response()->json([
'error' => 'Too many login attempts. Please try again later.'
], 429);
});
});
// Apply to routes
Route::middleware('throttle:oauth')->group(function () {
Route::post('/oauth/token', [\Laravel\Passport\Http\Controllers\AccessTokenController::class, 'issueToken']);
});
Passport vs Sanctum Comparison
| Feature |
Sanctum |
Passport |
| Complexity |
Simple |
Complex |
| OAuth2 Support |
No |
Yes |
| SPA Auth |
Yes (Cookie) |
Yes (Token) |
| Mobile Apps |
Yes |
Yes |
| Third-Party Apps |
No |
Yes |
| Token Refresh |
No |
Yes |
| Scopes |
Basic |
Advanced |
Exercise:
- Install and configure Passport in your application
- Create a password grant client and implement login endpoint
- Define custom scopes for posts (read, write, delete) and users (read, write)
- Implement token refresh endpoint
- Create middleware that checks for specific scopes before allowing actions
- Test your implementation with Postman or a REST client
Security Best Practices
- Use HTTPS: Always use HTTPS to protect tokens in transit
- Secure Client Secrets: Store client secrets securely, never expose them in frontend code
- Short Token Lifetimes: Use short-lived access tokens (15-60 minutes) and longer refresh tokens
- Rotate Secrets: Periodically rotate client secrets for security
- Limit Scopes: Grant minimal necessary permissions to tokens
- Rate Limiting: Implement rate limiting on OAuth endpoints
- Audit Logs: Log token issuance and usage for security auditing
- Revocation: Implement token revocation for compromised tokens
Summary
Laravel Passport provides a complete OAuth2 server implementation with multiple grant types, token refresh, and advanced scope management. While more complex than Sanctum, it's essential for applications that need to support third-party OAuth authentication or require enterprise-grade authorization features. Choose Passport when building platforms that allow third-party integrations or need OAuth2 compliance.