Search code examples
phpjwtcoinbase-api

Coinbase: Authenticating with JWT in PHP


I'm trying to use the advanced trade API from Coinbase in PHP. They decided to go with JWT but didn't give an example in PHP.

So I tried this code of mine but it is returning NULL. I have checked many times the API key. The details are correct. Here is the link to the Coinbase doc: https://docs.cloud.coinbase.com/advanced-trade-api/docs/rest-api-auth

use Firebase\JWT\JWT;

$keyName = 'organizations/b0f974e{redacted}860/apiKeys/d840f8{redacted}b6d';
$keySecret = "-----BEGIN EC PRIVATE KEY-----\nMHcC{redacted}bHEzMVwDhg==\n-----END EC PRIVATE KEY-----\n";

$requestMethod = "GET";
$requestHost = "api.coinbase.com";
$requestPath = "/api/v3/brokerage/accounts";

function buildJwt($uri, $keyName, $keySecret) {
    $privateKeyResource = openssl_pkey_get_private($keySecret);
    if (!$privateKeyResource) {
        throw new Exception('Private key could not be parsed or is invalid.');
    }
    $payload = [
        'sub' => $keyName,
        'iss' => 'coinbase-cloud',
        'iat' => time(),
        'exp' => time() + 120,  // Token expiration time (example: 120 seconds from now)
        'uri' => $uri,
    ];
    $jwt = JWT::encode($payload, $privateKeyResource, 'ES256');
    return $jwt;
}

function main($requestMethod, $requestHost, $requestPath, $keyName, $keySecret) {
    $uri = "$requestMethod $requestHost$requestPath";
    $jwtToken = buildJwt($uri, $keyName, $keySecret);
    return $jwtToken;
}

$myjwt = main($requestMethod, $requestHost, $requestPath, $keyName, $keySecret);

function getAccountDetails($jwt, $requestMethod, $requestHost, $requestPath) {
    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => "https://$requestHost$requestPath",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => '',
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => $requestMethod,
        CURLOPT_HTTPHEADER => [
            "Authorization: Bearer $jwt",
            "Content-Type: application/json"
        ],
    ]);
    $response = curl_exec($curl);
    $err = curl_error($curl);
    curl_close($curl);
    if ($err) {
        echo "cURL Error #:" . $err;
    } else {
        return json_decode($response, true);
    }
}

$res = getAccountDetails($myjwt, $requestMethod, $requestHost, $requestPath);
var_dump($res);

Solution

  • I too had this error and this was how I solved it. There really isn't much documentation or resources online on how to do this via PHP so hopefully this can help others.

    I essentially did this by converting the provided python example code from coinbase into php, and then tweaked it until it worked. It was a very long convoluted testing process so I wont note it all down here but for any other PHP conversions it is best to start with a working example in another language and recreate it best you can in php. If that fails, chatGPT.

    This method will generate a JWT for any GET request. To create one for POST requests you need to change the $request_method variable.

    A new JWT token needs to be created for each unique request.

    <?php 
    
    require '../../vendor/autoload.php';
    
    use \Firebase\JWT\JWT;
    
    function createMyJWT($request_path)
    {
        $key_name = 'organizations/9xxxxxxa-93ce-4f50-bee9-4xxxxxxxxxxd/apiKeys/2xxxxxxx0-5aea-47c8-9445-cxxxxxxxxxxf';
    $key_secret = "-----BEGIN EC PRIVATE KEY-----\nMxxxxxxxxxxxxxxxxxxxxxxxxxl4/NxxxxxxxxxS/rtxxxxxxxxxxxxxxxxxxxxxxxxM49\nAxxxxxxxxxQgAEc2/6Bxxxxxxxl+oxxxxxxxxxxhc/SxxxxxxxxxxxxxxxxxxxxxxY\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx9A==\n-----END EC PRIVATE KEY-----\n";
    $request_method = 'GET';
    $url = 'api.coinbase.com';
    
    $algorithm = 'ES256';
    $uri = $request_method . ' ' . $url . $request_path;
    
    $token = JWT::encode(
        [
            'iss' => 'coinbase-cloud',
            'nbf' => time(),
            'exp' => time() + 120,
            'sub' => $key_name,
            'uri' => $uri
        ],
        $key_secret,
        $algorithm,
        null,
        [
            'kid' => $key_name,
            'nonce' => bin2hex(random_bytes(16))
        ]
    );
    return $token;
    }
    
    ?>