How to Fix Coinbase Pro API Request Headers? - javascript

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)

Related

Cross-Origin Request Blocked using NodeJS/Coinbase API

I'm trying to get Coinbase to work using NodeJS
I get the following error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:3000/create-payment. (Reason: CORS request did not succeed). Status code: (null).
I've tried downloading one of those Corse plugins that should allow it but I just can't get it to work.
Javascript code:
<script> const button = document.getElementById('order');
button.addEventListener('click', event => { const form = document.getElementById('payment-form');
event.preventDefault(); // Prevent the form from submitting to the server
form.submit();
const price = "<?php echo $price; ?>";
const amount = document.getElementById('quantitymills').value;
const total_price = document.getElementById('inputd').value;
const fname = document.getElementById('fname').value;
const email = document.getElementById('email').value;
fetch('http://localhost:3000/create-payment', {
method: 'POST',
body: JSON.stringify({ price }),
body: JSON.stringify({ amount }),
body: JSON.stringify({ total_price }),
body: JSON.stringify({ name }),
body: JSON.stringify({ email }),
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(payment => {
// Do something with the payment details
console.log(payment);
})
.catch(error => console.error(error)); });
Nodejs code:
const express = require('express');
const axios = require('axios');
const bodyParser = require('body-parser');
const mysql = require('mysql2');
const app = express();
// Parse application/x-www-form-urlencoded requests app.use(bodyParser.urlencoded({ extended: false }));
// Connect to the database
const db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'coinbase' });
// Create a payment
app.post('http://localhost:3000/create-payment', async (req, res) => { try {
// Make an API request to create the payment
const response = await axios.post(
'https://api.commerce.coinbase.com/charges',
{
name: 'Product one',
description: 'Product one',
local_price: {
amount: req.body.total_price,
currency: 'USD' },
metadata: {},
pricing_type: 'fixed_price',
redirect_url: 'http://localhost/index.php'
},
{
headers:
{ 'X-CC-Api-Key': 'myapikey' } }
);
// Store the payment URL in the database
const payment = {
url: response.data.hosted_url,
amount: req.body.total_price,
currency: 'USD',
status: 'pending'
};
db.query('INSERT INTO payments SET ?', payment, (error, result) => {
if (error) throw error;
res.send(payment);
});
} catch (error) { console.error(error); } });
app.listen(3000, () => { console.log('Server listening on port 3000'); });
It might be something else wrong in my code, I am still new to coding and could have messed up in another area not quite sure.
Any help would be greatly appreciated!
Downloading cors plugin
tried 127.0.0.1 instead of localhost
tried switching ports
Try it like this:
npm install --save cors
Then inside your code put
const cors = require('cors');
at the begining and this before the row with app.listen(3000....)
app.use(cors({
origin: '*'
}));
This will allow request from all domains to your backend.
To authorize only your domain (your browser requests) substitute the "star" after origin with the domain (even localhost:port) where your frontend is running.
Hint: you should create environments variable for the domain names. So if you deploy it to a production site you can dynamically substitute them.

CORS Policy Error when attempting to POST to Twilio API

