How integrate jwt with the 4th version of Slim

Hello you all,

I would like to integrate jwt into my slim project but I have no idea about the beginning, can someone help me?

What is your client ? PHP or JS?

I use JS.

It’s depends of the client ?

To make myself more clear. Just was wondering if your client application is js (react, vue, etc.) and you try to fetch some data from php endpoint or maybe your client application is another (also) php. Simply saying, which one sends a request to the endpoint, some js client or some php client ?
I’m asking because as far as I can see, last time there was a mobile engaged in the process.

Okay.

My client application is vuejs, and I would like to create web services including the token processes.

For example, I would like create authentication web service. How can I integrate the token processes into my authentication endpoint ?

This should give you a solid start point for an api endpoint token integration within Slim4:

$ cd my-api-project
$ composer require tuupola/slim-jwt-auth

.htaccess

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

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

config.php/settings.php

return [
    ...
    'jwt_authentication' => [
        'secret' => $_ENV['JWT_SECRET'],
        'algorithm' => 'HS256',
        'secure' => false, // only for localhost for prod and test env set true
        'error' => static function ($response, $arguments) {
            $data['status'] = 401;
            $data['error'] = 'Unauthorized/'. $arguments['message'];
            return $response
                ->withHeader('Content-Type', 'application/json;charset=utf-8')
                ->getBody()->write(json_encode(
                    $data,
                    JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT
                ));
        }
    ],
    ...
];

dependencies.php

use Tuupola\Middleware\JwtAuthentication;

return [
    ...
    JwtAuthentication::class => static function (Container $container):  JwtAuthentication {
        return new  JwtAuthentication(
            $container->get('jwt_authentication'),
        );
    },
    ...
];

routes.php

use Tuupola\Middleware\JwtAuthentication;

if (isset($app, $container)) {
    ...
    $app->get('/api/example', function (Request $request, Response $response) {
        return $this->get('example_controller')->getExamples($request, $response);
    })->setName('api-examples')
        ->addMiddleware($container->get(JwtAuthentication::class));
    ...
}

For further information:

slim-jwt-auth

Okay thanks, I will try it and give a feedback

First you import Tuupola jwt library.

$ cd my-api-project
$ composer require tuupola/slim-jwt-auth

Then create the .htaccess file inside the public/ folder with the following code.

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

After that you must create your controller for user authentication.

<?php

namespace App\Controllers;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

use App\Models\Users;
use Firebase\JWT\JWT;
use App\Utilities\JsonRenderer;

final class Auth
{
    private JsonRenderer $renderer;
    private Users $Users;

    public function __construct(JsonRenderer $renderer, Users $Users)
    {
        $this->renderer = $renderer;
        $this->Users = $Users;
    }

    public function __invoke(Request $req, Response $res): Response
    {
        try {
            if ($req->hasHeader("email") && $req->hasHeader("senha")) :
                $result = $this->Users->getUserByEmail($req->getHeader("email")[0]);

                if (!isset($result["Error"])) :

                    if (!is_null($result)) :

                        if ($req->getHeader("senha")[0] === $result["senha"]) :

                            $expire = (new \DateTime("now", new \DateTimeZone("America/Sao_Paulo")))->modify("+1 hour")->format("Y-m-d H:i:s");
                            $token = JWT::encode(["expired_at" => $expire], "I3SISTEMAS");

                            return $this->renderer->json($res, [
                                "Success" => [
                                    "token" => $token
                                ]
                            ])->withHeader("Content-Type", "application/json")
                                ->withStatus(201);
                        else :
                            return $this->renderer->json($res, [
                                "Error" => [
                                    "Message" => "Falha Na Autenticação"
                                ]
                            ])->withHeader("Content-Type", "application/json")
                                ->withStatus(401);
                        endif;
                    else :
                        return $this->renderer->json($res, [
                            "Error" => [
                                "Message" => "Falha Na Autenticação"
                            ]
                        ])->withHeader("Content-Type", "application/json")
                            ->withStatus(401);
                    endif;
                else :
                    return $this->renderer->json($res, [
                        "Error" => [
                            "Message" => $result["Error"]
                        ]
                    ])->withHeader("Content-Type", "application/json")
                        ->withStatus(500);
                endif;
            else :
                return $this->renderer->json($res, [
                    "Error" => [
                        "Message" => "Você Deve Passar o `email` && `senha` no Cabeçalho!"
                    ]
                ])->withHeader("Content-Type", "application/json")
                    ->withStatus(422);
            endif;
        } catch (\PDOException $e) {
            return $this->renderer->json($res, [
                "Error" => [
                    "Message" => $e->getMessage()
                ]
            ])->withHeader("Content-Type", "application/json")
                ->withStatus(500);
        }
    }
}

