Laravel Sanctum e Passport para autenticação API

1. Introdução aos Pacotes de Autenticação API no Laravel

O ecossistema Laravel oferece duas soluções robustas para autenticação de APIs: Sanctum e Passport. Ambos resolvem o mesmo problema fundamental — proteger rotas de API — mas com abordagens e complexidades distintas.

1.1. Diferenças fundamentais entre Sanctum e Passport

O Sanctum é um pacote leve que oferece dois mecanismos de autenticação: tokens de API para aplicações mobile/single-page e autenticação baseada em sessão para SPAs no mesmo domínio. Ele não implementa o protocolo OAuth2 completo, focando em simplicidade.

O Passport é uma implementação completa do OAuth2, permitindo fluxos como Authorization Code, Client Credentials e Password Grant. Ele é indicado quando você precisa que terceiros acessem sua API de forma controlada, com scopes e refresh tokens.

1.2. Cenários de uso

  • Sanctum: Ideal para SPAs first-party, aplicações mobile próprias, APIs simples onde você controla todos os clientes.
  • Passport: Necessário quando você expõe sua API para desenvolvedores externos, precisa de fluxos OAuth2 completos, ou requer scopes granulares para diferentes aplicações.

1.3. Requisitos de sistema

Ambos requerem Laravel 8+ e PHP 8.0+. Para o Passport, é necessário ter o php-xml e php-gmp instalados.

2. Laravel Sanctum: Autenticação Simples para SPAs e APIs

2.1. Instalação e configuração inicial

composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

No arquivo config/sanctum.php, você pode configurar a expiração de tokens:

'expiration' => 60 * 24, // 24 horas

2.2. Criação de tokens de API

No modelo User, adicione o trait HasApiTokens:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

2.3. Autenticação baseada em sessão para SPAs

Para SPAs no mesmo domínio, configure o middleware web no arquivo config/cors.php:

'supports_credentials' => true,

3. Implementando Autenticação com Sanctum na Prática

3.1. Protegendo rotas

Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', function (Request $request) {
        return $request->user();
    });
});

3.2. Gerando tokens com habilidades

public function login(Request $request)
{
    $user = User::where('email', $request->email)->first();

    if (!$user || !Hash::check($request->password, $user->password)) {
        return response()->json(['message' => 'Credenciais inválidas'], 401);
    }

    $token = $user->createToken('api-token', ['read', 'write'])->plainTextToken;

    return response()->json(['token' => $token]);
}

3.3. Revogação de tokens

// Revogar token específico
$user->tokens()->where('id', $tokenId)->delete();

// Revogar todos os tokens
$user->tokens()->delete();

4. Laravel Passport: OAuth2 Completo para APIs

4.1. Instalação e configuração

composer require laravel/passport
php artisan passport:install
php artisan migrate

O comando passport:install cria as chaves de criptografia necessárias.

4.2. Fluxos OAuth2 suportados

O Passport suporta quatro fluxos principais:
- Authorization Code: Para aplicações web com servidor backend
- Client Credentials: Para comunicação máquina-a-máquina
- Password Grant: Para aplicações mobile próprias
- Implicit Grant: (obsoleto) Para SPAs legadas

4.3. Registro de clientes

use Laravel\Passport\Client;

// Criar cliente first-party
$client = Client::create([
    'user_id' => null,
    'name' => 'Mobile App',
    'secret' => Str::random(40),
    'redirect' => 'http://localhost:8000/callback',
    'personal_access_client' => false,
    'password_client' => true,
    'revoked' => false,
]);

5. Implementando Passport com Password Grant e Tokens Pessoais

5.1. Configuração do Password Grant

No AuthServiceProvider:

use Laravel\Passport\Passport;

public function boot()
{
    Passport::routes();
    Passport::tokensExpireIn(now()->addDays(15));
    Passport::refreshTokensExpireIn(now()->addDays(30));
    Passport::personalAccessTokensExpireIn(now()->addMonths(6));
}

