Learn how to manage your API authentication and authorization with JWT in this article and how it works under the hood. We will use jwt-auth package to get this done.
How does JWT actually work?
JWT does not store the access token. What it actually does is the access token actually contains the information called payload. JWT uses a special algorithm to encrypt the payload and sign a digital signature with a secret key and a payload into a signature combined together into an access token. When an API request is authorized by the access token it will then decrypt the access token payload giving back the information that usually contains the user id and verifying the validity of the token with a digital signature.
Install jwt-auth dependency
The command here will install the jwt-auth package into your Laravel application.
composer require tymon/jwt-auth
Generate the JWT config file
This will generate a config/jwt.php that stores all the configurations of JWT, especially the JWT secret key.
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
Generate JWT secret key
Now generate the JWT secret key by the below command. The JWT will use this key to sign the payload in order to generate the access token for your users.
php artisan jwt:secret
Integrate JWT into the user model
To integrate this JWT package into your user model you need to implement JWTSubject which required 2 methods getJWTIdentifier and getJWTCustomClaims to be implemented.
- getJWTIdentifier is what gives the jwt-auth the primary key value of the user model, which JWT used as a payload to encrypt and give back the access token when login.
- getJWTCustomClaims is the same as the getJWTIdentifier but this one you can return any custom key value array which JWT will use as a payload with the value given by the getJWTIdentifier above to encrypt into an access token.
For example, if we return this sample associative array in the user model.
public function getJWTCustomClaims()
{
return ['customKey' => 'customValue'];
}
We can get the value back when the API request with the access token that was created with this custom claim.
auth()->payload()->get('customKey');
In ./app/Models/User.php
<?php
namespace App\Models;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
class User extends Authenticatable implements JWTSubject
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'email_verified_at'
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* @return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}
public function setPasswordAttribute($value)
{
$this->attributes['password'] = Hash::make($value);
}
}
Configure auth guard
Config API auth to use JWT and users as a provider. Then set the API guard as default.
In ./config/auth.php
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
If you don’t set the “api” guard as default you need to call the auth() with auth(‘api’) in the AuthController to let auth know to use “api” guard.
AuthController implementation
The __construct middleware will protect the controller with the constructed middleware with specific except login and register as sample below.
In ./app/Http/Controllers/AuthController.php
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
class AuthController extends Controller
{
/**
* Create a new AuthController instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login', 'register']]);
}
/**
* Get a JWT via given credentials.
*
* @return \Illuminate\Http\JsonResponse
*/
public function login()
{
$credentials = request(['email', 'password']);
if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
/**
* Get the authenticated User.
*
* @return \Illuminate\Http\JsonResponse
*/
public function me()
{
return response()->json(auth()->user());
}
/**
* Log the user out (Invalidate the token).
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth()->logout();
return response()->json(['message' => 'Successfully logged out']);
}
public function register(Request $request)
{
User::create($request->only('name', 'email', 'password'));
return response()->json(['message' => 'Successfully register user']);
}
/**
* Refresh a token.
*
* @return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}
/**
* Get the token array structure.
*
* @param string $token
*
* @return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
]);
}
}
API Route
In ./routes/api.php
Route::group([
'prefix' => 'auth'
], function () {
Route::post('login', [AuthController::class, 'login']);
Route::post('logout', [AuthController::class, 'logout']);
Route::post('refresh', [AuthController::class, 'refresh']);
Route::post('register', [AuthController::class, 'register']);
Route::post('me', [AuthController::class, 'me']);
});
Conclusion
After going through all of this you get the Laravel API authorization with JWT and a clear understanding of how JWT works. These are the steps that we going you through:
- Install jwt-auth dependency
- Generate the JWT config file
- Generate JWT secret key
- Integrate JWT into the user model
- Configure auth guard
- AuthController implementation
- API Route