Next, create the authentication route.

$app->post("/auth", \App\Controllers\Auth::class);

Remembering that this is my method of verifying and authenticating a user, in your case it may be different.

Leaving this controller aside, let’s create the middleware that will check if the token passed in the “Authorization” header is valid.

In this case you must create a middleware folder, then create the jwtAuth.php file and put the following code.

<?php

namespace App\Middlewares;

use Nyholm\Psr7\Response as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use App\Utilities\JsonRenderer;

final class jwtAuth
{
    private JsonRenderer $renderer;
    private Response $res;

    public function __construct(JsonRenderer $renderer, Response $res)
    {
        $this->renderer = $renderer;
        $this->res = $res;
    }

    public function __invoke(Request $req, RequestHandler $han)
    {
        try {
            if ($req->hasHeader("Authorization")) :
                $header = $req->getHeader("Authorization");

                if (!empty($header)) :
                    $bearer = trim($header[0]);
                    preg_match("/Bearer\s(\S+)/", $bearer, $matches);
                    $token = $matches[1];

                    $key = new Key("I3SISTEMAS", "HS256");
                    $dataToken = JWT::decode($token, $key);
                    $now = (new \DateTime("now", new \DateTimeZone("America/Sao_Paulo")))->format("Y-m-d H:i:s");

                    if ($dataToken->expired_at < $now) :
                        return $this->renderer->json($this->res, [
                            "Error" => [
                                "Message" => "Token Expirado!"
                            ]
                        ])->withHeader("Content-Type", "application/json")
                            ->withStatus(401);
                    endif;
                endif;
            else :
                return $this->renderer->json($this->res, [
                    "Error" => [
                        "Message" => "Acesso Não Autorizado!"
                    ]
                ])->withHeader("Content-Type", "application/json")
                    ->withStatus(401);
            endif;
        } catch (\Exception $e) {
            return $this->renderer->json($this->res, [
                "Error" => [
                    "Message" => $e->getMessage()
                ]
            ])->withHeader("Content-Type", "application/json")
                ->withStatus(500);
        }
        $res = $han->handle($req);
        return $res;
    }
}

After that we should import the middleware/jwtAuth.php in the routes file.

use App\Middlewares\jwtAuth;

Then add the class like any other controller.

->add(jwtAuth::class);

Example

$app->get("/", \App\Controllers\HomeAction::class)->add(jwtAuth::class);

For those who want to use the “JsonRenderer” method that I use, they must create a folder called Utilities, after that, inside the folder, create the JsonRenderer.php file, and add the following code:

<?php

namespace App\Utilities;

use Psr\Http\Message\ResponseInterface;

final class JsonRenderer
{
    public function json(ResponseInterface $res, $data = null, int $options = 0): ResponseInterface
    {
        $res = $res->withHeader("Content-Type", "application/json");
        $res->getBody()->write((string)json_encode($data, $options));

        return $res;
    }
}

Remembering that you must pass the token in the “Authorization” header otherwise the application will not allow access to the designated routes.

In theory everything is ready, if you have a problem, you can send it down here.
Another thing, I’m not AMERICAN, I had to use a translator, in case the words are a bit meaningless, don’t blame me, blame google Translate :slight_smile:

I hope I helped you, a hug to all of you :slight_smile: