PHP
Article

PHP Authorization with JWT (JSON Web Tokens)

By Miguel Ibarra Romero

If you like computer security topics, you will know that one of the most discussed and controversial topics is user authentication. Within its context, you will find a broad range of study areas, from new mechanisms to usability. It is, thus, to my surprise that JSON Web Tokens is a topic not often talked about, and I think it deserves to be in the spotlight today. We will see how easy it is to integrate it in an API authentication mechanism.

Key icon

Versus Sessions

There was a time when the only way to authenticate yourself into an application was by giving out credentials. Later came service APIs and sending out credentials in plain text was unacceptable. The idea of API tokens came up and nowadays, they are common practice.

Some of the disadvantages of giving out credentials to an application and maintaining a user’s state in relation to the application with session cookies are:

  • Data is stored in plain text on the server
    Even though the data is usually not stored in a public folder, anyone with access can read the contents of the session files.

  • Filesystem read/write requests
    Every time a session starts or its data is modified, the server needs to update the session file. The same goes for every time the application sends a session cookie. You will end up with a slow server if you have a considerable amount of users, unless you use alternative session stores.

  • Distributed/clustered applications
    Since the session files are stored in the file system by default, it is hard to have a distributed or clustered infrastructure for high availability applications that require the use of load balancers, clustered servers, etc… Other storage media and special configurations have to be made.

When dealing with service APIs that have restricted service calls, you will need to add your key to every request made (either in the request header, such as Authorization, or in the URL query string). API keys commonly rely on a centralized mechanism to control them. So if you want to mark an API key as invalid, it has to be revoked on the application side.

JWT

Since October 2010, there have been several proposals to use JSON based tokens. JWT or JSON Web Token was proposed on December 2010, having the following characteristics:

  • Intended for space constrained environments, such as HTTP Authorization headers or query string parameters.
  • Data to be transmitted in Javascript Object Notation format (JSON)
  • The data has to be the payload of a JSON Web Signature (JWS)
  • Represented using Base64 URL encoding

The JSON Web Signature is a cryptographic mechanism designed to secure data with a digital signature unique to the contents of the token in such a way that we are able to determine whether the data of the token has been tampered with or not.

The use of JWTs has many advantages over a single API key:

  • API keys are just random strings, while JWTs contain information and metadata that can describe user identity, authorization data and the validity of the token within a time frame or domain.
  • JWTs do not require a centralized issuing or revoking authority.
  • OAUTH2 compatible.
  • JWT data can be inspected.
  • JWTs have expiration controls.

On May 19th 2015, JWT became a published IETF RFC 7519.

What does it look like?

A JWT would look like the following:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MTY5MjkxMDksImp0aSI6ImFhN2Y4ZDBhOTVjIiwic2NvcGVzIjpbInJlcG8iLCJwdWJsaWNfcmVwbyJdfQ.XCEwpBGvOLma4TCoh36FU7XhUbcskygS81HE1uHLf0E

It would appear that the string is just random characters concatenated together, and not very different from an API key. However, if you look closely, there are actually 3 strings, separated by a dot character.

The first and second strings are Base64 URL encoded JSON strings, so if we decode those, we will have the following results:

{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "iat": 1416929109,
  "jti": "aa7f8d0a95c",
  "scopes": [
    "repo",
    "public_repo"
  ]
}

The first string is the JWS header, which states which cryptographic algorithm was used to generate the signature and the payload type. The second string is the payload, and passes along some standard fields, any data you wish to send within the token. The third string is the cryptographic signature, and will decode to binary data.

What is interesting about the signature is that the cryptographic algorithm requires a secret key, a string that only the issuer application has to know and should never be disclosed in any way. This way when the application receives a token, it can verify the signature against the contents of the token using said secret key. If the signature verification fails, we can know for sure that the data within the token has been tampered with and should be discarded.

You can take a look at jwt.io where you can play around with encoding and decoding JWTs.

Let’s Play

So how do we apply this to a PHP app? Let’s say we have a login mechanism that currently uses session cookies to store information about a user’s login state within the application. Please note that JWT was not designed to substitute session cookies. However, for this example, we will have a couple of services: one that generates a JWT based on the provided username and password, and another that will fetch a protected resource provided we supply a valid JWT.

Login page

Once we are signed in, we will be able to retrieve a protected resource from the application.