5.2. Criação de tokens pessoais

public function login(Request $request)
{
    $http = new GuzzleHttp\Client;

    $response = $http->post(env('APP_URL') . '/oauth/token', [
        'form_params' => [
            'grant_type' => 'password',
            'client_id' => env('PASSPORT_PASSWORD_CLIENT_ID'),
            'client_secret' => env('PASSPORT_PASSWORD_CLIENT_SECRET'),
            'username' => $request->email,
            'password' => $request->password,
            'scope' => 'read write',
        ],
    ]);

    return json_decode((string) $response->getBody(), true);
}

5.3. Scopes e permissões granulares

// Definir scopes no AuthServiceProvider
Passport::tokensCan([
    'read' => 'Acesso de leitura',
    'write' => 'Acesso de escrita',
    'admin' => 'Acesso administrativo',
]);

// Verificar scope nas rotas
Route::middleware(['auth:api', 'scope:admin'])->group(function () {
    Route::get('/admin/users', [UserController::class, 'index']);
});

6. Comparação e Boas Práticas de Segurança

6.1. Performance e overhead

O Sanctum é significativamente mais leve que o Passport, pois não requer consultas adicionais ao banco para validar tokens OAuth2. Para APIs com alto throughput, Sanctum oferece melhor performance.

6.2. Proteção contra CSRF e expiração

Sanctum: Tokens expiram conforme configurado, mas não possuem refresh tokens automáticos. Para SPAs, use o middleware web e proteção CSRF.

Passport: Oferece refresh tokens que permitem renovar acesso sem reautenticação. Configure expiração adequada:

Passport::tokensExpireIn(now()->addMinutes(60));
Passport::refreshTokensExpireIn(now()->addDays(7));

6.3. Quando usar cada um

  • Use Sanctum: APIs first-party, SPAs, aplicações mobile próprias, prototipagem rápida
  • Use Passport: APIs públicas para terceiros, necessidade de scopes complexos, fluxos OAuth2 completos
  • Use ambos: Quando você precisa de tokens simples para sua aplicação e OAuth2 para parceiros

7. Exemplos Práticos de Integração

7.1. Consumindo API protegida com Sanctum via Axios

// Login
axios.post('/api/login', {
    email: 'user@example.com',
    password: 'password'
}).then(response => {
    const token = response.data.token;
    localStorage.setItem('api_token', token);

    // Configurar header padrão
    axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
});

// Requisição autenticada
axios.get('/api/user').then(response => {
    console.log(response.data);
});

7.2. Consumindo API protegida com Passport via OAuth2

// Cliente OAuth2 em PHP
$http = new GuzzleHttp\Client;

$response = $http->post('https://api.example.com/oauth/token', [
    'form_params' => [
        'grant_type' => 'authorization_code',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'redirect_uri' => 'https://yourapp.com/callback',
        'code' => $authorizationCode,
    ],
]);

$accessToken = json_decode($response->getBody())->access_token;

7.3. Testes de autenticação com PHPUnit

public function test_authenticated_user_can_access_protected_route()
{
    $user = User::factory()->create();

    // Sanctum
    Sanctum::actingAs($user, ['read']);

    // Passport
    Passport::actingAs($user, ['read']);

    $response = $this->getJson('/api/user');

    $response->assertStatus(200)
             ->assertJson(['email' => $user->email]);
}

8. Conclusão

A escolha entre Sanctum e Passport depende exclusivamente do seu caso de uso. Para a maioria das aplicações modernas, o Sanctum oferece a simplicidade necessária com segurança adequada. O Passport permanece indispensável para cenários que exigem o protocolo OAuth2 completo.

Ambos os pacotes são mantidos ativamente pela equipe do Laravel e seguem as melhores práticas de segurança. Invista tempo em entender os requisitos da sua API antes de decidir — essa escolha impactará diretamente a arquitetura e manutenção do seu sistema.

Referências