Php jwt refresh token example

While developing an application, security has become a critical factor to consider. As users grow more conscious of security issues and hackers become well-known, you'll need to implement mechanisms to improve the data security of your app.

Using session storage to safeguard apps was formerly commonplace. Sessions have become inefficient in recent years, prompting a migration to API-based authentication. Although this was a fantastic and reliable method of securing online applications, it became outdated as hackers attempted to breach it.

WHAT IS JWT?

JSON Online Token (JWT) is a secure method of authenticating users in a web application. You may send encrypted data information between a client computer and a server using JWT.

Choosing between JWT and Session is more than simply a matter of preference. To decide which one to utilize in an application, you must consider many variables. Now let us compare both of them and understand their difference.

Server-Side Sessions

Assume you have a website that has a login form. Your browser sends a request to the server when you input your email ID and password. Your server compares the password hashes, and if they match, a session with a unique session ID is generated. The server then delivers a cookie with the session ID, which is HTTP-only. Hence cannot be read by any javascript other than yours. It's also protected so that the cookie is never sent via an insecure connection. Otherwise, someone, such as a man in the middle attack, might intercept the conversation.

Client-side Sessions Work with JWT

You verify whether the password hashes match instead of starting a session in your session storage. If they do match, all you have to do now is produce a JSON signature token. The token is signed with the secret. If someone tries to change the payload notification will be rolled out and, the signature validation will fail.

You may return a web signature token stored in a cookie, which is much more convenient. Because if you don't, a third-party javascript may be able to access it.

STATELESSNESS AND PROBLEMS WITH JWT

Consider the following scenario: a bank client's personal information is compromised, and the consumer phones the bank to request that the account be locked. Because JWT is stateless, there will be a problem if the bank utilizes it for authentication. Although adding a state may be used to get around this, it contradicts the purpose of having a JWT Token as it risks logging everyone out, including the customer. Because the client's status is saved, logging out that one customer won't be a problem.

  1. Control and Visibility of Data
  2. Revoking of Roles and Privileges in JWT and Session-based Systems
  3. Consumption of Bandwidth

ATTACKS IN JWT AND SESSION-BASED SYSTEMS

Because authentication tokens are delivered via the network, they are subject to attacks. Although such attacks are unlikely, it is critical to take security seriously and deploy necessary safeguards. The system's susceptibility is determined by the combined probability of all forms of attacks. You may mitigate the attack in a few ways:

  • Man-in-the-middle attack: Using secure HTTP and secure cookies across the app will easily prevent you from this sort of attack. It does not, however, prevent proxy-based attacks.
  • OAuth token theft: The answer to OAuth token theft is to implement adequate procedures to identify stolen refresh tokens and to employ only short-lived access tokens.
  • CSRF (cross-site request forgery): CSRF attacks are prevented by using an anti-CSRF token or SameSite cookies. There are, however, additional approaches you may use to handle this in a way that is consistent with the rest of the authentication process.
  • Session fixation: Create a fresh set of tokens for each account every time a user logs in. If necessary, this procedure will invalidate the existing ones.
  • XSS Attack: An XSS attack can be avoided by ensuring that all dependencies are safe. It is a time-consuming and expensive procedure.
  • Database and filesystem access: You might take the following steps to limit the damage caused by unauthorized access to your database or filesystem: a) To prevent unwanted access, only save the hashed version of the tokens in your database. b) The attacker can access both the current and future sessions of JWTs if the private key is compromised. To avoid this, all existing JWTs must be updated before they are rendered invalid.

PHP API SECURITY USING JWTs

Now that you know everything there is to know about JWT, let's use it to protect your PHP API. If you follow the code step by step, you'll end up with a tamper-proof PHP API.

This post provides a basic login page and uses JWT to authenticate it. This guide will assist you in getting started with JWT and PHP.

You'll need PHP and composer installed on your PC to follow along. If you haven't already done so, you may learn how to install composer on your computer here.

Run composer from your project folder after you've installed it. Firebase PHP-JWT, a third-party library for working with JWTs and Apache, will be installed with Composer's help.

<?php

declare(strict_types=1);
use Firebase\JWT\JWT;
require_once('../vendor/autoload.php');

After the form is submitted, you must cross-reference the data entered with a data source or database. Let's make a hasValidCredentials variable and set it to true for our purposes. When this variable is set to true, the data has been reviewed and is legitimate.

<?php

