TrinityTuts

Secure your PHP Web Services using JWT

Last updated on August 14th, 2019 at 11:40 pm

Slim framework is used to create REST API, In my last post, I explain how to create Rest API using slim. In this post, I will explain how to make your REST API secure using JSON Web Token (JWT). There is lots of information already on the web about JSON Web Token (JWT) Authentication so we will not duplicate it here but in a nutshell, it allows authenticating users against a single token instead of the more commonly used username/password. You can simply follow below step to create REST API with Slim.

 

 

Install Slim framework

You can install Slim using composer which is the best way to setup, open terminal and run below command.

php composer.phar create-project slim/slim-skeleton jwtApp

Else if your composer install globally then use this command

composer create-project slim/slim-skeleton jwtApp

Install required dependencies

After setup Slim, we need to add some dependencies which we can use to generate JWT

composer require firebase/php-jwt
composer require tuupola/base62
composer require tuupola/slim-basic-auth
composer require tuupola/slim-jwt-auth
composer require tuupola/cors-middleware

I am using PSR-7 JWT Authentication Middleware for this post.

Slim JWT Auth setup

If you are using Apache server you need to update your project .htaccess open your  .htaccess file in the editor and replace the code with below code

RewriteEngine On
#RewriteBase /api/

RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

# Make sure $HTTP_RAW_POST_DATA is deprecated warning does not appear
php_value   always_populate_raw_post_data   -1

Now go to your project src folder and edit middleware.php file in this file we can add our middleware in which we define all our settings, there are lots of options available with this plugin if you want to read about them please follow this link. Now add this below code into your middleware.php 

<?php
// Application middleware

// e.g: $app->add(new \Slim\Csrf\Guard);
// Adding dependencies

use Tuupola\Middleware\HttpBasicAuthentication;

$container = $app->getContainer();
$container['logger'] = function($c) {
    $logger = new \Monolog\Logger('my_logger');
    $file_handler = new \Monolog\Handler\StreamHandler("../logs/app.log");
    $logger->pushHandler($file_handler);
    return $logger;
};

$container["jwt"] = function ($container) {
    return new StdClass;
};

$app->add(new \Slim\Middleware\JwtAuthentication([
	"path" => "/",
    "logger" => $container['logger'],
    "secret" => "123456789helo_secret",
    "rules" => [
        new \Slim\Middleware\JwtAuthentication\RequestPathRule([
            "path" => "/",
            "passthrough" => ["/token", "/not-secure", "/home"]
        ]),
        new \Slim\Middleware\JwtAuthentication\RequestMethodRule([
            "passthrough" => ["OPTIONS"]
        ]),
    ],
    "callback" => function ($request, $response, $arguments) use ($container) {
        $container["jwt"] = $arguments["decoded"];
    },
    "error" => function ($request, $response, $arguments) {
        $data["status"] = "error";
        $data["message"] = $arguments["message"];
        return $response
            ->withHeader("Content-Type", "application/json")
            ->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
    }
]));

$app->add(new \Slim\Middleware\HttpBasicAuthentication([
    "path" => "/api/token",
    "users" => [
        "user" => "password"
    ]
]));


$app->add(new \Tuupola\Middleware\Cors([
    "logger" => $container["logger"],
    "origin" => ["*"],
    "methods" => ["GET", "POST", "PUT", "PATCH", "DELETE"],
    "headers.allow" => ["Authorization", "If-Match", "If-Unmodified-Since"],
    "headers.expose" => ["Authorization", "Etag"],
    "credentials" => true,
    "cache" => 60,
    "error" => function ($request, $response, $arguments) {
        return new UnauthorizedResponse($arguments["message"], 401);
    }
]));

In above code, we call our JwtAuthentication class in which we can set some parameter like path, secret key, Path which we need to ignore, error, callback etc. Copy above code into your middleware.php. 

After adding above code you can now edit your routes.php which is also inside your src folder in this we can create or routes and handle user request generate token for the user. You can create no of routes depend upon your need but for this sample, I create 4 routes as shown below code snippet. 

<?php
// Routes

use Firebase\JWT\JWT;
use Tuupola\Base62;

$app->post("/token",  function ($request, $response, $args) use ($container){
    /* Here generate and return JWT to the client. */
    //$valid_scopes = ["read", "write", "delete"]

  	$requested_scopes = $request->getParsedBody() ?: [];

    $now = new DateTime();
    $future = new DateTime("+10 minutes");
    $server = $request->getServerParams();
    $jti = (new Base62)->encode(random_bytes(16));
    $payload = [
        "iat" => $now->getTimeStamp(),
        "exp" => $future->getTimeStamp(),
        "jti" => $jti,
        "sub" => $server["PHP_AUTH_USER"]
    ];
    $secret = "123456789helo_secret";
    $token = JWT::encode($payload, $secret, "HS256");
    $data["token"] = $token;
    $data["expires"] = $future->getTimeStamp();
    return $response->withStatus(201)
        ->withHeader("Content-Type", "application/json")
        ->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
});

$app->get("/secure",  function ($request, $response, $args) {

    $data = ["status" => 1, 'msg' => "This route is secure!"];

    return $response->withStatus(200)
        ->withHeader("Content-Type", "application/json")
        ->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
});

$app->get("/not-secure",  function ($request, $response, $args) {

    $data = ["status" => 1, 'msg' => "No need of token to access me"];

    return $response->withStatus(200)
        ->withHeader("Content-Type", "application/json")
        ->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
});

$app->post("/formData",  function ($request, $response, $args) {
    $data = $request->getParsedBody();

    $result = ["status" => 1, 'msg' => $data];

    // Request with status response
    return $this->response->withJson($result, 200);
});


$app->get('/home', function ($request, $response, $args) {
        // Sample log message
    $this->logger->info("Slim-Skeleton '/' route");

    // Render index view
    return $this->renderer->render($response, 'index.phtml', ["name" => "Welcome to Trinity Tuts demo Api"]);
});

In first routes, we can create token through which we can access other routes and I also create /not-secure route for this route we don’t need any token. Please check the demo video to check working of these routes. You can also try this in your REST Client like PostMan or Advanced REST Client.

 

Demo API Link:-  https://demo.trinitytuts.com/api/

Hope this post helps you create your secure REST API using JWT Auth.

If this post helps you to understand working with Android Volley library, then please subscribe, our blog Also do Like our Facebook Page or Add us on Twitter.