aws signature v4 InvalidSignatureException - javascript

I am trying to trying to make a request to apigateway use the signature v4. No matter what I do it seems to fail. I have an endpoint in apigateway that just echoes back the request that's sent to it. I am also doing this in a react application. All the examples of how to use this #aws-sdk/signature-v4 library are using node. Which may be the problem but I dont know. I am able to generate a signature but I get a 403 InvalidSignatureException every time. This endpoint does work when I use postman and use their built in aws signature feature. One thing I noticed is the signedHeaders postman gives me back has host in the string. I dont get that when I create the signedHeaders myself.
import { SignatureV4 } from "#aws-sdk/signature-v4";
import { HttpRequest } from "#aws-sdk/protocol-http";
import { HttpRequest as IHttpRequest } from "#aws-sdk/types";
import { Sha256 } from "#aws-crypto/sha256-js";
interface CreateSignHttpRequestParams {
body?: string;
headers?: Record<string, string>;
hostname: string;
method?: string;
path?: string;
protocol?: string;
query?: Record<string, string>;
service: string;
}
export async function createSignedHttpRequest({
body,
headers,
hostname,
method = "POST",
path = "/",
protocol = "https:",
query,
service,
}: CreateSignHttpRequestParams): Promise<IHttpRequest> {
const httpRequest = new HttpRequest({
body,
headers,
hostname,
method,
path,
protocol,
query,
});
const signer = new SignatureV4({
applyChecksum: false,
credentials: {
accessKeyId: 'XXX',
secretAccessKey: 'XXX',
sessionToken: 'XXX',
},
region: 'XXX',
service,
sha256: Sha256
});
return signer.sign(({
method: 'POST',
protocol: 'https',
hostname: `https://XXX.execute-api.XXX.amazonaws.com/STAGENAME/`,
path: '/',
query: {},
headers: {},
}));
}
const results = createSignedHttpRequest({hostname: 'https://XXX.execute-api.XXX.amazonaws.com/STAGENAME', service: 'execute-api'}).then( async response => {
var myHeaders = new Headers();
myHeaders.append("X-Amz-Security-Token", response.headers['x-amz-security-token']);
myHeaders.append("X-Amz-Date", response.headers['x-amz-date']);
myHeaders.append("Authorization", response.headers.authorization);
var requestOptions = {
method: 'POST',
headers: myHeaders
};
fetch("https://XXX.execute-api.XXX.amazonaws.com/STAGENAME/", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
})

Related

How to sign API Gateway URL that has a query string

I am trying to use SignatureV4 to sign a request for an API Gateway endpoint that uses IAM Authorizor. An issue is that I keep getting a 403 error whenever I append a query string to my URL, i.e. /pets?type=1. Everything works fine when a query string is not included, i.e. /pets
This is how I build a request:
const region = 'xxx'
const method = 'GET'
const protocol = 'https:'
const host = `xxx.execute-api.${region}.amazonaws.com`
const path = '/dev/pets'
const query = {
type: 1,
}
const request = new HttpRequest({
method: method,
protocol: protocol,
hostname: host,
path: path,
query: query,
headers: {
'Content-Type': 'application/json',
host: host ,
},
})
const signer = new SignatureV4({
credentials: AWS.config.credentials,
service: 'execute-api',
region: region,
sha256: Sha256,
})
const { headers } = await signer.sign(request)
const response = await fetch(`${protocol}//${host}${path}?type=1`, {
headers,
method
}).then((res) => res.json())
I've tried running the same query within Postman and it worked just fine. So, I have to assume that an issue is with my implementation.
An issue was due to getCanonicalQuery ignoring values that are not string or array:
https://github.com/aws/aws-sdk-js-v3/blob/main/packages/signature-v4/src/getCanonicalQuery.ts#L19
So, to fix this I had to swap my query to below:
const query = {
type: '1',
}

Request for an API with Axios - Unauthorized

I'm trying to make the request for an API, using Axios:
const axios = require ("axios")
const httpsAgent = require('https-agent')
const https = require('https')
const instance = axios ({
httpsAgent: new https.Agent({
rejectUnauthorized: false
}),
auth: {
username: 'username'
}
})
axios.post("url_api").then(function(response){
console.log(response.data)
}).then(function(response){
console.log(response.data)
}).catch((e)=>{console.log(e)})
but it displays error 401:
TypeError [ERR_INVALID_ARG_TYPE]: The "url" argument must be of type string
response: {
status: 401,
statusText: 'Unauthorized',
...
},
data: 'Unauthorized'
},
isAxiosError: true,
toJSON: [Function: toJSON]
}
Is there any more configuration to do? Insomnia/Postman works
The code you've here
const instance = axios ({
httpsAgent: new https.Agent({
rejectUnauthorized: false
}),
auth: {
username: 'username'
}
})
It's already equivalent to initiating a request, but the problem is you've not passed the url and method parameter which is mandatory
So modify it to
const request = axios ({
httpsAgent: new https.Agent({
rejectUnauthorized: false
}),
method: 'post',
url: 'your_api_url_here', // important change
auth: {
username: 'username'
}
})
Or you can simple follow and do
axios.post('url_here', data );
Finally, your code must look like this
const instance = axios({
httpsAgent: new https.Agent({
rejectUnauthorized: false
}),
auth: {
username: 'username'
},
method: 'post',
url: 'your_api_url_here',
})
.then(response => console.log(response.data))
.catch((e) => console.log(e));
Choose either one of them but not both.
axios.post("url_api",body,header)

