laravel/passport
Laravel Passport provides a full OAuth2 server for Laravel, enabling API authentication with access tokens, personal access tokens, and client credentials. Includes token issuing, revocation, and scope support with first-party integration.
Installation:
composer require laravel/passport
php artisan passport:install
php artisan passport:keys --force in production to regenerate keys.User Model:
Ensure your User model uses HasApiTokens trait and implements OAuthenticatable:
use Laravel\Passport\HasApiTokens;
use Laravel\Passport\Contracts\OAuthenticatable;
class User extends Authenticatable implements OAuthenticatable
{
use HasApiTokens;
// ...
}
First API Request:
Use Passport::actingAs() in tests or manually issue tokens via:
php artisan passport:client --password
client_id and client_secret for OAuth flows.Route Setup:
Route::post('/oauth/token', function () {
return Passport::issueToken('password', request()->all());
});
Client Request:
curl -X POST http://your-app.test/oauth/token \
-d "grant_type=password" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "username=user@example.com" \
-d "password=password" \
-d "scope=read"
Token Usage:
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $token,
])->get('/api/resource');
$token = Passport::issueToken('password', [
'grant_type' => 'password',
'client_id' => $clientId,
'client_secret' => $clientSecret,
'username' => $user->email,
'password' => $password,
'scope' => 'read write',
]);
$token = Passport::issueToken('client_credentials', [
'grant_type' => 'client_credentials',
'client_id' => $clientId,
'client_secret' => $clientSecret,
'scope' => 'api',
]);
// Redirect user to OAuth auth endpoint
return redirect()->route('passport.authorizations.create');
// Exchange code for token
$token = Passport::issueToken('authorization_code', [
'grant_type' => 'authorization_code',
'client_id' => $clientId,
'client_secret' => $clientSecret,
'code' => $authorizationCode,
'redirect_uri' => 'https://your-app.com/callback',
]);
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::middleware(['auth:api', 'scope:admin'])->get('/admin');
Route::middleware(['auth:api', 'client:mobile-app'])->get('/mobile');
$user = App\Models\User::find(1);
$token = $user->createToken('My PAT')->accessToken;
$user->tokens()->delete();
$user = User::factory()->create();
Passport::actingAs($user, ['read', 'write']);
$response = $this->getJson('/api/resource');
$response->assertOk();
$client = Passport::personalAccessClients()->create([
'name' => 'Test Client',
'password_client' => true,
]);
Passport::actingAsClient($client, ['api']);
AuthServiceProvider:
Passport::tokensCan([
'read' => 'Read access',
'write' => 'Write access',
]);
class PostPolicy {
public function update(User $user, Post $post) {
return $user->can('write');
}
}
Passport::tokensExpireIn(CarbonInterval::minutes(60));
Passport::refreshTokensExpireIn(CarbonInterval::days(30));
Passport::personalAccessTokensExpireIn(never());
Route::middleware(['throttle:60,1', 'auth:api'])->get('/api/resource');
actingAs with tenant context:
$tenant = Tenant::find($request->tenant_id);
Passport::actingAs($user, [], $tenant->id);
AccessToken model to add custom fields:
class AccessToken extends Laravel\Passport\AccessToken {
protected $casts = [
'metadata' => 'json',
];
}
bootstrap/cache/passport-public.key or passport-private.key to version control.php artisan passport:keys --force
Passport::clientCredentialsTokensExpireIn()).password_client for confidential clients:
$client = Passport::personalAccessClients()->create([
'name' => 'Confidential Client',
'password_client' => true, // Forces confidentiality
]);
5 and client ID 5).
findForPassport method:
public function findForPassport($identifier) {
return $this->where('id', $identifier)->orWhere('email', $identifier)->first();
}
auth:api must come before scope or client middleware:
// Correct
Route::middleware(['auth:api', 'scope:admin'])->get('/admin');
// Incorrect (will fail silently)
Route::middleware(['scope:admin', 'auth:api'])->get('/admin');
TokenGuard is properly configured in AuthServiceProvider:
Passport::tokensCan([...]);
Passport::hashClientSecrets();
Passport::routes(); // Only if using built-in auth routes
php artisan passport:tokens
$user->tokens()->delete();
config/auth.php):
'passport' => [
'log_events' => true,
],
| Error | Solution |
|---|---|
invalid_grant |
Check client_id, client_secret, or user credentials. |
unsupported_grant_type |
Verify the grant type (e.g., password, client_credentials). |
invalid_scope |
Ensure scopes are defined in tokensCan() and requested in the grant. |
invalid_client |
Regenerate client secrets or check password_client flag. |
invalid_request |
Validate all required fields (e.g., redirect_uri for authorization code). |
$user->load('tokens'); //
How can I help you explore Laravel packages today?