From a VueJS application I'm attempting to do a simple POST to the Twilio API in order to send an SMS. When the POST is executed I receive the following error:
"Access to XMLHttpRequest at 'https://api.twilio.com/2010-04-01/Accounts/AC42exxxxxxxxxxcfa9c48/SMS/Messages' from origin 'http://localhost:8080' has been blocked by CORS policy: Request header field username is not allowed by Access-Control-Allow-Headers in preflight response."
The offending code is the following:
sendTwilio(){
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const sFromNumber = process.env.TWILIO_NUMBER;
const sBaseURL = 'https://api.twilio.com';
const phoneNumber = parsePhoneNumberFromString(this.sms.to_number,'US')
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authToken}`,
'username': `${accountSid}`
},
sBodyText='Test'
this.SmsUrl = sBaseURL + '/2010-04-01/Accounts/' + accountSid + '/SMS/Messages';
if (phoneNumber.isValid()){
this.sms.formattedPhone = phoneNumber.number;
this.postData = 'From=' + sFromNumber
+ '+To=' + phoneNumber.number
+ '+Body=' + sBodyText
axios.post(`${this.SmsUrl}`, this.postData, {
headers: headers
})
.then((response) => {
console.log(response)
})
.catch((error) => {
console.log(error)
})
}
},
Is the problem with the format used for the username in the header or something with my CORS settings?
My CORS settings are as follows:
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = [
'http://localhost:8000',
'http://localhost:8080',
'http://127.0.0.1:8000'
]
Twilio uses Basic Auth to do authentication, so in your case when doing your POST using axios you need to do:
const headers = {
'Content-Type': 'application/json'
};
const auth = {
username: accountSid,
password: authToken
}
[...]
axios.post(this.SmsUrl, this.postData, {
auth: auth,
headers: headers
})
I'm not sure how you're using this though. Have a look at the comments of the question. You should never expose your Twilio credentials to the client in a browser application.

Node.js POST request with .p12 and pem certificates outputs "Error: read ECONNRESET"

I'm setting up a function using node on Firebase to test a Swish integration. Swish is a mobile application for money transfer between wallets (bank accounts). Currently Im testing their Merchant Swish Simulator test environment that is available for merchants to test their integration with the Swish Commerce API.
There is a available manual on their developer website: https://developer.getswish.se/merchants/
Example request that can be done through a unix terminal (to test one of their endpoints) which works perfectly for me (getting 201 respond) is:
curl -s -S -i --cert ./Swish_Merchant_TestCertificate_1231181189.p12:swish --cert-type p12 --cacert ./Swish_TLS_RootCA.pem --tlsv1.1 --header "Content-Type: application/json" https://mss.cpc.getswish.net/swish-cpcapi/api/v1/paymentrequests --data '{ "payeePaymentReference" : "0123456789", "callbackUrl" : "https://myfakehost.se/swishcallback.cfm", "payerAlias" : "46766268608", "payeeAlias" : "1231181189", "amount" : "100", "currency" : "SEK", "message" : "Kingston USB Flash Drive 8 GB" }'
The issue is im not sure of how to make a similar request with including the certs using firebase.
const functions = require('firebase-functions');
const express = require('express');
const app = express();
let fs = require('fs');
let request = require('request');
app.post('/request-payment', async (req, res) => {
const url = 'https://mss.cpc.getswish.net/swish-cpcapi/api/v1/paymentrequests';
let data = {
payeePaymentReference : '0123456789',
message: 'Kingston USB Flash Drive 8 GB',
callbackUrl: 'https://myfakehost.se/swishcallback.cfm',
amount: '100',
currency: 'SEK',
payeeAlias: '1231181189',
payerAlias: '46766268608'
};
let options = {
url: url,
headers: {
"content-type": "application/json",
},
agentOptions: {
pfx: fs.readFileSync('certificates/Swish_Merchant_TestCertificate_1231181189.p12'),
passphrase: 'swish',
},
ca: fs.readFileSync('certificates/Swish_TLS_RootCA.pem'),
body: data,
json: true
};
return await request.post(options, async (error, response, body) => {
if (!error && response.statusCode === 201) {
return res.status(200).json({
url: response.body.url,
})
} else {
console.log('error', error);
return res.status(404).json({
message: 'Error: ' + error,
})
}
});
});
exports.swish = functions.https.onRequest(app);
I've been trying to pass the .p12 and .pem files when making a request to the endpoint above and im sending the exact same data as above. I still get this error:
error { Error: read ECONNRESET at TLSWrap.onread (net.js:622:25) errno: 'ECONNRESET', code: 'ECONNRESET', syscall: 'read' }
Can anyone see anything wrong I do?
I've also tried to set a "binary" option to the .pem file. I do then get a crash from node saying that the header is to long....
Here is the way I populate options for request to make it work:
const options = {
url: "https://mss.cpc.getswish.net/swish-cpcapi/api/v1/paymentrequests",
json: true,
cert: fs.readFileSync("Swish_Merchant_TestCertificate_1231181189.pem", "ascii"),
passphrase: "swish",
body: data,
ca: fs.readFileSync("Swish_TLS_RootCA.pem", "ascii"),
key: fs.readFileSync("Swish_Merchant_TestCertificate_1231181189.key", "ascii")
};
Note the important differences:
"ascii" encoding to read the files
usage of the "pem" certificate instead of "p12".
Note that Swish has now released API v2. I got the payment initiation endpoint working like this:
import { readFileSync } from "fs"
import { Agent, AgentOptions } from "https"
import fetch from "node-fetch"
const url = `https://mss.cpc.getswish.net/swish-cpcapi/api/v2/paymentrequests/${randomUUID}`
const data = {
callbackUrl: "https://myfakehost.se/swishcallback.cfm",
amount: "100.00",
currency: "SEK",
payeeAlias: "1234679304",
payeePaymentReference: "foo"
}
const cert = {
cert: readFileSync(`./Swish_Merchant_TestCertificate_1234679304.pem`, "utf-8"),
key: readFileSync(`./Swish_Merchant_TestCertificate_1234679304.key`, "utf-8"),
passphrase: "swish",
}
const options = {
agent: new Agent({ ...cert, minVersion: "TLSv1.2", maxVersion: "TLSv1.2" }),
method: "PUT",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" }
}
fetch(url, options)
.then(handleToken)
.catch(handleError)

How can I send the serve response data from a fetch API to client?

