Fernanda, boa noite.
Em geral eu utilizo somente o 'auth:api' nas rotas que serão manipuladas pelo usuário com um Access Token gerado por um client do tipo password.
Como não sei onde pode estar diferente do que faço, vou descrever como utilizo.
1. Instalar o Passport
Estes passos provavelmente você deve ter executado, pois são exatamente os que estão na documentação, se preferir pode pular para a segunda parte.
composer require laravel/passport
php artisan migrate
php artisan passport:install
Neste comando o Laravel já cria dois clientes um Personal e um Password, vamos utilizar somente o Password que numa instalação como a acima tem o id 2.
Além destes comandos é preciso adicionar as rotas do Passport no método boot de algum Service Provider, Eu normalmente adiciono ao AuthServiceProvider:
public function boot()
{
Passport::routes();
$this->registerPolicies(); // já tem no AuthServiceProvider
}
Também adicione a trait Laravel\Passport\HasApiTokens ao modelo que vai ser autenticado, normalmente o User.
E por último altere o arquivo config/auth.php para os seguintes valores:
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
// outras guards
],
Este passo é muito importante diz ao Laravel para uitilizar o Laravel Passport para a autenticação das rotas que tiverem com o Middleware auth:api ao invés do TokenGuard que vem por padrão junto com o Laravel.
2. Utilização
Agora tem dois cenários:
A. O usuário está deslogado e precisa obter um Access Token (Autenticação)
B. O usuário tem um Access Token e quer acessar rotas protegidas (Autorização)
Vamos ver os dois cenários:
2-A. Autenticação
Neste caso, para um usuário que existe na base, fazemos uma requisição POST para o endpoint /oauth/token que já é instalado pelo Laravel Passport:
{
"client_id": 2,
"client_secret": "secret",
"grant_type": "password",
"username": "usu...@example.com",
"password": "123456",
"scopes": "[*]"
}
Note que usamos o client_id e o client_secret do cliente do tipo password, neste exemplo usei o client_id = 2 que numa instalação padrão é um cliente do tipo Password, mas você pode utilizar quaisquer clientes do tupo Password.
A informaçào sobre os escopos autorizados fica armazenada no Access Token, que é um JWT (uma representação encriptada de alguns dados do usuário e dos escopos autorizados). Pode estar aí o problema na sua implementação.
Esta requisição vai retornar um payload parecido com o abaixo:
{
"token_type": "Bearer",
"expires_in": 1296000,
"access_token": "token123",
"refresh_token": "token456"
}
O que você precisa salvar é o Access Token, para utilizar nas rotas com autenticação. O Refresh Token é útil quando queremos renovar um Access Token expirado, mas exige um outro fluxo e por simplicidade não vou abordar tal fluxo.
2-B. Autorização
Basta utilizarmos o Access Token obtido na autenticação, para acessar rotas protegidas com pelo Guard de api.
Por exemplo, imagina que tenhamos a seguinte rota:
Route::get( 'api/user', function ( \Illuminate\Http\Request $request ) {
return $request->user();
} )->middleware( 'auth:api' );
Bastaria fazermos a seguinte requisição:
Utilizanto o mesmo Access Token obtido na autenticação. Note que não usamos aspas e o prefixo Bearer é separado por um espaço apenas do token.
Note que não foi necessário nesta rota enviarmos nem o client_id nem o client_secret.
Daí fica a pergunta: E para que serve o grant do tipo client?
De acordo com a documentação o grant do tipo client é adequado para comunicações máquina-máquina.
Por exemplo: imagine que, por requisito funcional, seu sistema cliente (o feito em Cobol) precise ter uma rotina agendada para enviar uma notificação a cada usuário pela manhã com as tarefas pendentes. Eu normalmente implementaria do lado do servidor, mas vamos imaginar isto como exemplo.
Como os dados estão na aplicação servidor (a feita em Laravel) esta rotina do sistema cliente vai precisar interagir de alguma forma com a aplicação servidor para manipular estes dados e provavelmente acessar rotas onde você vai querer ter algum nível de autenticação. Mas nesta comunicação o sistema cliente não vai estar executando com nenhum usuário logado, ele vai estar executando sozinho. Como garantir esta autenticação?
Daí que entra o grant do tipo client.
Primeiro você cria um client do tipo client com o comando que você citou no seu e-mail:
php artisan passport:client --client
Este comando vai fornecer um client_id e um client_secret deste tipo que tem este grant autorizado.
De posse destes dados, você faz uma requisição para o /oauth/token para obter um Access Token para ser utilizado na comunicação máquina-máquina:
{
"client_id": 3,
"client_secret": "secret",
"grant_type": "client_credentials",
"scopes": "[*]"
}
Assim como no exemplo de autenticação em 2-A, esta rota vai te retornar um Access Token (mas não vai retornar um Refresh Token).
Com este Access Token, você pode acessar rotas que estão protegidas pelo middleware Laravel\Passport\Http\Middleware\CheckClientCredentials.
Por exemplo, assuma que você tem a seguinte rota:
Route::get( '/api/send-notifications', function () {
return 'sent!';
} )->middleware( \Laravel\Passport\Http\Middleware\CheckClientCredentials::class );
Esta rota não precisa de um usuário especifico, mas requer autenticação. Com o Access Token obtido no grant anterior podemos acessá-la mesmo não representando um usuário específico, por exemplo:
GET http://example.com/api/send-notifications
Content-Type: application/json
Accept: application/json, text/plain, */*
Authorization: Bearer client-token123
Num projeto que estou trabalhando eu utilizo este tipo de rota para realizar o registro de novos usuários, já que um dos requisitos é usuário poder se registrar pela aplicação cliente.
Nesta rota eu não tenho como ter um Access Token de usuário, pois o usuário ainda não existe. E também como a API não é pública eu preciso de alguma forma de autenticação, então utilizo esta estrategia.
Respondendo a sua última pergunta:
1. Utilize o auth:api quando você precisa autenticar um usuário.
2. Utilize o middleware CheckClientCredentials quando você precisa autenticar uma máquina.
Você deve estar se perguntando: Mas eu utilizei somente o middleware CheckClientCredentials com um Access Token de usuário e funcionou, e quando uso o auth:api não funciona (que foi o que você descreveu).
Realmente o middleware CheckClientCredentials funciona com um Access Token fornecido em nome de um usuário, mas o contrário não ocorre, ou seja numa rota com o middleware auth:api um Access Token de máquina não vai ser autorizado.
Além disto a implementação interna muda um pouco, quando você utilizar o middelware auth:api o usuário é adicionado ao Request durante a autenticação, assim quando você precisar acessar o usuário via $request->user() o usuário
já existe no request e o Laravel não precisa ir ao banco de dados para recuperar os dados de usuário.
Quando você utiliza o middleware CheckClientCredentials com um Access Token de usuário e acessa $request->user(), o Laravel vai revalidar o token acessando as tabelas oauth novamente. Ou seja, acessa as tabelas de oautth 2 vezes.
Num teste que fiz ativando o query logger, usando um Access Token de usuário numa rota com o middleware auth:api o Laravel executa 4 consultas ao banco. Já utilizando o CheckClientCredentials o Laravel executa 5 consultas ao banco.
A consulta a mais é a revalidação do token do usuário. Utilizando os dois middlewares, como você tentou antes, também executa 5 requisições.
O fato do middleware auth:api não ter funcionado na sua implementação pode ter sido algum detalhe que você deixou passar, não vi a sua implementação, mas provavelmente você não alterou o aquivo config/auth.php para que o guard api utilize o Passport.
Espero que ajude,
- Rodrigo
Referências: