401/Unauthorized while doing REST vs Coinbase Advanced Trade API/Javascript

216 views Asked by At

I am querying coinbase advancedTrade using node/javascript, using their most recent v3 api. They recommend using axios, then implement an authentication scheme using jsonwebtoken.

I have created an api token on coinbase, with appropriate trading permissions (a trading token), then I store it on a USB. This creates an appropriate Bearer token, as far as I can see.

My definitions of secret and key names are ok, but I get a 401/Unauthorized. Here is my authentication routine and REST request, with an ensuing response.

Code I am using, doing authentication before RESTful call.

const axios = require('axios');
const crypto = require('crypto');
const fs = require('fs');

const data = fs.readFileSync('/Volumes/Extreme SSD/coinbaseSecurity/coinbase_cloud_api_key.json', 'utf8');
const json = JSON.parse(data);
const apiKey  = json.name;  //'organizations/{org_id}/apiKeys/{key_id}';
const secretKey = json.privateKey;  //'-----BEGIN EC PRIVATE KEY-----\nYOUR PRIVATE KEY\n-----END EC PRIVATE KEY-----\n';

const baseURL = 'https://api.coinbase.com/api/v3/brokerage/';

function signRequest(method, path, body) {
  const timestamp = Math.floor(Date.now() / 1000).toString();

  const message = timestamp + method + path + body;

  const hmac = crypto.createHmac('sha256', secretKey);

  hmac.update(message);

  const signature = hmac.digest('hex');

  return {
    'CB-ACCESS-KEY': apiKey,
    'CB-ACCESS-SIGN': signature,
    'CB-ACCESS-TIMESTAMP': timestamp,
  };
}

async function listAccounts() {
  try {
    const method = 'GET';
    const path = 'orders/historical/batch';
    const headers = signRequest(method, path, '');
    const response = await axios.get(baseURL + path, { headers });
    console.log(response.data);
  } catch (error) {
    console.error(error);
  }
}

Then I get my response, containing the 401 error, some of which I show here.

socketPath: undefined,
    method: 'GET',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    path: '/api/v3/brokerage/orders/historical/batch',
    _ended: true,
    res: IncomingMessage {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 4,
      _maxListeners: undefined,
      socket: [TLSSocket],
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      headers: [Object],
      rawHeaders: [Array],
      trailers: {},
      rawTrailers: [],
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 401,
      statusMessage: 'Unauthorized',
      client: [TLSSocket],
      _consuming: false,
      _dumped: false,
      req: [Circular *1],
      responseUrl: 'https://api.coinbase.com/api/v3/brokerage/orders/historical/batch',
      redirects: [],
      [Symbol(kCapture)]: false,
      [Symbol(RequestTimeout)]: undefined

UPDATED SOLUTION: This is getting a Bearer, but keeps getting a 401:

const axios = require('axios');
const jwt = require('jsonwebtoken');
const fs = require('fs');

const data = fs.readFileSync('/Volumes/Extreme SSD/coinbaseSecurity/coinbase_cloud_api_key.json', 'utf8');
const json = JSON.parse(data);
const key_name = json.name; //'organizations/{org_id}/apiKeys/{key_id}'
const key_secret = json.privateKey; //'-----BEGIN EC PRIVATE KEY-----\nYOUR PRIVATE KEY\n-----END EC PRIVATE KEY-----\n'
const uuid = `/${json.principal}`

const request_method = 'GET';
const request_host = 'https://api.coinbase.com';
const request_path = '/api/v3/brokerage/accounts';
const service_name = 'retail_rest_api_proxy';

const jwt_payload = {
  aud: [service_name],
  iss: 'coinbase-cloud',
  nbf: Math.floor(Date.now() / 1000),
  exp: Math.floor(Date.now() / 1000) + 10,
  sub: key_name,
  uri: request_method + ' ' + request_host + request_path+ uuid,
};

const jwt_header = {
  kid: key_name,
  nonce: Math.floor(Date.now() / 1000).toString(),
};

const token = jwt.sign(jwt_payload, key_secret, {
  algorithm: 'ES256',
  header: jwt_header,
});

const getAccounts = async () => {
  try {
    const response = await axios.get(request_host + request_path, {
      headers: {
        Authorization: 'Bearer ' + token,
      },
    });
    console.log(response.data);
  } catch (error) {
    console.error(error);
  }
};

getAccounts();
0

There are 0 answers