REST API Development

API Authentication with Passport

25 min Lesson 10 of 35

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:
  1. Install and configure Passport in your application
  2. Create a password grant client and implement login endpoint
  3. Define custom scopes for posts (read, write, delete) and users (read, write)
  4. Implement token refresh endpoint
  5. Create middleware that checks for specific scopes before allowing actions
  6. 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.