In this post we will see how you can implement token based authentication in lumen or laravel. We will use JWT token in our implementation.
As, In any API, session is not maintained as it works on stateless architecture, we can’t use a general approach of authenticating and storing sessions in server or in cookie. So, we will use a token to maintain and identify sessions on subsequent requests.
We will use “tymondesigns/jwt-auth” in our project for this implementation, You can check that out in the github page.
Installing package
First, let’s install this package using composer
composer require tymon/jwt-auth
Registering Provider
This is only needed if you are working with lumen or Laravel 5.4 or below.
In lumen
Edit config/app.php file with following
Uncomment this like “$app->register(App\Providers\AuthServiceProvider::class);”
And just after that add “$app->register(Tymon\JWTAuth\Providers\LumenServiceProvider::class);”
Also, in the same file under routeMiddleware, uncomment ‘auth’ middleware. (Don’t forget to uncomment whole routeMiddleware if not already done)
$app->routeMiddleware([ 'auth' => App\Http\Middleware\Authenticate::class, ]);
Also, we are going to use eloquent, so if you have not already uncommented it, then uncomment $app->withEloquent();
In laravel 5.4 or below
Edit config/app.php file with following
Add this line in providers “Tymon\JWTAuth\Providers\LaravelServiceProvider::class”
Generating JWT Key
We need to generate a key using which our token will be signed.
You can use following command to generate jwt key
php artisan jwt:secret
Note: In lumen this command will only work if you have registered a provider correctly as shown above.
If by any chance above command does not work then you can manually add key in .env file like shown below
JWT_SECRET=ANY_RANDOM_STRING
You can use any string, make sure that it’s length is 32 characters or more in length, this library was using 32 characters for key before and now it is using 64 characters, making it a little complex is a good choice by including some special characters as well. For this you can use any online random string generator service. I generally use https://www.lastpass.com/password-generator to generate any key or passwords.
Configuring JWT
All configuration related to JWT will go in config/jwt.php file, in laravel you can use the following command to publish config file.
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
And in lumen, As it does not have config files, All configuration will be done in .env file.
Generally used configurations
- JWT_ALGO
– This is the algorithm that will be used for signing token, default is HS256 - JWT_SECRET
– This key will be used to sign token, used only when signing algorithm is Symmetric (HS256, HS384 & HS512) - JWT_PUBLIC_KEY
– This key will be used to sign token, used only when signing algorithm is Asymmetric (RS256, RS384 & RS512 / ES256, ES384 & ES512) - JWT_PRIVATE_KEY
– This key will be used to sign token, used only when signing algorithm is Asymmetric (RS256, RS384 & RS512 / ES256, ES384 & ES512) - JWT_PASSPHRASE
– This password is for a private key file, If your private key file does not have a password, then leave it as null.
Note, If you are using Asymmetric algorithm then you have to set both JWT_PUBLIC_KEY & JWT_PRIVATE_KEY option and JWT_PASSPHRASE as well if your private key is password protected. By default algo is HS256 (Symmetric) and so this three option is not required
- JWT_TTL
– Token validity in minutes, default is 60 minute. So any token will only be valid for 60 minute. - JWT_REFRESH_TTL
– Time under which the token can be refreshed, after this time token can’t be refreshed. Default is 20160 minute (2 weeks) - JWT_BLACKLIST_ENABLED
– If you want to invalidate an active token then enable it, by default it’s set to true. If you set it to false then Auth::invalidate(); will not blacklist token.
Using JWT
Now all basic requirements are clear, Let’s see how to use it.
First we need to implement JWTSubject in the model that will be used for authentication, in our case it will be User.
This interface require us to implement following methods, so add it in our model
/** * Get the identifier that will be stored in the subject claim of the JWT. * * @return mixed */ public function getJWTIdentifier() { // Return your unique user identifier like email or username, change according to user column name return $this->attributes['email']; } /** * Return a key value array, containing any custom claims to be added to the JWT. * * @return array */ public function getJWTCustomClaims() { // Key value array of any custom claim that you might want to add it in token return []; }
Please note that the token body is not protected and so never add any sensitive data in custom claims.
So our User model becomes like this
Lumen
<?php namespace App; use Illuminate\Auth\Authenticatable; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Database\Eloquent\Model; use Laravel\Lumen\Auth\Authorizable; use Tymon\JWTAuth\Contracts\JWTSubject; class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject { use Authenticatable, Authorizable; protected $primaryKey = 'email'; // $incrementing should be set to false if your primary key is not set to auto incrementing public $incrementing = false; protected $fillable = [ 'name', 'email', ]; protected $hidden = [ 'password', ]; public function getJWTIdentifier() { return $this->attributes['email']; } public function getJWTCustomClaims() { return []; } }
Laravel
<?php namespace App; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Tymon\JWTAuth\Contracts\JWTSubject; class User extends Authenticatable implements JWTSubject { use Notifiable; protected $primaryKey = 'email'; // $incrementing should be set to false if your primary key is not set to auto incrementing public $incrementing = false; protected $fillable = [ 'name', 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; protected $casts = [ 'email_verified_at' => 'datetime', ]; public function getUser($username) { return $this->where('email', $username)->first(); } public function getJWTIdentifier() { return $this->attributes['email']; } public function getJWTCustomClaims() { return []; } }
Note, Specifying primary key is must if your table primary key is other than id and our user identification column should be primary key in table
Now, Change our config/auth.php file to set jwt as auth driver.
<?php return [ 'defaults' => [ 'guard' => 'api', 'passwords' => 'users', ], 'guards' => [ 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ], 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\User::class, ], ], 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], ], 'password_timeout' => 10800, ];
Note that here i have set api in defaults config and so our password based login will not work in laravel, so if you want both type of authentication then keep defaults as it is and just configure guards.api section to use jwt and then in your controller, you can use Auth::guard(‘api’)->anyMethod() to use api guard, For example
// To generate a token based on provided credentials,
you can use Auth::guard(‘api’)->attempt($credentials); // this will give a token if credentials is correct.
// Get current authenticated user
Auth::guard(‘api’)->user();
In lumen, Config file does not exist by default, so create config/auth.php file and copy the code provided above and make changes as per your need.
Now, Implementation is ready, to authenticate users and generate token, you can use the attempt() method available in jwt auth guard.
public function login(Request $request) { // Key should match column name in database $credentials = [ 'email' => $request->get('email'), 'password' => $request->get('password'), ]; $authRes = Auth::attempt($credentials); if ($authRes) { // We can get current authenticated user like this $user = Auth::user(); // Or we can simply get username (User identifier) like this $username = Auth::id(); echo $authRes; } else { echo 'Well, There is an issue with provided credentials :('; } }
Auth::attempt($credentials); will return a token if credentials is correct or else will return false.
Now, We have our auth token, as we have seen in configuration, this token is valid for 60 minutes, so we have to refresh this token before that or after 60 minute user has to login again. This is a Bearer token so while sending this token in the header you have to append Bearer before the token and the header name will be Authorization. So you will have to add a header like this in your all protected request.
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9sb2NhbGhvc3Q6ODAwMFwvYXBpXC9sb2dpbiIsImlhdCI6MTU4OTU1NTAzNSwiZXhwIjoxNTg5NTU4NjM1LCJuYmYiOjE1ODk1NTUwMzUsImp0aSI6ImtNWE8zNVpiOEE0WFJySTMiLCJzdWIiOiJrYW1iYWRwcmFzaGFudEBnbWFpbC5jb20iLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.ZyfpZ_Cg0U8Gg-W2K9qQSGaI_XVm1GPapwo8iyhV5so
Postman screenshot of showing authorization header.
And to protect your route you have to add auth middleware in your route or route group.
$router->get('/user', [ 'middleware' => ['auth'], 'uses' => 'LoginController@user']);
And this is your user function which will return current authenticated user
public function user(Request $request) { return Auth::user(); }
So, That’s it, this is how you generate a token and use it to access protected routes. We have also seen how you can protect your routes as well.
JWT custom modification
Here, i would like to show you some custom modifications that you can make in your JWT implementation if you require it in any of your projects.
1. Changing Authorization header name
As we have seen, this library uses Authorization header by default, but what if you want to change it?
Just add the following code in your auth middleware under handle method.
$yourCustomHeaderName = 'X-Auth-Token'; if ($request->hasHeader($yourCustomHeaderName)) { $token = $request->header($yourCustomHeaderName); if (Str::startsWith($token, 'Bearer ')) { $token = Str::substr($token, 7); } $this->auth->guard($guard)->setToken($token); }
So, Your auth middleware will look like this
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Contracts\Auth\Factory as Auth; use Illuminate\Support\Str; class Authenticate { protected $auth; public function __construct(Auth $auth) { $this->auth = $auth; } public function handle($request, Closure $next, $guard = null) { $yourCustomHeaderName = 'X-Auth-Token'; if ($request->hasHeader($yourCustomHeaderName)) { $token = $request->header($yourCustomHeaderName); if (Str::startsWith($token, 'Bearer ')) { $token = Str::substr($token, 7); } $this->auth->guard($guard)->setToken($token); } if ($this->auth->guard($guard)->guest()) { return response('Unauthorized.', 401); } return $next($request); } }
You can do many other fun stuff in middleware like encrypting token, auto refreshing token and sending it in response header for your client if token is close to expiring, checking other conditions that you want to check before marking request as authorized, modifying unauthorized response to something you like and many more…
2. Changing token TTL dynamically
We can set token TTL in .env file or in config file, but what if we want to change it runtime? There are cases when we need this, for ex, if we want to allow the user to customize their session timeout.
It’s very easy, Just add this before attempting login (creating token)
Auth::setTTL(i); // Here i is new TTL in minute
// You can also use following to archive the same
//Auth::guard()->factory()->setTTL(1);
//JWTAuth::factory()->setTTL(20); // Add, use Tymon\JWTAuth\Facades\JWTAuth;