Authentication using JWT in Laravel 5
With Javascript front-end technologies, we are building APIs for the back-end. Our back-end is exclusively designed for frontend mobile apps. Authentication is one of the major part of a web application. The two best solutions to authentication problems for APIs are the OAuth 2.0 and JWT (JSON Web Token).
JWT is used to send authentication details or any custom information that can be verified. It is represented as a sequence of base64url encoded values and that are separated by period characters.
Header
The first part of the token contains the metadata for token, type of signature and algorithm for encryption.
{ “alg”: “HS256”, “typ”: “JWT” }
Once this is base64 encoded, then we get the first part of token.
eg: EyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
Payload
The second part of the token contains the claims. Claims are the statements about an entity and additional metadata.
There are three types of claims:
Reserved
There are a set of predefined claims, they are not mandatory but recommended
These include:
iss
: The issuer of the token
sub
: The subject of the token
aud
: The audience of the token
exp
: Token expiration time defined in Unix time
nbf
: “Not before” time that identifies the time before which the token must not be accepted for processing
iat
: “Issued at” time, in Unix time, at which the token was issued
jti
: JWT ID claim provides a unique identifier for the web token
Public claims
These can be defined at will by using JSON Web Tokens. To avoid collisions, they should be defined in the IANA JWT Registry or be defined as a URI which contains a collision resistant namespace
.
Private claims
The custom claims created to share various information between parties that agree on using them.
Example :
{ "sub": "1", "iss": "http://localhost:8000/auth/login", "iat": 1451888119, "exp": 1454516119, "nbf": 1451888119, "jti": "37c107e4609ddbcc9c096ea5ee76c667" }
Signature
To create the signature part, we have to take the encoded header, encoded payload, a secret, the algorithm that specified in the header, and sign that.
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Putting all together
The following is an example token that has the previous header and encoded payload, and it is signed with a secret.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWx
ob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4OD
ExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ.wyoQ95RjAyQ2FF3aj8EvCSaUmeP0KUqcCJDENNfnaT4
Integration with Laravel 5
Installation
To install, we need to edit the composer.json to require the package.
"require": { "tymon/jwt-auth": "0.5.*" }
then update composer using composer update command.
After completing composer update we need to add it to the providers array of the app.php
'Tymon\JWTAuth\Providers\JWTAuthServiceProvider'
and also add these to providers array
'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth'
'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory'
then, publish config file, If you’re using laravel 5:
php artisan vendor:publish
--provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
and generate a secret key
php artisan jwt:generate
if you want to add it into .env file, then create JWT_SECRET
in it
and generate secret key again
Configuration
In config/jwt.php
, you can configure settings:
ttl:
This is the length of time, in minutes, that the token will be considered valid.
refresh_ttl:
This is the length of time, in minutes, that we can refresh a token.
algo:
This is the algorithm used to sign the tokens.
user:
The namespace path that points to your User model.
identifier:
is used for retrieving the user from the token subject claim.
required_claims:
These claims must be present in the payload of token or a TokenInvalidException will be thrown.
blacklist_enabled:
If this option is set to false, then we will not be able to invalidate tokens. Although, we may still refresh tokens
Providers
Providers are the concrete implementations that are used for achieving various tasks. We can override these as long as the implementation adheres to the relevant interfaces.
User – providers.user
Specify the implementation that is used to find the user based on the subject claim.
JWT – providers.jwt
This will do the heavy lifting of decoding and encoding of tokens.
Authentication – providers.auth
This will retrieve the authenticated user, via credentials or by an id.
Storage – providers.storage
This is used to drive the Blacklist, and store the tokens until they expire.
Token creation
Token based on the user’s credentials
Authenticate the user and return a token corresponding to that user.
Example, Suppose we have a Controller AuthController
use JWTAuth; use Tymon\JWTAuth\Exceptions\JWTException; class AuthController extends Controller { public function authenticate(Request $request) { // grab credentials from the request $credentials = $request->only('email', 'password'); try { // attempt to verify the credentials and create a token for the current user if (! $token = JWTAuth::attempt($credentials)) { return response()->json(['error' => 'invalid_credentials'], 401); } } catch (JWTException $e) { // error while attempting to encode token return response()->json(['error' => 'could_not_create_token'], 500); } // all good, return the token return response()->json(compact('token')); } }
In some cases we need to create token from user object then,
// some user $user = User::first(); $token = JWTAuth::fromUser($user);
There is another option to create token based on any data using Tymon\JWTAuth\PayloadFactory
instance (or using the included JWTFactory
facade)
$customClaims = ['foo' => 'bar', 'baz' => 'bob']; $payload = JWTFactory::make($customClaims); $token = JWTAuth::encode($payload);
You can also chain claims directly onto the Tymon\JWTAuth\PayloadFactory instance, (or using the included JWTFactory facade)
// add a custom claim with a key of `foo` and a value of ['bar' => 'baz'] $payload = JWTFactory::sub(123)->aud('foo')->foo(['bar' => 'baz'])->make(); $token = JWTAuth::encode($payload);
Authentication
To make authenticated requests via http, you will need to set an authorization header
Authorization: Bearer {yourtokenhere}
Alternatively you can include the token via a mysiteing
http://api.cubettech.com/me?token={yourtokenhere}
For getting token from request,
// this will set the token on the object
JWTAuth::parseToken();
// and you can continue to chain methods
$user = JWTAuth::parseToken()->authenticate();
To get the token value, you can call:
$token = JWTAuth::getToken();
you can also manually set the token
JWTAuth::setToken(‘foo.bar.baz’);
Retreiving an Authenticated user from a token
// somewhere in your controller public function getAuthUser() { try { if (! $user = JWTAuth::parseToken()->authenticate()) { return response()->json(['user_not_found'], 404); } } catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) { return response()->json(['token_expired'], $e->getStatusCode()); } catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) { return response()->json(['token_invalid'], $e->getStatusCode()); } catch (Tymon\JWTAuth\Exceptions\JWTException $e) { return response()->json(['token_absent'], $e->getStatusCode()); } // Token is valid and we have found the user via the sub claim return response()->json(compact('user')); }
If you don’t like to catch multiple exceptions inline, then you can add a global exception handler by adding the following code to the render method within app/Exceptions/Handler.php
.
public function render($request, Exception $e) { if ($e instanceof Tymon\JWTAuth\Exceptions\TokenExpiredException) { return response()->json(['token_expired'], $e->getStatusCode()); } else if ($e instanceof Tymon\JWTAuth\Exceptions\TokenInvalidException) { return response()->json(['token_invalid'], $e->getStatusCode()); } return parent::render($request, $e); }
There are two included middlewares are there,
'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken', 'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken',
To use these will have to register them in app/Http/Kernel.php
under the $routeMiddleware
property.
Conclusion
JWT enables us to make easy and secure authentication. Tokens can be stored in local storage/web storage or cookies based on the need. Authentication of API’s is always an easy process with JWT.