Overview
This tutorial shows you how to authenticate to the Issuing API using PHP by generating signed JSON Web Tokens (JWTs).
Prerequisites
- PHP 7.4 or higher
- Composer for dependency management
- Your Issuing API credentials (Access Key and RSA key pair)
Install required dependencies:
composer require firebase/php-jwt
composer require phpseclib/phpseclib
composer require guzzlehttp/guzzle
Step 1: Create signJWT function
Create a function to handle JWT signing with SHA256 body hashing:
<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
use phpseclib3\Crypt\RSA;
use GuzzleHttp\Client;
function signJWT($privateKeyFilePath, $accessKey, $uri, $method, $requestBody) {
// Decode the private key from base64
$privateKeyData = file_get_contents($privateKeyFilePath);
// Load private key using phpseclib
$rsa = RSA::loadFormat('PKCS8', $privateKeyData);
// Calculate hashed body using SHA256
$hashedBody = hash('sha256', $requestBody);
$now = new DateTime();
$issuedAt = $now->getTimestamp();
$expiration = $now->getTimestamp() + 30;
$payload = array(
"typ" => "JWT",
"sub" => $accessKey,
"exp" => $expiration,
"iat" => $issuedAt,
"uri" => $uri,
"method" => $method,
"body" => $hashedBody
);
// Sign the payload using RSA private key
$jwt = JWT::encode($payload, $rsa, 'RS256');
return $jwt;
}
Important Notes:
- Request body is hashed with SHA256 before signing
- 30 second expiration for security
- Private key format: PKCS8 format from file
Step 2: Usage - GET Request
To use the signJWT function when making a GET request to /v1/ping:
<?php
// use composer to manage the dependencies
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$privateKey = "./utgl-access.private"; // Replace with your private key
$accessKey = "6e33a078-99ed-4aa1-8e67-b0e19e9475fd"; // Replace with your access key
$uri = "/v1/ping";
$method = "GET"; // Change to "POST" if it's a POST endpoint
$requestBody = ""; // Fill with the request body for POST endpoint
$jwtToken = signJWT($privateKey, $accessKey, $uri, $method, $requestBody);
$apiUrl = "https://sandbox.access.utgl.io/v1/ping";
try {
$response = $client->request($method, $apiUrl, [
'headers' => [
'Authorization' => 'Bearer ' . $jwtToken,
'Content-Type' => 'application/json'
]
]);
$statusCode = $response->getStatusCode();
$responseBody = $response->getBody();
echo "$method $uri $statusCode\n";
} catch (Exception $e) {
echo "An error occurred: " . $e->getMessage();
}
Step 3: Usage - POST Request
To use the signJWT function when making a POST request to /v1/ping:
<?php
// use composer to manage the dependencies
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$privateKey = "./utgl-access.private"; // Replace with your private key
$accessKey = "6e33a078-99ed-4aa1-8e67-b0e19e9475fd"; // Replace with your access key
// Usage example - POST
$uri = "/v1/ping";
$method = "POST"; // Change to "POST" if it's a POST endpoint
$requestBody = json_encode([]); // Fill with the request body for POST endpoint
$jwtToken = signJWT($privateKey, $accessKey, $uri, $method, $requestBody);
$apiUrl = "https://sandbox.access.utgl.io/v1/ping";
try {
$response = $client->request($method, $apiUrl, [
'headers' => [
'Authorization' => 'Bearer ' . $jwtToken,
'Content-Type' => 'application/json'
],
'body' => $requestBody
]);
$statusCode = $response->getStatusCode();
$responseBody = $response->getBody();
echo "$method $uri $statusCode\n";
} catch (Exception $e) {
echo "An error occurred: " . $e->getMessage();
}
Common Pitfalls
Body Hashing is Critical! The request body must be hashed with SHA256 before being included in the JWT claims. Make sure you pass the exact same request body to both signJWT() and your HTTP client.
Token Expiration: JWT tokens expire after 30 seconds. Generate a fresh token for each API request to avoid authentication errors.
Next Steps