To begin, we install php-jwt with composer require firebase/php-jwt. In the sample application developed for this tutorial, I’m also using zend-config and zend-http, so if you’d like to follow along, feel free to install those as well:

composer require firebase/php-jwt:dev-master
composer require zendframework/zend-config:~2.3
composer require zendframework/zend-http:~2.3

There is another PHP library, jose from namshi if you would like to play with it later on.

Now, let’s assume that the login form submits the data to our JWT issuer service via AJAX, where the credentials are validated against a database, and after determining that the credentials are valid, we have to build our token. Let’s build it as an array first:

<?php
require_once('vendor/autoload.php');

/*
 * Application setup, database connection, data sanitization and user  
 * validation routines are here.
 */
$config = Factory::fromFile('config/config.php', true); // Create a Zend Config Object

if ($credentialsAreValid) {

    $tokenId    = base64_encode(mcrypt_create_iv(32));
    $issuedAt   = time();
    $notBefore  = $issuedAt + 10;             //Adding 10 seconds
    $expire     = $notBefore + 60;            // Adding 60 seconds
    $serverName = $config->get('serverName'); // Retrieve the server name from config file
    
    /*
     * Create the token as an array
     */
    $data = [
        'iat'  => $issuedAt,         // Issued at: time when the token was generated
        'jti'  => $tokenId,          // Json Token Id: an unique identifier for the token
        'iss'  => $serverName,       // Issuer
        'nbf'  => $notBefore,        // Not before
        'exp'  => $expire,           // Expire
        'data' => [                  // Data related to the signer user
            'userId'   => $rs['id'], // userid from the users table
            'userName' => $username, // User name
        ]
    ];

     /*
      * More code here...
      */
}

Please notice that you can define the data structure however you want, there are however some reserved claims, such as the ones used above:

  • iat – timestamp of token issuing.
  • jti – A unique string, could be used to validate a token, but goes against not having a centralized issuer authority.
  • iss – A string containing the name or identifier of the issuer application. Can be a domain name and can be used to discard tokens from other applications.
  • nbf – Timestamp of when the token should start being considered valid. Should be equal to or greater than iat. In this case, the token will begin to be valid 10 seconds
    after being issued.
  • exp – Timestamp of when the token should cease to be valid. Should be greater than iat and nbf. In this case, the token will expire 60 seconds after being issued.

Those claims are not required, but will help you determine the validity of a token (more on this later). Our application’s payload comes inside the data claim, where we are storing the userId and userName values. Since a JWT can be inspected client side, please remember not to include any sensitive information in it.

Transforming this array into a JWT is super easy:

<?php

/*
 * Code here...
 */

    /*
     * Extract the key, which is coming from the config file. 
     * 
     * Best suggestion is the key to be a binary string and 
     * store it in encoded in a config file. 
     *
     * Can be generated with base64_encode(openssl_random_pseudo_bytes(64));
     *
     * keep it secure! You'll need the exact key to verify the 
     * token later.
     */
    $secretKey = base64_decode($config->get('jwtKey'));
    
    /*
     * Encode the array to a JWT string.
     * Second parameter is the key to encode the token.
     * 
     * The output string can be validated at http://jwt.io/
     */
    $jwt = JWT::encode(
        $data,      //Data to be encoded in the JWT
        $secretKey, // The signing key
        'HS512'     // Algorithm used to sign the token, see https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-3
        );
        
    $unencodedArray = ['jwt' => $jwt];
    echo json_encode($unencodedArray);

JWT::encode() will take care of everything (transforming the array to JSON, producing the headers, signing the payload and encoding the final string). You will want to make your secret key a long, binary string, encode it in a config file and never disclose it. Having it directly in your code is a bad idea.

Now that the client has the token, you can store it using JS or whichever mechanism you like. Here is an example using jQuery:

$(function(){
    var store = store || {};
    
    /*
     * Sets the jwt to the store object
     */
    store.setJWT = function(data){
        this.JWT = data;
    }
    
    /*
     * Submit the login form via ajax
     */
	$("#frmLogin").submit(function(e){
	        e.preventDefault();
	        $.post('auth/token', $("#frmLogin").serialize(), function(data){
	            store.setJWT(data.JWT);
	        }).fail(function(){
	            alert('error');
	        });
	    });
});

Now let’s retrieve a resource that is protected by our JWT mechanism.

Sample UI

When clicking on the “Get resource >>” button, if everything is alright, you should see an image in the grey area. Let’s use an ajax call to send the request to the resource service:

$("#btnGetResource").click(function(e){
        e.preventDefault();
        $.ajax({
            url: 'resource/image',
            beforeSend: function(request){
                request.setRequestHeader('Authorization', 'Bearer ' + store.JWT);
            },
            type: 'GET',
            success: function(data) {
                // Decode and show the returned data nicely.
            },
            error: function() {
                alert('error');
            }
        });
    });

Please notice the beforeSend option. We are telling jQuery that before every request is made through this call, we need to set the Authorization header with the contents of the JWT in the format of Bearer [JWT]. So when we click the button, the following request is made:

GET /resource.php HTTP/1.1
Host: yourhost.com
Connection: keep-alive
Accept: */*
X-Requested-With: XMLHttpRequest
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0MjU1ODg4MjEsImp0aSI6IjU0ZjhjMjU1NWQyMjMiLCJpc3MiOiJzcC1qd3Qtc2ltcGxlLXRlY25vbTFrMy5jOS5pbyIsIm5iZiI6MTQyNTU4ODgyMSwiZXhwIjoxNDI1NTkyNDIxLCJkYXRhIjp7InVzZXJJZCI6IjEiLCJ1c2VyTmFtZSI6ImFkbWluIn19.HVYBe9xvPD8qt0wh7rXI8bmRJsQavJ8Qs29yfVbY-A0

Now we can see what the protected resource is:

Kitteh

This is how we validate the token in the resource service.

<?php
chdir(dirname(__DIR__));

require_once('vendor/autoload.php');

use Zend\Config\Config;
use Zend\Config\Factory;
use Zend\Http\PhpEnvironment\Request;

/*
 * Get all headers from the HTTP request
 */
$request = new Request();

if ($request->isGet()) {
    $authHeader = $request->getHeader('authorization');

    /*
     * Look for the 'authorization' header
     */
    if ($authHeader) {
        /*
         * Extract the jwt from the Bearer
         */
        list($jwt) = sscanf( $authHeader->toString(), 'Authorization: Bearer %s');

        if ($jwt) {
            try {
                $config = Factory::fromFile('config/config.php', true);

                /*
                 * decode the jwt using the key from config
                 */
                $secretKey = base64_decode($config->get('jwtKey'));
                
                $token = JWT::decode($jwt, $secretKey, array('HS512'));

                $asset = base64_encode(file_get_contents('http://lorempixel.com/200/300/cats/'));

                /*
                 * return protected asset
                 */
                header('Content-type: application/json');
                echo json_encode([
                    'img'    => $asset
                ]);

            } catch (Exception $e) {
                /*
                 * the token was not able to be decoded.
                 * this is likely because the signature was not able to be verified (tampered token)
                 */
                header('HTTP/1.0 401 Unauthorized');
            }
        } else {
            /*
             * No token was able to be extracted from the authorization header
             */
            header('HTTP/1.0 400 Bad Request');
        }
    } else {
        /*
         * The request lacks the authorization token
         */
        header('HTTP/1.0 400 Bad Request');
        echo 'Token not found in request';
    }
} else {
    header('HTTP/1.0 405 Method Not Allowed');
}

I’m using Zend\Http\PhpEnvironment\Request to make things a little easier to deal with extracting HTTP request types and headers:

$request = new Request();
if ($request->isGet()) { //Will only process HTTP GET requests.
	$authHeader = $request->getHeader('authorization');
	// ...

Now let’s find out if the authorization header has a JWT string in it:

/*
 * Look for the 'authorization' header
 */
if ($authHeader) {
    /*
     * Extract the JWT from the Bearer
     */
    list($jwt) = sscanf( $authHeader->toString(), 'Authorization: Bearer %s');
    // MORE CODE
}

This way the variable $jwt will have the contents of a potential JWT.

One alternative you might choose if you do not want to deal with HTTP Authorization headers, is to include the token in the request as a URL parameter:

GET /resource.php?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0MjU1ODg4MjEsImp0aSI6IjU0ZjhjMjU1NWQyMjMiLCJpc3MiOiJzcC1qd3Qtc2ltcGxlLXRlY25vbTFrMy5jOS5pbyIsIm5iZiI6MTQyNTU4ODgyMSwiZXhwIjoxNDI1NTkyNDIxLCJkYXRhIjp7InVzZXJJZCI6IjEiLCJ1c2VyTmFtZSI6ImFkbWluIn19.HVYBe9xvPD8qt0wh7rXI8bmRJsQavJ8Qs29yfVbY-A0 HTTP/1.1
Host: yourhost.com
Connection: keep-alive
Accept: */*
X-Requested-With: XMLHttpRequest

Let’s try to decode the JWT now. Remember the secret key we used earlier to generate the token? It is a vital part of the decoding process here:

$secretKey = base64_decode($config->get('jwtKey'));

/*
 * decode the JWT using the key from config
 */
$token = JWT::decode($jwt, $secretKey, array('HS512'));

If the process to decode the JWT fails, it could be that:

  1. The number of segments provided did not match the standard 3 as described earlier.
  2. The header or the payload is not a valid JSON string
  3. The signature is invalid, which means the data was tampered with!
  4. The nbf claim is set in the JWT with a timestamp, when the current timestamp is less than that.
  5. The iat claim is set in the JWT with a timestamp, when the current timestamp is less than that.
  6. The exp claim is set in the JWT with a timestamp, when the current timestamp is more than that.

As you can see, JWT has a nice set of controls that will mark it as invalid, without the need to manually revoke it or check it against a list of valid tokens.

In case you were wondering about the JWT signature and tampered data, this is possible thanks to cryptographic Message authentication codes. In a nutshell, arbitrary data input along with a key will produce a unique ‘fingerprint’ of the data. This fingerprint alone cannot be reversed back to the data input and the slightest change to either the data input or the key will produce a totally different fingerprint.

At this point we can be sure that the JWT is valid. Additionally, you could check if the user in the token is still valid, if the issuer of the token (from the iss claim) is you, or if your token has embedded permission flags, then check those against the action the user is requesting to perform.

Finally, we request an image from lorempixel.com, base64 encode it and return it in a json response string:

$asset = base64_encode(file_get_contents('http://lorempixel.com/200/300/cats/'));

/*
 * return protected asset
 */
header('Content-type: application/json');
echo json_encode([
    'img'    => $asset
]);

If you want to play with a sample application, you can check out my project’s repo for this article, follow the instructions of the README, and take a closer look at the code.

sample app

Conclusion

From here on, you can try to implement JWTs in your next API, maybe trying some other signing algorithms that use asymmetric keys like RS256 or integrate it in an existing OAUTH2 authentication server to be the API key. All your constructive feedback is welcome, as well as any questions or comments.

  • Oleg Fedorov

    Much thanks for your post, just dealing with the subject!

    Please, provide me some moments:

    1) Using jwt you still use cookie to store non-sensitive clientside user settings or no?

    2) How to deal with “session up” mechanism (for not logging out user every 20 mins)? Generate new token and replace it?

    • Miguel Ibarra

      As for your first question, a JWT is targeted to an API as a mechanism to authenticate the consumer, instead of a login mechanism to a web application. An API should not use cookies at all, since not all consumers are browsers or have a cookie handling mechanism. I would not recommend to use a JWT on a classic login mechanism on a web application. Let’s say you have a web application, which also has its API (like github, twitter, etc…) You can login into the application with the classic user/password mechanism (and store user state and settings in a cookie), and have the application generate a JWT for you, so you can be able to call your app’s non-public API without supplying your user/password with every call.

      After reading above, you’ll see that JWT are not a replacement of sessions, however JWT do have its own validity controls. If a JWT expires, your only choice is to generate a new one.

      Hope this helps.

      • http://www.skooppa.com s.molinari

        I think this image does a good job on showing where the JWT should come in as a replacement of the session.

        https://docs.google.com/drawings/d/1wtiF_UK2e4sZVorvfBUZh2UCaZq9sTCGoaDojSdwp7I/edit

        I’d also like to throw in these advantages. From here: https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/

        Cross-domain / CORS: cookies + CORS don’t play well across different domains. A token-based approach allows you to make AJAX calls to any server, on any domain because you use an HTTP header to transmit the user information.

        Stateless (a.k.a. Server side scalability): there is no need to keep a session store, the token is a self-contanined entity that conveys all the user information. The rest of the state lives in cookies or local storage on the client side.

        CDN: you can serve all the assets of your app from a CDN (e.g. javascript, HTML, images, etc.), and your server side is just the API.

        Decoupling: you are not tied to a particular authentication scheme. The token might be generated anywhere, hence your API can be called from anywhere with a single way of authenticating those calls.

        Mobile ready: when you start working on a native platform (iOS, Android, Windows 8, etc.) cookies are not ideal when consuming a secure API (you have to deal with cookie containers). Adopting a token-based approach simplifies this a lot.

        CSRF: since you are not relying on cookies, you don’t need to protect against cross site requests (e.g. it would not be possible to your site, generate a POST request and re-use the existing authentication cookie because there will be none).

        Performance: we are not presenting any hard perf benchmarks here, but a network roundtrip (e.g. finding a session on database) is likely to take more time than calculating an HMACSHA256 to validate a token and parsing its contents.

        Login page is not an special case: If you are using Protractor to write your functional tests, you don’t need to handle any special case for login.

        Standard-based: your API could accepts a standard JSON Web Token (JWT). This is a standard and there are multiple backend libraries (.NET, Ruby, Java, Python, PHP) and companies backing their infrastructure (e.g. Firebase, Google, Microsoft). As an example, Firebase allows their customers to use any authentication mechanism, as long as you generate a JWT with certain pre-defined properties, and signed with the shared secret to call their API.

      • Oleg Fedorov

        Thank you for your answers, gradually comes understanding of usage jwt.

        Together with the recent release of JSON API (http://jsonapi.org/#update-history) I hope to finally put an end to many disputes about format and auth principles in application API, that we are currenly developing.

      • Matthew Koch

        Where would you store the JWT that your app generates after log in?

    • SP1966

      1. No, for non-sensitive items you can just put them in the data payload.
      2. Yes, you’ll need to manually create a new token.

  • lebobbi

    Excellent article. Fresh and straight to the point. Will use to communicate a php application with a node one.

    • Miguel Ibarra

      Thank you for your feedback. I’m glad you liked it.

  • Art Hundiak

    Nice article. Kind of wish you had picked a different implementation since using static methods is not always a good choice. But I really wish you had mentioned the use of https vs http. A casual reader might think that the encoding stuff protects the contents payload from being read.

    • Miguel Ibarra

      Unfortunately, php-jwt/JWT library offers a static method interface only at the moment (check https://github.com/firebase/php-jwt/blob/master/Authentication/JWT.php) Perhaps other libraries offer an instantiated object approach as well. The important thing is understanding the JWT concept, since it should be independent from the implementation. You have a point about HTTP/HTTPS, JWT is not intended to conceal data. However the article does mention the jwt.io site, where you can encode and decode JWTs, so you can get a hint that the contents of a JWT can be easily decoded.

      I’m glad you liked the article, thank you for your feedback.

  • Alejandro Acosta Barrios

    Awsome article Miguel, this is helpful for apps in the cloud.

    • Miguel Ibarra

      Thanks a lot for your comments.

  • Miguel Ibarra

    Excellent resource! I agree that JWT could take the job of a pure cookie-based login mechanism. The trouble comes when trying to store stateful data along the auth data, like in sessions. You could have the JWT in a cookie, and the session data in a different cookie. However JWT has a bigger role than replacing session cookies.

    Thanks for your feedback.

    • http://www.skooppa.com scamo

      I agree, but the idea of JWT is to store the session state with on the client side, so you don’t have to do it on the server, saving some resources. And a JWT should be stored in either client side session storage or local storage. Cookie storage should only be a last resort.

      • Miguel Ibarra

        In fact you can store session state in the token, but not for the purpose of saving resources on servers, since by rfc7519 definition, “JSON Web Token (JWT) is a compact claims representation format intended for space constrained environments such as HTTP Authorization headers and URI query parameters.” If session state data becomes too large, a JWT is not the ideal place to store it.

        • http://www.skooppa.com scamo

          The only reason to keep the token small is because it is sent over the wire on every request. It isn’t a reason to not store session state and it does save resources. If you don’t have to query a database or a cache for session data, you’ve saved resources, albeit I’ll agree, it isn’t much. It is still an advantage.

  • Eran

    Thanks for the useful article. I’m still trying to wrap my head around the security here and maybe it’s not concerning JWT at all but rather a much more basic question about client side stored session/token in general, sorry if the answer is trivial.

    I’m aware of JWT’s advantages when it comes to simplicity of use, scalability, performance and statelessness.
    I understand that the token resides inside either a cookie or other web storage means of the browser.
    I also know about http only (for cookies) and SSL security while auth.

    Allow me to describe a possible scenario and please let me know what I’m missing. While I’m working in an open space and logged into a system/site (that uses JWT), I’m currently off my desk for a couple of minutes. During that time, a fellow co-space user approaches my laptop and copies the token. Here’s the question:
    Can they do that? Can a token (or its container) be copied and used by someone else, hijacking my session without even decrypting the token or viewing its contents?
    Also, if we’re using the same co-space office, we’re most likely using the same IP address so that also rules out this extra verification method? (or maybe it’s ruled out in the first place because it will break statelessness because of the need to touch db)?

  • http://janvt.com janvt

    lovely! works awesome, thanks for this! :)

  • dede

    Hi, thanks for the article, i’m new in php, honestly i don’t know how to create your article.
    Could yo provide a sample code please?
    Thanks

  • Jure Čekon

    Hi. Is it posible to decode the token that has expired? (on the server-side, with this library – without throwing exception)

    Or how to determine why is token invalid? (is it expiration, wrong data, nbf < current time…)

  • Irfan

    I installed everything but i am getting error jwt is undefinedand
    $config = Factory::fromFile(‘config/config.php’, true);
    var_dump($config); does not show any thing
    I am very young in this field please help me

    • Miguel Ibarra

      make sure you have run “composer update” from the command line, and that you have created the file config/config.php with the required content. use the sample file as reference

  • http://serialsfree.net/ Valeriy Donika

    i didn`t see any config file, where is this file ?

  • Anexsoft

    I have a question … What happend if you are login by your token, i copy the token and pass the token from other browser. I will have acces to the System no? How you can validate that Toke is unique for client, that this token cant use in other pc or browser ..

  • Anexsoft

    I think the same but, using IP + USER-AGENT + HOST-NAME .. thx ;)

  • Normunds Pauders

    I dont undrestant, what i am doing wrong:

    1. Download zip arhive https://github.com/tecnom1k3/sp-simple-jwt
    2. Put on server and Extra to….
    3. Run command composer install
    4. Run command composer update
    5. Change my config file data

    Run this excample. Try login, bet i get this error:
    http://postimg.org/image/h9jmkylg3/

    Maybe you can suggest, what am I doing wrong?

    In future this excample can use with AngularJS

  • Hilman

    thanks for the tutorial, learn a lot. i’d like to know, in the validaiton part, we store a $token from JWT::decode($jwt, $secretKey, array(‘HS512’)); but didn’t do anything about that. Why?

  • Kaluosi

    There are a lot of error and misconfiguration in the github repository. The code as you have in the repository following the steps in the tutorial simply does not work. After much work I can figure out what you were intended to do. There is a mismatch between the tutorial and the repository. Bad practice!!

  • arvindpdmn

    Just did php composer.php update. This installed php-jwt and Zend framework. But when I execute the very first lines of code I get:

    PHP Fatal error: Class ‘Factory’ not found in /home/arvind/workspace/mysite/components/com_wsn/apiauth2.php on line 11

    I thought autoload.php is supposed to bring all the composer-installed stuff into the execution environment. Anyway, I am new to this. Please help.

    • themhz

      I have the same problem and I cant understand why. Is Zend Framework not loaded?

  • http://maxvoloshin.com/ Max Voloshin

    No doubt :)

    But firstly you should be able to steal JWT, then figure out IP and User-Agent from a custom hash, then tamper source IP in a request and do it before JWT will be expired…

  • Miguel Ibarra

    Yes, everything is subject to be tampered with… However, the slightest change will make the signature check on server side to fail, thus making the entire JWT invalid. That’s exactly the whole point of JWT, you’ll realize that all that effort is not worthy at all since the signature check will fail

  • http://codezone4.wordpress.com Rajitha Bandara

    Thanks for the detailed tutorial. I managed to get this worked with some changes to repository code.

    • Miguel Ibarra

      If you wish, you can do a pull request, so I can see the changes and update the repo. Thanks!

  • mth

    Great tutorial.. This is what I am looking for.

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in PHP, once a week, for free.