This document provides instructions for generating MigratoryData JWT tokens with PHP.

Signing algorithms

MigratoryData JWT Authorization add-on supports both symmetric and asymmetric signatures:

HMAC

This symmetric signature method uses a secret key for signing the JWT tokens by your backend. The same secret key must be used for verifying the JWT tokens by the JWT Authorization add-on. The signature algorithms supported by this method, automatically selected according to the length of the secret key, are as follows:

  • HS256 (HMAC SHA256), requires a 32-byte secret - recommended for most use cases
  • HS384 (HMAC SHA384), requires a 48-byte secret
  • HS512 (HMAC SHA512), requires a 64-byte secret

For example, to generate a 32-byte HMAC secret key, you can use the following command:

openssl rand -base64 32
Configure the parameter signature.hmac.secret of the JWT Authorization add-on with this HMAC secret key.

RSA

This asymmetric signature method uses a pair of public and private keys. The private key should be used by your backend for signing the JWT tokens. The public key should be used by the JWT Authorization add-on for verifying the JWT tokens. The signature algorithms supported by this method, automatically selected according to the length of the private key, are as follows:

  • RS256 (RSA SHA256), requires a 2048-bit private key - recommended for most use cases
  • RS384 (RSA SHA384), requires a 3072-bit private key
  • RS512 (RSA SHA512), requires a 4096-bit private key

For example, to generate a 2048-bit RSA pair of keys, you can use the following commands. Generate a private key using this command:

openssl genrsa \
-out rsa-private-key.pem \
2048

Then, extract the public key from the private key as follows:

openssl rsa -in rsa-private-key.pem \
-pubout \
-outform PEM \
-out rsa-public-key.pem
Copy the public key file rsa-public-key.pem to the JWT Authorization add-on folder and configure its parameter signature.rsa.publicKeyPath accordingly.

PHP JWT token generator

You need to have installed PHP and composer.

  1. Create a PHP project and add the dependencies slim (for http requests) and jwt (for token generation) as follows:
composer init
composer require slim/slim:"3.*"
composer require lcobucci/jwt
  1. Create a source folder src in the current project and add the following PHP code to the file src/index.php:
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Hmac\Sha256 as HMAC;
use Lcobucci\JWT\Signer\Rsa\Sha256 as RSA;

require '../vendor/autoload.php';

$config = [
    'username' => 'admin',
    'password' => 'password',
    'key' => 'He39zDQW7RdkOcxe3L9qvoSQ/ef40BG6Ro4hrHDjE+U=',    /* HMAC */
    // 'key' => '../rsa-private-key.pem',                       /* RSA */
];

function isSubjectValid($subject) {
    return preg_match('/^\/([^\/]+\/)*([^\/]+|\*)$/', $subject);
}

function generateToken($permissions, $ttl, $key) {
    $config = Configuration::forSymmetricSigner(
        new HMAC(),
        // new RSA(),
        InMemory::plainText(base64_decode($key)),  /* HMAC */
		// InMemory::file($key),                   /* RSA */                         
    );

    $now   = new \DateTimeImmutable();
    $token = $config->builder()
                    // // Configures the issuer (iss claim)
                    ->issuedBy("http://example.com")
                    // // Configures the audience (aud claim)
                    ->permittedFor("ws://example.com")
                    // Configures the id (jti claim)
                    ->identifiedBy(uniqid('',TRUE))
                    // Configures the time that the token was issue (iat claim)
                    ->issuedAt($now)
                    // Configures the expiration time of the token (exp claim)
                    ->expiresAt($now->add(new DateInterval('PT' . $ttl . 'S')))
                    // Configures a new claim, called "uid"
                    ->withClaim('permissions', $permissions)
                    // Builds a new token
                    ->getToken($config->signer(), $config->signingKey());

    return $token->toString();
}

$app = new \Slim\App;

$app->post('/token/generate', function (Request $request, Response $response) use ($config) {
    $data = $request->getParsedBody();

    if ($data['username'] != $config['username'] || $data['password'] != $config['password']) {
        return $response->withStatus(400)->withHeader('Content-type', 'application/json')->write(json_encode(['response' => 'Invalid username or password!']));
    }

    if (!array_key_exists('permissions',$data) || count($data['permissions']) == 0) {
        return $response->withStatus(400)->withHeader('Content-type', 'application/json')->write(json_encode(['response' => 'Missing permissions for token!']));
    }
    
    foreach($data['permissions'] as $permission => $subjects) {
        foreach($data['permissions'][$permission] as $subject) {
            if (!isSubjectValid($subject)) {
                return $response->withStatus(400)->withHeader('Content-type', 'application/json')->write(json_encode(['response' =>'Error, invalid subject format for subject: ' . $subject]));
            }    
        }
    }

    return $response->withHeader('Content-type', 'application/json')->write(json_encode(['response' => generateToken($data['permissions'], $data['ttlSeconds'], $config['key'])]));
});

$app->run();
  1. Run the JWT token generator
php -S 0.0.0.0:8080 -t src
  1. Generate JWT tokens with publish and subscribe permissions for subjects

For example, run the following command to generate a JWT token valid one hour (i.e. 3600 seconds) with the following permissions:

  • the client is authorized to subscribe to the subject /sensor/temp
  • the client is authorized to publish to the subject /sensor/notification
  • the client is authorized to both subscribe to and publish on the subject /server/status
curl -X POST http://127.0.0.1:8080/token/generate \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"password", "ttlSeconds": 3600, "permissions": { "sub": ["/sensor/temp"], "pub": ["/sensor/notification"], "all" :["/server/status"]}}'