Laravel Sanctum
Introdução
Laravel Sanctum fornece um sistema de autenticação leve para SPAs (aplicativos de página única), aplicativos móveis e APIs simples baseadas em tokens. O Sanctum permite que cada usuário do seu aplicativo gere vários tokens de API para sua conta. Esses tokens podem receber habilidades/escopos que especificam quais ações os tokens têm permissão para executar.
Como funciona
O Laravel Sanctum existe para resolver dois problemas separados. Vamos discutir cada um antes de nos aprofundarmos na biblioteca.
Tokens de API
Primeiro, o Sanctum é um pacote simples que você pode usar para emitir tokens de API para seus usuários sem a complicação do OAuth. Esse recurso é inspirado no GitHub e outros aplicativos que emitem "tokens de acesso pessoal". Por exemplo, imagine que as "configurações de conta" do seu aplicativo tenham uma tela onde um usuário pode gerar um token de API para sua conta. Você pode usar o Sanctum para gerar e gerenciar esses tokens. Esses tokens geralmente têm um tempo de expiração muito longo (anos), mas podem ser revogados manualmente pelo usuário a qualquer momento.
O Laravel Sanctum oferece esse recurso armazenando tokens de API do usuário em uma única tabela de banco de dados e autenticando solicitações HTTP de entrada por meio do cabeçalho Authorization
que deve conter um token de API válido.
Autenticação SPA
Segundo, o Sanctum existe para oferecer uma maneira simples de autenticar aplicativos de página única (SPAs) que precisam se comunicar com uma API com tecnologia Laravel. Esses SPAs podem existir no mesmo repositório que seu aplicativo Laravel ou podem ser um repositório totalmente separado, como um SPA criado usando o Vue CLI ou um aplicativo Next.js.
Para esse recurso, o Sanctum não usa tokens de nenhum tipo. Em vez disso, o Sanctum usa os serviços de autenticação de sessão baseados em cookies do Laravel. Normalmente, o Sanctum utiliza o guarda de autenticação web
do Laravel para fazer isso. Isso fornece os benefícios da proteção CSRF, autenticação de sessão, bem como protege contra vazamento das credenciais de autenticação via XSS.
O Sanctum só tentará autenticar usando cookies quando a solicitação de entrada se originar do seu próprio frontend SPA. Quando o Sanctum examina uma solicitação HTTP de entrada, ele primeiro verificará se há um cookie de autenticação e, se nenhum estiver presente, o Sanctum examinará o cabeçalho Authorization
para um token de API válido.
NOTA
É perfeitamente aceitável usar o Sanctum apenas para autenticação de token de API ou apenas para autenticação SPA. Só porque você usa o Sanctum não significa que você é obrigado a usar os dois recursos que ele oferece.
Instalação
Você pode instalar o Laravel Sanctum por meio do comando Artisan install:api
:
php artisan install:api
Em seguida, se você planeja utilizar o Sanctum para autenticar um SPA, consulte a seção Autenticação do SPA desta documentação.
Configuração
Substituindo modelos padrão
Embora normalmente não seja necessário, você tem a liberdade de estender o modelo PersonalAccessToken
usado internamente pelo Sanctum:
use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;
class PersonalAccessToken extends SanctumPersonalAccessToken
{
// ...
}
Então, você pode instruir o Sanctum a usar seu modelo personalizado por meio do método usePersonalAccessTokenModel
fornecido pelo Sanctum. Normalmente, você deve chamar esse método no método boot
do arquivo AppServiceProvider
do seu aplicativo:
use App\Models\Sanctum\PersonalAccessToken;
use Laravel\Sanctum\Sanctum;
/**
* Inicialize qualquer serviço de aplicativo.
*/
public function boot(): void
{
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
}
Autenticação de Token de API
NOTA
Você não deve usar tokens de API para autenticar seu próprio SPA de primeira parte. Em vez disso, use os recursos de autenticação de SPA integrados do Sanctum.
Emitindo Tokens de API
O Sanctum permite que você emita tokens de API/tokens de acesso pessoal que podem ser usados para autenticar solicitações de API para seu aplicativo. Ao fazer solicitações usando tokens de API, o token deve ser incluído no cabeçalho Authorization
como um token Bearer
.
Para começar a emitir tokens para usuários, seu modelo User deve usar o trait Laravel\Sanctum\HasApiTokens
:
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
Para emitir um token, você pode usar o método createToken
. O método createToken
retorna uma instância Laravel\Sanctum\NewAccessToken
. Os tokens de API são hash usando hash SHA-256 antes de serem armazenados em seu banco de dados, mas você pode acessar o valor de texto simples do token usando a propriedade plainTextToken
da instância NewAccessToken
. Você deve exibir este valor para o usuário imediatamente após o token ter sido criado:
use Illuminate\Http\Request;
Route::post('/tokens/create', function (Request $request) {
$token = $request->user()->createToken($request->token_name);
return ['token' => $token->plainTextToken];
});
Você pode acessar todos os tokens do usuário usando o relacionamento tokens
do Eloquent fornecido pelo trait HasApiTokens
:
foreach ($user->tokens as $token) {
// ...
}
Habilidades do token
O Sanctum permite que você atribua "habilidades" aos tokens. As habilidades têm um propósito semelhante aos "escopos" do OAuth. Você pode passar uma matriz de habilidades de string como o segundo argumento para o método createToken
:
return $user->createToken('token-name', ['server:update'])->plainTextToken;
Ao manipular uma solicitação de entrada autenticada pelo Sanctum, você pode determinar se o token tem uma determinada habilidade usando o método tokenCan
:
if ($user->tokenCan('server:update')) {
// ...
}
Middleware de Habilidade de Token
O Sanctum também inclui dois middlewares que podem ser usados para verificar se uma solicitação de entrada é autenticada com um token que recebeu uma determinada habilidade. Para começar, defina os seguintes aliases de middleware no arquivo bootstrap/app.php
do seu aplicativo:
use Laravel\Sanctum\Http\Middleware\CheckAbilities;
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'abilities' => CheckAbilities::class,
'ability' => CheckForAnyAbility::class,
]);
})
O middleware abilities
pode ser atribuído a uma rota para verificar se o token da solicitação recebida tem todas as habilidades listadas:
Route::get('/orders', function () {
// Token has both "check-status" and "place-orders" abilities...
})->middleware(['auth:sanctum', 'abilities:check-status,place-orders']);
O middleware ability
pode ser atribuído a uma rota para verificar se o token da solicitação recebida tem pelo menos uma das habilidades listadas:
Route::get('/orders', function () {
// Token has the "check-status" or "place-orders" ability...
})->middleware(['auth:sanctum', 'ability:check-status,place-orders']);
Solicitações iniciadas pela IU de primeira parte
Por conveniência, o método tokenCan
sempre retornará true
se a solicitação autenticada recebida for do seu SPA de primeira parte e você estiver usando a autenticação do SPA interna do Sanctum.
No entanto, isso não significa necessariamente que seu aplicativo precisa permitir que o usuário execute a ação. Normalmente, as políticas de autorização do seu aplicativo determinarão se o token recebeu permissão para executar as habilidades, bem como verificarão se a própria instância do usuário deve ter permissão para executar a ação.
Por exemplo, se imaginarmos um aplicativo que gerencia servidores, isso pode significar verificar se o token está autorizado a atualizar servidores e se o servidor pertence ao usuário:
return $request->user()->id === $server->user_id &&
$request->user()->tokenCan('server:update')
A princípio, permitir que o método tokenCan
seja chamado e sempre retorne true
para solicitações iniciadas pela UI de primeira parte pode parecer estranho; no entanto, é conveniente poder sempre assumir que um token de API está disponível e pode ser inspecionado por meio do método tokenCan
. Ao adotar essa abordagem, você sempre pode chamar o método tokenCan
dentro das políticas de autorização do seu aplicativo sem se preocupar se a solicitação foi acionada pela interface do usuário do seu aplicativo ou foi iniciada por um dos consumidores terceirizados da sua API.
Protegendo Rotas
Para proteger rotas de modo que todas as solicitações recebidas sejam autenticadas, você deve anexar a proteção de autenticação sanctum
às suas rotas protegidas dentro dos seus arquivos de rota routes/web.php
e routes/api.php
. Esta proteção garantirá que as solicitações recebidas sejam autenticadas como solicitações com estado, autenticadas por cookie ou contenham um cabeçalho de token de API válido se a solicitação for de terceiros.
Você pode estar se perguntando por que sugerimos que você autentique as rotas dentro do arquivo routes/web.php
do seu aplicativo usando a proteção sanctum
. Lembre-se, o Sanctum tentará primeiro autenticar as solicitações recebidas usando o cookie de autenticação de sessão típico do Laravel. Se esse cookie não estiver presente, o Sanctum tentará autenticar a solicitação usando um token no cabeçalho Authorization
da solicitação. Além disso, autenticar todas as solicitações usando Sanctum garante que sempre podemos chamar o método tokenCan
na instância do usuário atualmente autenticada:
use Illuminate\Http\Request;
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
Revogando Tokens
Você pode "revogar" tokens excluindo-os do seu banco de dados usando o relacionamento tokens
fornecido pelo trait Laravel\Sanctum\HasApiTokens
:
// Revogar todos os tokens...
$user->tokens()->delete();
// Revogar o token que foi usado para autenticar a solicitação atual...
$request->user()->currentAccessToken()->delete();
// Revogar um token específico...
$user->tokens()->where('id', $tokenId)->delete();
Expiração do Token
Por padrão, os tokens Sanctum nunca expiram e só podem ser invalidados revogando o token. No entanto, se você quiser configurar um tempo de expiração para os tokens de API do seu aplicativo, você pode fazer isso por meio da opção de configuração expiration
definida no arquivo de configuração sanctum
do seu aplicativo. Esta opção de configuração define o número de minutos até que um token emitido seja considerado expirado:
'expiration' => 525600,
Se você quiser especificar o tempo de expiração de cada token independentemente, você pode fazer isso fornecendo o tempo de expiração como o terceiro argumento para o método createToken
:
return $user->createToken(
'token-name', ['*'], now()->addWeek()
)->plainTextToken;
Se você configurou um tempo de expiração de token para seu aplicativo, você também pode desejar agendar uma tarefa para podar os tokens expirados do seu aplicativo. Felizmente, o Sanctum inclui um comando Artisan sanctum:prune-expired
que você pode usar para fazer isso. Por exemplo, você pode configurar uma tarefa agendada para excluir todos os registros de banco de dados de tokens expirados que expiraram por pelo menos 24 horas:
use Illuminate\Support\Facades\Schedule;
Schedule::command('sanctum:prune-expired --hours=24')->daily();
Autenticação SPA
O Sanctum também existe para fornecer um método simples de autenticação de aplicativos de página única (SPAs) que precisam se comunicar com uma API com tecnologia Laravel. Esses SPAs podem existir no mesmo repositório que seu aplicativo Laravel ou podem ser um repositório totalmente separado.
Para esse recurso, o Sanctum não usa tokens de nenhum tipo. Em vez disso, o Sanctum usa os serviços de autenticação de sessão baseados em cookies integrados do Laravel. Essa abordagem para autenticação fornece os benefícios da proteção CSRF, autenticação de sessão, bem como protege contra vazamento das credenciais de autenticação via XSS.
AVISO
Para autenticar, seu SPA e API devem compartilhar o mesmo domínio de nível superior. No entanto, eles podem ser colocados em subdomínios diferentes. Além disso, você deve garantir que enviou o cabeçalho Accept: application/json
e o cabeçalho Referer
ou Origin
com sua solicitação.
Configuração
Configurando seus domínios primários
Primeiro, você deve configurar de quais domínios seu SPA fará solicitações. Você pode configurar esses domínios usando a opção de configuração stateful
em seu arquivo de configuração sanctum
. Essa configuração determina quais domínios manterão a autenticação "stateful" usando cookies de sessão do Laravel ao fazer solicitações à sua API.
AVISO
Se estiver acessando seu aplicativo por meio de uma URL que inclui uma porta (127.0.0.1:8000
), você deve garantir que incluiu o número da porta com o domínio.
Sanctum Middleware
Em seguida, você deve instruir o Laravel que as solicitações recebidas do seu SPA podem ser autenticadas usando os cookies de sessão do Laravel, enquanto ainda permite que solicitações de terceiros ou aplicativos móveis sejam autenticadas usando tokens de API. Isso pode ser facilmente realizado invocando o método de middleware statefulApi
no arquivo bootstrap/app.php
do seu aplicativo:
->withMiddleware(function (Middleware $middleware) {
$middleware->statefulApi();
})
CORS e cookies
Se você estiver tendo problemas para autenticar com seu aplicativo de um SPA que executa em um subdomínio separado, provavelmente você configurou incorretamente suas configurações de CORS (Cross-Origin Resource Sharing) ou cookie de sessão.
O arquivo de configuração config/cors.php
não é publicado por padrão. Se você precisar personalizar as opções CORS do Laravel, você deve publicar o arquivo de configuração cors
completo usando o comando Artisan config:publish
:
php artisan config:publish cors
Em seguida, você deve garantir que a configuração CORS do seu aplicativo esteja retornando o cabeçalho Access-Control-Allow-Credentials
com um valor de True
. Isso pode ser feito definindo a opção supports_credentials
dentro do arquivo de configuração config/cors.php
do seu aplicativo como true
.
Além disso, você deve habilitar as opções withCredentials
e withXSRFToken
na instância global axios
do seu aplicativo. Normalmente, isso deve ser feito no seu arquivo resources/js/bootstrap.js
. Se você não estiver usando o Axios para fazer solicitações HTTP do seu frontend, você deve executar a configuração equivalente no seu próprio cliente HTTP:
axios.defaults.withCredentials = true;
axios.defaults.withXSRFToken = true;
Finalmente, você deve garantir que a configuração do domínio do cookie de sessão do seu aplicativo suporte qualquer subdomínio do seu domínio raiz. Você pode fazer isso prefixando o domínio com um .
inicial dentro do arquivo de configuração config/session.php
do seu aplicativo:
'domain' => '.domain.com',
Autenticando
Proteção CSRF
Para autenticar seu SPA, a página "login" do seu SPA deve primeiro fazer uma solicitação ao endpoint /sanctum/csrf-cookie
para inicializar a proteção CSRF para o aplicativo:
axios.get('/sanctum/csrf-cookie').then(response => {
// Login...
});
Durante essa solicitação, o Laravel definirá um cookie XSRF-TOKEN
contendo o token CSRF atual. Esse token deve então ser passado em um cabeçalho X-XSRF-TOKEN
em solicitações subsequentes, o que algumas bibliotecas de cliente HTTP como Axios e Angular HttpClient farão automaticamente para você. Se sua biblioteca JavaScript HTTP não definir o valor para você, será necessário definir manualmente o cabeçalho X-XSRF-TOKEN
para corresponder ao valor do cookie XSRF-TOKEN
definido por esta rota.
Fazendo login
Depois que a proteção CSRF for inicializada, você deve fazer uma solicitação POST
para a rota /login
do seu aplicativo Laravel. Esta rota /login
pode ser implementada manualmente ou usando um pacote de autenticação headless como Laravel Fortify.
Se a solicitação de login for bem-sucedida, você será autenticado e as solicitações subsequentes para as rotas do seu aplicativo serão autenticadas automaticamente por meio do cookie de sessão que o aplicativo Laravel emitiu para seu cliente. Além disso, como seu aplicativo já fez uma solicitação para a rota /sanctum/csrf-cookie
, as solicitações subsequentes devem receber proteção CSRF automaticamente, desde que seu cliente HTTP JavaScript envie o valor do cookie XSRF-TOKEN
no cabeçalho X-XSRF-TOKEN
.
É claro que, se a sessão do seu usuário expirar devido à falta de atividade, as solicitações subsequentes ao aplicativo Laravel podem receber uma resposta de erro HTTP 401 ou 419. Nesse caso, você deve redirecionar o usuário para a página de login do seu SPA.
AVISO
Você é livre para escrever seu próprio endpoint /login
; no entanto, você deve garantir que ele autentique o usuário usando os serviços de autenticação padrão, baseados em sessão que o Laravel fornece. Normalmente, isso significa usar o guarda de autenticação web
.
Protegendo rotas
Para proteger rotas para que todas as solicitações recebidas sejam autenticadas, você deve anexar a proteção de autenticação sanctum
às suas rotas de API dentro do seu arquivo routes/api.php
. Esta proteção garantirá que as solicitações recebidas sejam autenticadas como solicitações autenticadas com estado do seu SPA ou contenham um cabeçalho de token de API válido se a solicitação for de terceiros:
use Illuminate\Http\Request;
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
Autorizando canais de transmissão privados
Se o seu SPA precisar autenticar com canais de transmissão privados/presenciais, você deve remover a entrada channels
do método withRouting
contido no arquivo bootstrap/app.php
do seu aplicativo. Em vez disso, você deve invocar o método withBroadcasting
para que possa especificar o middleware correto para as rotas de transmissão do seu aplicativo:
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
// ...
)
->withBroadcasting(
__DIR__.'/../routes/channels.php',
['prefix' => 'api', 'middleware' => ['api', 'auth:sanctum']],
)
Em seguida, para que as solicitações de autorização do Pusher sejam bem-sucedidas, você precisará fornecer um authorizer
personalizado do Pusher ao inicializar o Laravel Echo. Isso permite que seu aplicativo configure o Pusher para usar a instância axios
que está configurada corretamente para solicitações entre domínios:
window.Echo = new Echo({
broadcaster: "pusher",
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
encrypted: true,
key: import.meta.env.VITE_PUSHER_APP_KEY,
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
axios.post('/api/broadcasting/auth', {
socket_id: socketId,
channel_name: channel.name
})
.then(response => {
callback(false, response.data);
})
.catch(error => {
callback(true, error);
});
}
};
},
})
Autenticação de aplicativo móvel
Você também pode usar tokens Sanctum para autenticar as solicitações do seu aplicativo móvel para sua API. O processo de autenticação de solicitações de aplicativo móvel é semelhante à autenticação de solicitações de API de terceiros; no entanto, há pequenas diferenças em como você emitirá os tokens de API.
Emissão de tokens de API
Para começar, crie uma rota que aceite o e-mail/nome de usuário, senha e nome do dispositivo do usuário e troque essas credenciais por um novo token Sanctum. O "nome do dispositivo" fornecido a este ponto de extremidade é para fins informativos e pode ser qualquer valor que você desejar. Em geral, o valor do nome do dispositivo deve ser um nome que o usuário reconheceria, como "iPhone 12 do Nuno".
Normalmente, você fará uma solicitação ao ponto de extremidade do token na tela de "login" do seu aplicativo móvel. O endpoint retornará o token de API em texto simples que pode então ser armazenado no dispositivo móvel e usado para fazer solicitações de API adicionais:
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
Route::post('/sanctum/token', function (Request $request) {
$request->validate([
'email' => 'required|email',
'password' => 'required',
'device_name' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
return $user->createToken($request->device_name)->plainTextToken;
});
Quando o aplicativo móvel usa o token para fazer uma solicitação de API para seu aplicativo, ele deve passar o token no cabeçalho Authorization
como um token Bearer
.
NOTA
Ao emitir tokens para um aplicativo móvel, você também é livre para especificar habilidades do token.
Protegendo rotas
Conforme documentado anteriormente, você pode proteger rotas para que todas as solicitações recebidas sejam autenticadas anexando a proteção de autenticação sanctum
às rotas:
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
Revogando tokens
Para permitir que os usuários revoguem tokens de API emitidos para dispositivos móveis, você pode listá-los por nome, junto com um botão "Revogar", dentro de uma parte de "configurações de conta" da IU do seu aplicativo da web. Quando o usuário clica no botão "Revogar", você pode excluir o token do banco de dados. Lembre-se, você pode acessar os tokens de API de um usuário por meio do relacionamento tokens
fornecido pelo trait Laravel\Sanctum\HasApiTokens
:
// Revogar todos os tokens...
$user->tokens()->delete();
// Revogar um token específico...
$user->tokens()->where('id', $tokenId)->delete();
Testando
Durante o teste, o método Sanctum::actingAs
pode ser usado para autenticar um usuário e especificar quais habilidades devem ser concedidas ao seu token:
use App\Models\User;
use Laravel\Sanctum\Sanctum;
test('task list can be retrieved', function () {
Sanctum::actingAs(
User::factory()->create(),
['view-tasks']
);
$response = $this->get('/api/task');
$response->assertOk();
});
use App\Models\User;
use Laravel\Sanctum\Sanctum;
public function test_task_list_can_be_retrieved(): void
{
Sanctum::actingAs(
User::factory()->create(),
['view-tasks']
);
$response = $this->get('/api/task');
$response->assertOk();
}
Se você quiser conceder todas as habilidades ao token, deve incluir *
na lista de habilidades fornecida ao método actingAs
:
Sanctum::actingAs(
User::factory()->create(),
['*']
);