I'm trying to hook into eTrade's API for a project. They use an OAuth v1 flow. I'm running into error's on the very first step of the flow- getting the request token. The info from their doc's can be found here.
The error I'm getting is 401- invalid signature.
I've been reading up on the OAuth flow here and have become familiar with the process. In the past I've used PassportJS for 3rd party integrations.
So eTrade has provided me 4 keys- PROD_KEY, PROD_SECRET, SANDBOX_KEY, SANDBOX_SECRET. I'm using the sandbox keys for now as my production key is still pending.
Now I have an endpoint on my API that I'll call from my client to get the request tokens. It's structure like this:
router.route('/auth/request').get(controller.request);
And the following controller is where I'm generating a signature and trying to request the token from eTrade. That controller looks like this:
// controllers/auth.js
const axios = require('axios');
const { v1 } = require('uuid');
const crypto = require('crypto');
function generateSignature() {
const method = 'GET',
url = 'http://localhost/',
encodedUrl = encodeURIComponent(url),
paramaters = {
oauth_consumer_key: process.env.ETRADE_SANDBOX_API_KEY,
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: Math.floor(Date.now() / 1000),
oauth_nonce: v1(),
oauth_callback: 'oob'
}
var ordered = {};
Object.keys(paramaters).sort().forEach((key) => {
ordered[key] = paramaters[key];
});
var encodedParameters = '';
for(k in ordered) {
const encodedValue = escape(ordered[k]);
const encodedKey = encodeURIComponent(k);
if(encodedParameters === '') {
encodedParameters += encodeURIComponent(`${encodedKey}=${encodedValue}`);
} else {
encodedParameters += encodeURIComponent(`&${encodedKey}=${encodedValue}`);
}
}
encodedParameters = encodeURIComponent(encodedParameters);
const signature_base_string = `${method}&${encodedUrl}&${encodedParameters}`;
const signing_key = `${process.env.ETRADE_SANDBOX_SECRET_KEY}`;
const signature = crypto.createHmac("SHA1", signing_key).update(signature_base_string).digest().toString('base64');
const encodedSignature = encodeURIComponent(signature);
return encodedSignature;
}
module.exports = {
request: async (req, res) => {
console.log('Fetching request token from etrade...');
try {
var response = await axios.get(`https://api.etrade.com/oauth/request_token`, {
params: {
oauth_consumer_key: process.env.ETRADE_SANDBOX_API_KEY,
oauth_signature_method: 'HMAC-SHA1',
oauth_signature: generateSignature(),
oauth_timestamp: Math.floor(Date.now() / 1000),
oauth_nonce: v1(),
oauth_callback: 'oob'
}
});
console.log('Fetched request token...', response.data);
} catch (error) {
console.log('Could not fetch request token...', error.response.data);
}
}
}
I have followed some guides on the internet to help me understand the process of signing a request, but I can't seem to get a valid response back.