You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Passport实现SPA客户端支持客户端凭证与个人访问令牌

Hey there! Let's break down how to implement your exact requirements with Laravel Passport and your SPA. You've got two distinct API access scenarios, so we'll leverage two specific Passport OAuth2 grant types to cover both cleanly:

Solution: Laravel Passport for Dual API Access Control

First, let's map your needs to the right Passport patterns:

  • Endpoints open only to valid clients (no user login required): Use the Client Credentials Grant (this authenticates the SPA itself, not a user)
  • Endpoints requiring user login: Use the Authorization Code Grant with PKCE (the secure standard for SPAs, since we can't safely store a client secret in browser-based apps)

Step 1: Base Passport Setup

First, get Passport up and running in your Laravel app:

  1. Install Passport and run migrations:
composer require laravel/passport
php artisan migrate
php artisan passport:install
  1. Add the HasApiTokens trait to your User model to enable token-based authentication for users:
// app/Models/User.php
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}
  1. Register Passport routes in your AuthServiceProvider:
// app/Providers/AuthServiceProvider.php
use Laravel\Passport\Passport;

public function boot()
{
    $this->registerPolicies();

    Passport::routes();
    // Optional: Set token expiration times (adjust as needed)
    Passport::tokensExpireIn(now()->addDays(15));
    Passport::refreshTokensExpireIn(now()->addDays(30));
}
  1. Update your config/auth.php to use Passport for the API guard:
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

Step 2: Configure Client-Only API Endpoints

These endpoints don't need a logged-in user—they just need to verify that the request is coming from your trusted SPA.

  1. Create a dedicated client for this grant type:
php artisan passport:client --client

Save the generated client_id and client_secret in your SPA's environment variables (e.g., .env for Vue/React/Vite apps) — never hardcode these values!

  1. Define protected routes with the client middleware:
    This middleware validates the client's access token without checking for a user:
// routes/api.php
Route::middleware('client')->group(function () {
    // Add your client-only endpoints here
    Route::get('/app/config', 'Api\AppConfigController@index');
    Route::post('/contact/submit', 'Api\ContactController@store');
});
  1. Fetch a client token in your SPA:
    Send a POST request to Laravel's OAuth token endpoint to get a valid client token, then store it (e.g., in localStorage):
// Example in a Vue/React component
async function getClientToken() {
    const response = await fetch('/oauth/token', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            grant_type: 'client_credentials',
            client_id: import.meta.env.VITE_CLIENT_ID,
            client_secret: import.meta.env.VITE_CLIENT_SECRET,
            scope: '', // Leave empty if no scopes are needed
        }),
    });

    const data = await response.json();
    localStorage.setItem('client_token', data.access_token);
}

// Use the token for client-only API calls
async function fetchAppConfig() {
    const token = localStorage.getItem('client_token');
    const response = await fetch('/api/app/config', {
        headers: {
            'Authorization': `Bearer ${token}`,
        },
    });
    return response.json();
}

Step 3: Configure User-Authenticated API Endpoints

These endpoints require a logged-in user, so we'll use the PKCE-enhanced Authorization Code Grant (the safest option for SPAs).

  1. Create an SPA-specific client (no secret required):
php artisan passport:client --spa

Save the generated client_id in your SPA's environment variables.

  1. Define user-protected routes with the auth:api middleware:
    This middleware validates that the request includes a valid user access token:
// routes/api.php
Route::middleware('auth:api')->group(function () {
    // Add your user-specific endpoints here
    Route::get('/user/profile', 'Api\UserProfileController@show');
    Route::put('/user/profile', 'Api\UserProfileController@update');
    Route::get('/user/orders', 'Api\UserOrderController@index');
});
  1. Implement user login & token handling in your SPA:
    Use Laravel's official @laravel/passport npm package to simplify the OAuth flow:
npm install @laravel/passport

Then set up the auth provider and handle the login/callback flow:

// Example in a Vue/React app
import { createAuthProvider } from '@laravel/passport';

const authProvider = createAuthProvider({
    clientId: import.meta.env.VITE_SPA_CLIENT_ID,
    authorizeEndpoint: '/oauth/authorize',
    tokenEndpoint: '/oauth/token',
    redirectUri: `${window.location.origin}/callback`, // Your SPA's callback route
    scopes: [], // Add scopes if you need granular permissions
});

// Trigger the login flow (redirects to Laravel's login page)
async function login() {
    await authProvider.login();
}

// Handle the OAuth callback to get the user token
async function handleCallback() {
    const { token } = await authProvider.handleCallback();
    localStorage.setItem('user_token', token.access_token);
    // Redirect back to your app's homepage
    window.location.href = '/';
}

// Use the user token for authenticated API calls
async function fetchUserProfile() {
    const token = localStorage.getItem('user_token');
    const response = await fetch('/api/user/profile', {
        headers: {
            'Authorization': `Bearer ${token}`,
        },
    });
    return response.json();
}
  1. Update CORS settings:
    Make sure your config/cors.php allows your SPA's domain to access the OAuth and API routes:
'paths' => ['api/*', 'oauth/*', 'login', 'logout'],
'allowed_origins' => [env('SPA_URL', 'http://localhost:3000')],
'allowed_methods' => ['*'],
'allowed_headers' => ['*'],
'supports_credentials' => true,

Bonus Tips

  • Token Refresh: Both client and user tokens can be refreshed using the refresh_token returned in the token response—this avoids forcing users to log in repeatedly.
  • Scopes: For granular access control, define scopes in Passport and enforce them on routes with the scope middleware (e.g., Route::middleware(['auth:api', 'scope:manage-orders'])).
  • Security: For the client credentials grant, keep your client_secret safe—use environment variables and avoid committing them to version control.

内容的提问来源于stack exchange,提问作者jmardz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:09:57