Having a json object,I made an API call using fetch API and I got a response with the expected resuts. How can I send the results to the client, so as to be able to display them in the interface? I used express module from Node.js.
const fetch = require("node-fetch");
const express = require("express");
var bodyParser = require('body-parser');
const app = express();
app.post('/', (req, res) => {
res.sendStatus(200);
jsonBody = JSON.parse(fs.readFileSync("./resources/jsonBody.json", {
encoding: 'utf8'
}));
function results() {
fetch(url, {
method: 'POST',
body: JSON.stringify(jsonBody),
headers: {
"Authorization": "Bearer " + predictionKey,
"Content-Type": "application/json"
},
}).then(response => response.json()).then(response =>
console.log(response.Results.output1.value.Values));
}
results()
})
});
The client side post function:
function postMessage(message){
$.post("http://localhost:8000",message);
}
This is the response of the api call on the server
[ [ '1008', '0', '1' ],
[ '1079', '1', '3' ],
[ '1022', '2', '3' ] ]
and I want to display these values in the interface (on client)
Your variable url used in the fetch is not defined anywhere, so I have to assume you have a global variable elsewhere to the API you are calling.
If so, then you are doing this out of sequence - you are sending an empty 200 OK then fetching the jsonBody (which is another global var, maybe unintentionally) but then never using those results.
Try rearranging like so:
const fetch = require("node-fetch");
const express = require("express");
var bodyParser = require('body-parser');
const app = express();
app.post('/', (req, res) => {
res.sendStatus(200);
var jsonBody = JSON.parse(fs.readFileSync("./resources/jsonBody.json", {
encoding: 'utf8'
}));
fetch(url, {
method: 'POST',
body: JSON.stringify(jsonBody),
headers: {
"Authorization": "Bearer " + predictionKey,
"Content-Type": "application/json"
},
}).then(response => response.json()).then(response => {
console.log(response.Results.output1.value.Values);
res.json(response.Results.output1.value.Values); // or whatever you want from response
});
});

How to fix CORS error when posting form from client to AWS API gateway function?

I have a contact form on my front end react application that I want to post to an Lambda function that is behind a API Gateway which in turn has a custom domain on top of it.
My front end runs on domain dev.example.com:3000
My API Gateway is on contact.example.com
Further more, I have created my Lambda function with serverless and in my YAML file, have enabled CORS as so:
# serverless.yml
service: contact-form-api
custom:
secrets: ${file(secrets.json)}
provider:
name: aws
runtime: nodejs8.10
stage: ${self:custom.secrets.NODE_ENV}
region: us-east-1
environment:
NODE_ENV: ${self:custom.secrets.NODE_ENV}
EMAIL: ${self:custom.secrets.EMAIL}
DOMAIN: ${self:custom.secrets.DOMAIN}
iamRoleStatements:
- Effect: "Allow"
Action:
- "ses:SendEmail"
Resource: "*"
functions:
send:
handler: handler.send
events:
- http:
path: email/send
method: post
cors: true
I am using AXIOS to make my post request which happens client side:
const data = await axios.post(
"https://contact.example.com/email/send",
formData,
{
"Content-Type": "application/json",
}
)
And the error I get is:
Access to XMLHttpRequest at 'https://contact.example.com/email/send' from origin 'http://dev.example.com:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains the invalid value 'example.com'.
I would have thought having the front end and API on the same domain would get around any cors errors (although I am spoofing dev.example.com) locally in order to test). I would also think the cors setting in my YAML file would get around it.
Anybody know why I might still be getting this CORS error?
Edit: Showing handler code that runs in Lambda function
// handler.js
const aws = require('aws-sdk')
const ses = new aws.SES()
const myEmail = process.env.EMAIL
const myDomain = process.env.DOMAIN
function generateResponse (code, payload) {
return {
statusCode: code,
headers: {
'Access-Control-Allow-Origin': "*",
'Access-Control-Allow-Headers': 'x-requested-with',
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify(payload)
}
}
function generateError (code, err) {
console.log(err)
return {
statusCode: code,
headers: {
'Access-Control-Allow-Origin': "*",
'Access-Control-Allow-Headers': 'x-requested-with',
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify(err.message)
}
}
function generateEmailParams (body) {
const { email, name, content } = JSON.parse(body)
console.log(email, name, content)
if (!(email && name && content)) {
throw new Error('Missing parameters! Make sure to add parameters \'email\', \'name\', \'content\'.')
}
return {
Source: myEmail,
Destination: { ToAddresses: [myEmail] },
ReplyToAddresses: [email],
Message: {
Body: {
Text: {
Charset: 'UTF-8',
Data: `Message sent from email ${email} by ${name} \nContent: ${content}`
}
},
Subject: {
Charset: 'UTF-8',
Data: `You received a message from ${myDomain}!`
}
}
}
}
module.exports.send = async (event) => {
try {
const emailParams = generateEmailParams(event.body)
const data = await ses.sendEmail(emailParams).promise()
return generateResponse(200, data)
} catch (err) {
return generateError(500, err)
}
}
You should add an HTTP request header as 'Content-type':
Now, go to Integration requests, and change the mapping template as follows:
Hope, it helps. and don't forgot to deploy the API before testing.

Categories

Resources