Skip to content

Usando JSON Web Tokens com Laravel para criar APIs

Usar JWT para autenticar usuários através de APIs é especialmente útil, pois não requer a implementação de sessões no servidor e o próprio JWT contém as informações correspondentes ao usuário que está fazendo a requisição, o que serve para validar sua autenticidade.

Saiba mais sobre JWT

Primeiramente, precisamos instalar o Laravel e criar um projeto. Instruções para instalar o Laravel 5.8. Depois de ter o Laravel instalado, seguimos para criar um projeto.

bash
$ laravel new webpage

Para implementar JWT no Laravel vamos utilizar o módulo chamado tymondesigns/jwt-auth com o seguinte código:

bash
$ composer require tymon/jwt-auth:dev-develop --prefer-source

Em seguida, para gerar o arquivo de configuração, executamos o seguinte código:

bash
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

Procedemos a gerar uma chave para o nosso projeto:

bash
$ php artisan jwt:secret

Para que o Laravel se conecte automaticamente ao nosso banco de dados e o utilize como método de validação de usuários, devemos criar uma tabela users com as seguintes colunas:

  • id PRIMARY KEY AUTOINCREMENT
  • name VARCHAR
  • surname VARCHAR
  • email VARCHAR
  • password VARCHAR
  • create_at TIMESTAMP
  • updated_at TIMESTAMP

O modelo User usará a tabela users por padrão. Se você quiser especificar outro nome para a tabela, você pode definir o nome a ser usado em app\User.php com o seguinte código:

php
...
protected $table = ''; // colocamos o nome da tabela
...

Os passos seguintes são a nível de código. Precisamos implementar Tymon\JWTAuth\Contracts\JWTSubject no modelo User. Neste modelo devemos implementar dois métodos: getJWTIdentifier() e getJWTCustomClaims().

O modelo User em app\User.php deve ficar semelhante a este:

php
<?php
namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Tymon\JWTAuth\Contracts\JWTSubject;  
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject {
    use Notifiable;
    
    protected $fillable = [
        'name', 'surname', 'email', 'password',  
    ];
  
    protected $hidden = [
        'password', 'remember_token',
    ];
    
    protected $casts = [
        'email_verified_at' => 'datetime',  
    ];
  
    public function getJWTIdentifier() {
        return $this->getKey();
    }
    
    public function getJWTCustomClaims() {
        return [];
    }
}

Agora para que o Laravel use JWT como método de autenticação, abrimos o arquivo config/auth.php e modificamos os seguintes dados:

php
'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],  
...
'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

Agora vamos adicionar o controller de autenticação. Vamos chamá-lo de AuthController. Podemos criá-lo manualmente ou com o seguinte comando:

bash
$ php artisan make:controller AuthController

Neste controller colocamos o seguinte código:

php
<?php
namespace App\Http\Controllers;

use App\Http\Requests\RegisterAuthRequest;
use App\User;  
use Illuminate\Http\Request;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class AuthController extends Controller {
    public $loginAfterSignUp = true;
    
    public function register(Request $request) {
        $user = new User();
        $user->name = $request->name;
        $user->surname = $request->surname;
        $user->email = $request->email;
        $user->password = bcrypt($request->password);
        $user->save();
    
        if ($this->loginAfterSignUp) {
            return $this->login($request);
        }
    
        return response()->json([
            'status' => 'ok',
            'data' => $user  
        ], 200);
    }
    
    public function login(Request $request) {
        $input = $request->only('email', 'password');
        $jwt_token = null;
        if (!$jwt_token = JWTAuth::attempt($input)) {
            return response()->json([
                'status' => 'invalid_credentials',
                'message' => 'E-mail ou senha incorretos.',
            ], 401);
        }
    
        return response()->json([
            'status' => 'ok',
            'token' => $jwt_token,
        ]);
    }
    
    public function logout(Request $request) {
        $this->validate($request, [
            'token' => 'required'
        ]);
    
        try {
            JWTAuth::invalidate($request->token);
            return response()->json([
                'status' => 'ok',
                'message' => 'Logout efetuado com sucesso.'
            ]);
        } catch (JWTException $exception) {
            return response()->json([
                'status' => 'unknown_error',
                'message' => 'O usuário não pôde ser desconectado.'  
            ], 500);
        }
    }
    
    public function getAuthUser(Request $request) {
        $this->validate($request, [
            'token' => 'required'
        ]);
    
        $user = JWTAuth::authenticate($request->token);
        return response()->json(['user' => $user]);
    }
}

Agora criamos as rotas que acessarão esses métodos em routes\api.php:

php
<?php
use Illuminate\Http\Request;

// essas rotas podem ser acessadas sem fornecer um token válido.  
Route::post('/login', 'AuthController@login');
Route::post('/register', 'AuthController@register');
// essas rotas requerem um token válido para serem acessadas.
Route::group(['middleware' => 'auth.jwt'], function () {
     Route::post('/logout', 'AuthController@logout');  
});

Adicionalmente, podemos adicionar o seguinte código no início de public\index.php para evitar erros de CORS durante nossos testes:

php
// permite requisições de qualquer origem
header('Access-Control-Allow-Origin: *');  
// permite requisições com os métodos GET, PUT, POST, DELETE e OPTIONS
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS'); 
// permite os cabeçalhos Content-Type e Authorization
header('Access-Control-Allow-Headers: Content-Type, Authorization');

Com isso concluímos a parte de configuração do Laravel para usar JSON Web Tokens. Agora vamos fazer algumas requisições de teste usando o Insomnia para verificar se nossas rotas estão funcionando.

Fazemos uma requisição para a rota /api/register e confirmamos que recebemos um status: ok e um token.

Teste de registro JWT

Isso significa que esse usuário foi registrado no banco de dados e foi gerado automaticamente um token que devemos enviar em cada requisição para rotas protegidas.

Também podemos testar /api/login.

Teste de login JWT

O token pode ser enviado em cada requisição de diferentes formas:

  • Podemos fazê-lo por url no caso de usar GET por exemplo .../api/products?token=eyJ0eXAiOiJ....
  • Como uma propriedade de um JSON enviado em um POST.
  • Ou como parte do cabeçalho Authorization: Bearer eyJ0eXAiOiJ....

No exemplo a seguir enviamos como propriedade em um JSON para desconectar o usuário usando api/logout.

Teste de logout JWT

Da mesma forma, poderíamos continuar criando mais rotas protegidas que exigiriam um token válido para serem acessadas. Como sempre, qualquer dúvida pode ser deixada nos comentários. Abraços.