How to Fix Coinbase Pro API Request Headers?

I am trying to code to execute orders using Coinbase Pro API according to the Documentation provided. However, I got an error like this.
Access to XMLHttpRequest at 'https://api.pro.coinbase.com/orders' from origin 'http://localhost:8000' has been blocked by CORS policy: Request header field cb-access-key is not allowed by Access-Control-Allow-Headers in preflight response.
And this is the code that I wrote.
var vm = this;
var coinbasePro = {
passphrase: 'xxxxxxxxx',
key: 'xxxxxxxxxxxxxxxxxxxxxxx',
secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==',
apiURI: 'https://api.pro.coinbase.com',
};
var dataRequest = {
url: '/orders',
method: 'POST',
timestamp: Date.now() / 1000,
};
var dataBody = JSON.stringify({
price: '1.0',
size: '1.0',
side: 'buy',
product_id: 'BTC-USD'
});
var what = vm.dataRequest.timestamp + vm.dataRequest.method + vm.dataRequest.url + dataBody;
var key = Buffer.from(vm.coinbasePro.secret, 'base64');
var hmac = cryptoJs.createHmac('sha256', key);
var sign = hmac.update(what).digest('base64');
vm.$http({
url: vm.coinbasePro.apiURI+vm.dataRequest.url,
method: vm.dataRequest.method,
headers: {
'Accept': 'application/json',
'CB-ACCESS-KEY': vm.coinbasePro.key,
'CB-ACCESS-SIGN': sign,
'CB-ACCESS-PASSPHRASE': vm.coinbasePro.passphrase,
'CB-ACCESS-TIMESTAMP': vm.dataRequest.timestamp,
},
}).then((res) => {
console.log(res);
}).catch((err) => {
});
I have tried different ways to get things going and applied some of the references I have come across. Thank you for the help.
Their API does support CORS, however it is misconfigured and does not permit the security headers that they require you to use! You can work around this by running an express proxy with middleware to re-write the headers:
import express from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware'
const app = express()
app.use(express.static('client'))
const apiProxy = createProxyMiddleware({
target: 'https://api.pro.coinbase.com',
changeOrigin: true,
onProxyRes: res => {
res.headers = {
...res.headers,
'access-control-allow-headers':
'Content-Type, cb-access-key, cb-access-sign, cb-access-timestamp, cb-access-passphrase',
}
},
})
app.use('/', apiProxy)
app.listen(3001)

Nodejs .Unable to send oauth v1 params in get request with axios

