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.
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.
$ laravel new webpage
Para implementar JWT no Laravel vamos utilizar o módulo chamado tymondesigns/jwt-auth com o seguinte código:
$ composer require tymon/jwt-auth:dev-develop --prefer-source
Em seguida, para gerar o arquivo de configuração, executamos o seguinte código:
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
Procedemos a gerar uma chave para o nosso projeto:
$ 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 AUTOINCREMENTname
VARCHARsurname
VARCHARemail
VARCHARpassword
VARCHARcreate_at
TIMESTAMPupdated_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:
...
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
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:
'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:
$ php artisan make:controller AuthController
Neste controller colocamos o seguinte código:
<?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
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:
// 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
.
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
.
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
.
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.