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();