> ## Documentation Index
> Fetch the complete documentation index at: https://developer.utgl.io/llms.txt
> Use this file to discover all available pages before exploring further.

# How to authenticate with the Issuing API using PHP

## 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:

```bash theme={null}
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 theme={null}
<?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 theme={null}
<?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 theme={null}
<?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

<Warning>
  **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.
</Warning>

<Tip>
  **Token Expiration**: JWT tokens expire after 30 seconds. Generate a fresh token for each API request to avoid authentication errors.
</Tip>

## Next Steps

* See [Authentication](/issuing/getting-started/authentication) for more details on JWT signing
* Explore [API Endpoints](/api-reference) to see what you can do
* Check out [Request Signing](/issuing/getting-started/request-signing) for security best practices