I wanted to make a request to ADP with autho1.0a
I was able to make successful requests as I wanted in postman but not through my application.
postman screenshot
npm module used
similar post
Code I tried
Part:1 Signature generation
const crypto = require('crypto')
const OAuth = require('oauth-1.0a')
const oauthObj = {};
function hash_function_sha1(base_string, key) {
return crypto
.createHmac('sha1', key)
.update(base_string)
.digest('base64')
}
oauthObj.getSignature = async payload => {
const { consumerKey,consumerSecret,apiUrl,method} = payload;
const oauth = OAuth({
consumer: { key: `${consumerKey}`, secret: `${consumerSecret}` },
signature_method: 'HMAC-SHA1',
hash_function: hash_function_sha1,
});
const request_data = {
url: `${apiUrl}`,
method: `${method}`
}
const token = {}
// return oauth.toHeader(oauth.authorize(request_data, token));
console.log('header string-----',oauth.toHeader(oauth.authorize(request_data, token)));
return oauth.authorize(request_data, token);
}
module.exports = oauthObj;
Part 2 : Axios Call
let oauthData=`oauth_consumer_key=${consumerKey}&oauth_signature_method=HMAC-SHA1&oauth_timestamp=${oauthTimestamp}&oauth_nonce=${oauthNonce}&oauth_version=1.0&oauth_signature=${oauthSignature}= HTTP/1.1`;
const eventData = await axios({
url:`${apiUrl}?${oauthData}`,
// url:`${apiUrl}?${oauthHeader.Authorization}`,
method:'GET',
headers:{
// ...oauthHeader,
'Authorization':'OAuth',
'Accept': 'application/json',
// "Authorization": `'OAuth oauth_consumer_key="${consumerKey}", oauth_nonce="${oauthNonce}", oauth_signature="${oauthSignature}", oauth_signature_method="HMAC-SHA1", oauth_timestamp="${oauthTimestamp}", oauth_version="1.0"`
}
});
Expected Result:
{
"code": "Gone",
"message": "Event with token 954c183f-26e0-4f9e-b452-c089aaf9842f has already been consumed."
}
Receiving error:
response: {
status: 401,
statusText: 'Unauthorized',
headers: {
What might have gone wrong ?
Try using request node package oauth option
request.get(`${apiUrl}?${oauthData}`, {
oauth: {
consumer_key: '..',
consumer_secret: '..',
},
headers: {
Accept: 'application/json'
},
}, function (err, res, body) {
console.log(body);
})

Getting access token with axios

I'm working with the Lyft API, and trying to figure out how to get an access token with axios with a node script.
I can manually get an access token by using Postman by filling out the form like this:
When I fill out the form, I can get a new token from Lyft successfully.
I'm trying to translate this into a POST request using axios by doing this:
var axios = require('axios');
var data = {
"grant_type": "client_credentials",
"scope": "public",
"client_id": "XXXXXXXXX",
"client_secret": "XXXXXXXX"
};
var url = "https://api.lyft.com/oauth/token";
return axios.post(url, data)
.then(function(response){
console.log(response.data)
})
.catch(function (error) {
console.log(error);
});
When I run the script, I get this error:
{ error_description: 'Unauthorized', error: 'invalid_client' }
What am I missing from my axios request? Any help would be appreciated!
According to the docs from Lyft (https://developer.lyft.com/docs/authentication), you need to use HTTP Basic auth.
var axios = require("axios");
axios.request({
url: "/oauth/token",
method: "post",
baseURL: "https://api.lyft.com/",
auth: {
username: "vaf7vX0LpsL5",
password: "pVEosNa5TuK2x7UBG_ZlONonDsgJc3L1"
},
data: {
"grant_type": "client_credentials",
"scope": "public"
}
}).then(function(res) {
console.log(res);
});
Happy coding :)
!IMPORTANT THING!
I strongly recommend you to change your secret_id and client_secret asap, because they are not the things to be public, if you use them for an important project or something like that.
I have solved my problem with this code.
var reqData = "grant_type=password&username=test&password=asd";
Axios({
method: 'post',
url: 'http://localhost:60439/token',
data: (reqData),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
}
}).then((response) =>{
console.log(response)
}).catch((error) =>{
console.log(error);
})
The Best solution was source using the following way. The client sends a POST request with following body parameters to the authorization server
grant_type with the value client_credentials
client_id with the the client’s ID
client_secret with the client’s secret
scope with a space-delimited list of requested scope permissions.
axios.post('https://exmaple.com/oauth/token',
'grant_type=client_credentials&scope=all&client_id=1&client_secret=bb'
)
.then(function(res) {
console.log(res);
})
.catch(error => {
console.log(error)
})
const axios = require("axios");
const qs = require("qs");
const url = "URL";
const data = {
grant_type: "client_credentials",
};
const auth = {
username: "Client ID",
password: "Client Secret",
};
const options = {
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
data: qs.stringify(data),
auth: auth,
url,
};
axios(options)
.then((response) => {
console.log(response.data.access_token);
})
.catch((err) => {
console.log(err);
});
The following works. I got it by reading the above comments. The trick was the data field. To be clear use - data: "grant_type=client_credentials"
Example:
const axios = require("axios");
axios.request({
headers:{'Content-Type': 'application/x-www-form-urlencoded'},
url: "/oauth2/token",
method: "post",
baseURL: "https://<ServerFQDN>/",
data: "grant_type=client_credentials",
auth: {
username: "<username>",
password: "<password>"
}
});

Categories

Resources