
Authentication is one of the most important parts of any web application. Handling authentication between mobile application and server side can be tricky and demand more secure approach. One of the best approach is using JSON Web Token (JWT).
The structure of a JWT token consists of header, payload, and signature. Basically, server side just create the token with existing claims and the Front-end like VueJS use as authorization of any API request. Server side just validate whether valid token from issuer or not. Enough talk, here the question :-
Are JWT tokens secure?
By default JWT are not encrypted and the token is simply a base64 encoded that can be easily decoded to see the plain JSON content that the token carries.
So the response to the question is ‘It depends’. JWT depends heavily on a good configuration when issuing the tokens and in a correct use and proper validation of the consumed tokens.
For best practice, you may refer here. The best practice covers on how you can secure JWT. But, none of it mention on how to restrict the JWT token based on IP Address, or etc…
What is the solution then?
Here come the solution. I proposed where there are 3 way what we can use on restricting JWT origin based on below criteria
- IP Address (Client IP)
- User Agent (Client user agent)
- Hostname (Server name)
All of the criteria will be used in the custom claim. So, the proposed solution are
- Generate JWT token with hash custom claim with one or all the origin of token’s ip address, user agent and hostname
- Create a Laravel middleware and validate payload of the token before the authentication happen
Lets get Started
An example payload would be like this
{
"iss":"example.com/api/token",
"iat": 1632451588,
"exp": 1632537988,
"nbf": 1632451588,
"jti": "R4MOzdBP8Hjv54fg",
"sub": 3170722,
}From the payload, we will add another claims like below
{
"iss":"example.com/api/token",
"iat": 1632451588,
"exp": 1632537988,
"nbf": 1632451588,
"jti": "R4MOzdBP8Hjv54fg",
"sub": 3170722,
"hst": "XXXXXXX",
"ura": "XXXXXXX",
"ipa": "XXXXXXXX"
}where hst referring to hostname, ura referring to User agent and ipa referring to IP Address. “hst”, “ura”, “ipa” is manmade parameters. You can freely use any wording to can other than reserved JWT claims.
So lets start with installing composer package (if not installed yet)
composer require tymon/jwt-auth
Based on quick start (i assumed you already use this package), implement JWTSubject to any model you want to use to make authentication. So, lets say table user
You will notice that there is method getJWTCustomClaims. This is where we can put our custom claims. Let’s put those 3 origin identity as we discuss earlier.
return [
'hst' => md5(gethostname()),
'ipa' => md5(request()->ip()),
'ura' => md5(request()->userAgent()),
];
i would like to suggest you hash the value. Because If you don’t, someone can easily decrypt the payload using online JWT debugger.
Now, generate the token
$token = auth()->attempt($request->only('username', 'password'))It will generate like below
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzUxMiJ9.eyJpc3MiOiJodHRwczovL2FwaS1kZXYua2l0YWphZ2EuY28vYXBpL3BvcnRhbC90b2tlbiIsImlhdCI6MTYzMjY5ODUyMiwiZXhwIjoxNjMyNzg0OTIyLCJuYmYiOjE2MzI2OTg1MjIsImp0aSI6IkpYOGJESlNCelhoajh4bXYiLCJzdWIiOjEsInBydiI6ImMwOTczZDkxMDk1ZTI0MWE3YTNjNWRmZmI1ZWQ0ZDJlODdiOGQ2NWEiLCJoc2giOiIxZGFXcFlRRGJNR25WbU80UmRhNnI4Mjl6akxkYkt6UFFtazN2N0owNWd3Wk5xRWxvQTFXWGV4UEtKcFAiLCJkZWkiOiJhYWFhYi0xMjMyMy0zc2RhZHNhdjEyMzIzMS0yMjItMzMtYWRyaS15ZnlmMTIxIiwicHRuIjpudWxsLCJoc3QiOiJ............
Ok lets use JWT debugger to see the payload whether the custom claim is attached or not

You can see the last 3 claims in the Payload is attached successfully. Now all we need is the validation middleware.
php artisan make:middleware AuthenticateJWT
The logic is where, we fetch the JWT payload from each HTTP request and compare the values. If either one is not meet the requirement, simply throw the exception. Here is the full snippet :-
Explanation
Here is some explanation. What is above middleware do? It basically just validating the token creation origin. Here some situation,
Customer create a token by IP 10.10.10.10 with User Agent Safari Browser. When transmitting in network, attackers able to grab your JWT token. The attackers would try to authenticate using your JWT token on their side. Unfortunately, the token is rejected due to attacker IP (11.11.11.11) is different from the victim IP and same goes to the user agent. The token only can use by IP 10.10.10.10, else rejected by the server.
For the hostname restriction, is less recommended because if you have multiple server under load balancer, you need to make sure all of your server have same hostname. Else it wont work.
For hashing. I would like to recommend to hash the custom claims value with salts. Because, the basic MD5 would be easily to be brute.
Conclusions
There are many way if we want to secure anything in application. Of course, there is no system is safe. If we can’t focus on how to completely secure it, just think how to slow down the attackers mind.
That’s it. Hope its help 😁. Thanks for your time.
P/S: This article are not really meant for PHP developers. It’s also meant for other developer because the flow is just the same. The usage of JWT is completely the same in other programming language
References
https://www.bbva.com/en/json-web-tokens-jwt-how-to-use-them-safely/