if ($hasValidCredentials) {

Any further coding will be included within this block. All functionality related to the creation and validation of the needed JWT is governed by the value of the hasValidCredentials variable. The JWT will be produced if its value is true; else, an error will be shown.

CREATING JWT

To help with this procedure, you'll need to create some extra variables first.

Client-side browsers may simply inspect and validate JWTs. As a result, it's best to store your secret key and other sensitive data in an environment file that the user can't access via client-side queries.

$secret_Key  = 'ENTER_SECRET_KEY_HERE'
$date   = new DateTimeImmutable();
$expire_at     = $date->modify('+6 minutes')->getTimestamp();   
$domainName = "your.domain.name";
$username   = "username";                                          
$request_data = [
    'iat'  => $date->getTimestamp(),        
    'iss'  => $domainName,  
    'nbf'  => $date->getTimestamp(),  
    'exp'  => $expire_at,                  
    'userName' => $username,                    
];

Now that you have all of the necessary information, you can easily build a JWT. The encode() function from the PHP-JWT library will be used here. This technique aids in the conversion of your data array to a JSON object.

The encode function creates JWT headers and signs the incoming payload with a cryptographic combination of all the information and the specified secret key after converting to a JSON object.

To use the encode() function successfully, you must provide it with three parameters. The payload information, which in this case is the data array, should be the first parameter. Second, you must pass the secret key as an input to the function, and third, you must specify the cryptographic technique that the function should use to sign the JWT.

<?php
    // Encode the array to a JWT string.
    echo JWT::encode(
        $request_data,
        $secret_Key,
        'HS512'
    );
}

After you've got the JWT token, you may use any web programming language to transmit it to the client-side and preserve it. Let's get started with a little JS example of the path ahead.

First, save the produced and received JWT in client-side memory after successful form submission. Remove the login form and replace it with a button that retrieves and shows the JWT's timestamp to the user when clicked.

const storeJWT = {}
const loginBtn = document.querySelector("#frmLogin")
const btnResource = document.querySelector("#btnGetResource")
const formData = document.forms[0]

storeJWT.setJWT = function (data) {
  this.JWT = data
}

loginBtn.addEventListener("submit", async e => {
  e.preventDefault()

  const response = await fetch("/authenticate.php", {
    method: "POST",
    headers: {
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
    },
    body: JSON.stringify({
      username: formData.inputEmail.value,
      password: formData.inputPassword.value,
    }),
  })

  if (response.status >= 200 && response.status <= 299) {
    const jwt = await response.text()
    storeJWT.setJWT(jwt)
    frmLogin.style.display = "none"
    btnResource.style.display = "block"
  } else {
  
    console.log(response.status, response.statusText)
  }
})

JWT VALIDATION

Everything should be all right if you've followed up to this point. Let's get started verifying the JWT.

You've already used the composer's autoloader tool to assist you with this task. To extract the token from the Bearer header, use the preg match function. You'll utilize the preg match function and a regular expression for this extraction.

if (! preg_match('/Bearer\s(\S+)/', $_SERVER['HTTP_AUTHORIZATION'], $matches)){ 
    header('HTTP/1.0 400 Bad Request');
    echo 'Token not found in request';
    exit;
}

The above code tries to get the token from the Bearer header. Error management is used in this scenario. If the token is not found, the user is presented with an HTTP 404 error.

To validate the JWT, you must compare it to the JWT that was previously produced.

$jwt = $matches[1]
if (! $jwt) {
    // No token was able to be extracted from the authorization header
    header('HTTP/1.0 400 Bad Request');
    exit;
};

The extracted JWT is kept in the matches array at the first index. If the matched array is empty, this indicates that no JWT has been extracted. If the above code executes correctly, the JWT has been extracted, and you may proceed.

Verifying a JWT necessitates decoding the received data. The received data can only be decoded with the secret key. You may utilize the PHP-JWT module's static decode method once you've got the secret key.

The decode method takes the following three parameters.

  • The JWT 
  • The key to the vault
  • The decoding algorithm to be used with the JWT

If the decode method is successful, you may validate the JWT.

$secret_Key  = 'ENTER_SECRET_KEY_HERE';
$token = JWT::decode($jwt, $secret_Key, ['HS512']);
$now = new DateTimeImmutable();
$serverName = "your.domain.name";

if ($token->iss !== $serverName ||
    $token->nbf > $now->getTimestamp() ||
    $token->exp < $now->getTimestamp())
{
    header('HTTP/1.1 401 Unauthorized');
    exit;
}

You learned about JWT in this article. Then, using PHP, We built a web application that was protected with JWT authentication. We realized how simple and crucial it is to protect a web application. Remember to use this method for your API Authentication.

Happy Learning :)