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

纯前端Web应用适配何种OAuth授权类型?Laravel Passport开发咨询

Hey there! Let's break this down step by step since I've been in your exact situation building a pure frontend app with Laravel Passport. Here's what you need to know:

Best OAuth 2.0 Flow for Your Pure Frontend + Laravel Passport Setup

1. Authorization Type: Go with Authorization Code Flow + PKCE

For a pure frontend (no backend server to keep secrets safe), Authorization Code Flow with PKCE (Proof Key for Code Exchange) is the only secure choice. Here's why:

  • The old Implicit Flow sends tokens directly to the frontend via URL fragments, which is risky (tokens can leak in browser history or logs).
  • PKCE adds a layer of security by generating a unique "code verifier" and "code challenge" pair. The frontend sends the challenge to your Laravel server first, then uses the verifier to exchange the authorization code for tokens—so even if an attacker intercepts the code, they can't get tokens without the verifier.
  • Laravel Passport fully supports PKCE, so it's a seamless fit.

2. Access Token Storage: Cookies (With Secure Flags) > localStorage

This is a hot debate, but here's the practical breakdown for your use case:

  • localStorage: Easy to use, but vulnerable to XSS attacks—if an attacker injects malicious JS into your frontend, they can steal the token directly. Avoid this if possible.
  • HttpOnly Cookies: Far more secure. Configure your cookies with these flags:
    • HttpOnly: Prevents JS from accessing the token (blocks XSS theft).
    • Secure: Only sends the cookie over HTTPS (never plain HTTP).
    • SameSite=None: Required for cross-domain requests (since your frontend and API are on different servers). Pair this with Secure—browsers enforce this.
    • Domain: Set to your API's domain to restrict where the cookie is sent.
  • Note: If you use cookies, you'll need to enable supports_credentials in your Laravel CORS config so the browser sends cookies with cross-domain API requests.

3. Step-by-Step Development Guide

Laravel Passport Setup

  1. Install Passport and run migrations:
    composer require laravel/passport
    php artisan migrate
    php artisan passport:install
    
  2. Create a public client (since your frontend can't safely store a secret):
    php artisan passport:client --public
    
    Save the client_id—you'll need this in your frontend.
  3. Update your CORS config (config/cors.php):
    return [
        'paths' => ['api/*', 'oauth/*'],
        'allowed_methods' => ['*'],
        'allowed_origins' => ['https://your-frontend-domain.com'], // Replace with your frontend URL
        'allowed_origins_patterns' => [],
        'allowed_headers' => ['*'],
        'exposed_headers' => [],
        'max_age' => 0,
        'supports_credentials' => true, // Critical for cookie-based auth
    ];
    
  4. Add the HasApiTokens trait to your User model:
    use Laravel\Passport\HasApiTokens;
    
    class User extends Authenticatable
    {
        use HasApiTokens, HasFactory, Notifiable;
    }
    
  5. Update your auth guard config (config/auth.php):
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'passport',
            'provider' => 'users',
        ],
    ];
    

Frontend Implementation

  1. Generate a PKCE code verifier and challenge:
    // Generate a random code verifier
    function generateCodeVerifier() {
        const array = new Uint32Array(56 / 2);
        window.crypto.getRandomValues(array);
        return Array.from(array, dec => ('0' + dec.toString(16)).substr(-2)).join('');
    }
    
    // Hash the verifier to create the challenge
    async function generateCodeChallenge(verifier) {
        const encoder = new TextEncoder();
        const data = encoder.encode(verifier);
        const digest = await window.crypto.subtle.digest('SHA-256', data);
        return btoa(String.fromCharCode(...new Uint8Array(digest)))
            .replace(/\+/g, '-')
            .replace(/\//g, '_')
            .replace(/=+$/, '');
    }
    
  2. Redirect users to Laravel's authorization page:
    async function redirectToAuth() {
        const verifier = generateCodeVerifier();
        localStorage.setItem('code_verifier', verifier); // Store temporarily for token exchange
        const challenge = await generateCodeChallenge(verifier);
    
        const authUrl = new URL('https://your-api-domain.com/oauth/authorize');
        authUrl.searchParams.set('client_id', 'YOUR_CLIENT_ID'); // From step 2 above
        authUrl.searchParams.set('redirect_uri', 'https://your-frontend-domain.com/callback'); // Your frontend callback page
        authUrl.searchParams.set('response_type', 'code');
        authUrl.searchParams.set('scope', ''); // Add scopes if you need them
        authUrl.searchParams.set('code_challenge', challenge);
        authUrl.searchParams.set('code_challenge_method', 'S256');
    
        window.location.href = authUrl.toString();
    }
    
  3. Handle the callback and exchange the code for tokens:
    async function handleAuthCallback() {
        const urlParams = new URLSearchParams(window.location.search);
        const code = urlParams.get('code');
        const verifier = localStorage.getItem('code_verifier');
        localStorage.removeItem('code_verifier');
    
        const response = await fetch('https://your-api-domain.com/oauth/token', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                client_id: 'YOUR_CLIENT_ID',
                grant_type: 'authorization_code',
                code: code,
                redirect_uri: 'https://your-frontend-domain.com/callback',
                code_verifier: verifier,
            }),
        });
    
        const data = await response.json();
        // If using cookies, Laravel will set them automatically. If not, store tokens securely (preferably in HttpOnly cookies)
        // For API requests, include the token in the Authorization header: `Bearer ${data.access_token}`
    }
    
  4. Make authenticated API requests:
    async function fetchProtectedData() {
        const response = await fetch('https://your-api-domain.com/api/protected', {
            credentials: 'include', // Critical if using cookie-based auth
            headers: {
                // If not using cookies, add this line:
                // 'Authorization': `Bearer ${accessToken}`
            },
        });
        const data = await response.json();
        // Handle the data
    }
    

Key Security Notes

  • Always use HTTPS for both your frontend and API—cookies with Secure flag won't work over HTTP, and sensitive data will be exposed.
  • Implement XSS protections in your frontend: sanitize user input, use Content Security Policy (CSP) headers, and avoid unsafe eval() calls.
  • Set short expiration times for access tokens, and use refresh tokens to get new ones without forcing users to re-login.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:40:30