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.
Related
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)
MRE -> node-server : react app
When I send a POST request using Postman, I get the expected result. This is the request that I am sending using Postman
and test sent gets printed to the console of my node server
If I send a request from my react form however, test sent does not print to the console, but the catch block of my fetch request get's executed and err is printed to the console of my react app, followed by {}.
I would like to know why my POST request is not working and is not getting received by the server
Below is the function that I call when someone clicks the submission button of my form created in react
Function called on form submission
nodeUrl = 'https://localhost:6060?'
const submitData = async () => {
fetch(nodeUrl, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({'test': 'test'})
}).then((res) => {
alert('then')
}).catch((err) => {
alert('err')
alert(JSON.stringify(err))
})
}
}
This is the server that I run using node server.js
server.js
server.post('/', function(req, res) {
console.log('test sent')
mailer.messages().send(req.body)
.then((mes) => {
console.log(mes)
res.json({ message: 'Thanks for your message. Our service team has been notified and will get back to you shortly.' })
}).catch(err => {
console.log(err)
res.json(err);
})
});
The majour issue here is due to CORS. CORS support can be used to overcome this. Just keep in mind to have this only for development mode(see below codes).
But, as per the Postman's snapshot and provided GitHub repositories, the request from Front-end should be of multipart/form-data type. Thus, the Front-end code would look like this
const nodeUrl = "http://localhost:6060/";
const submitData = async () => {
// create a FormData object
const formData = new FormData();
formData.append('form', 'example#email.com');
formData.append('to', 'example#email.com');
// this auto adds 'multipart/form-data' + HASH header in the request
fetch(nodeUrl, {
method: "POST",
body: formData
})
.then(res => {
console.log(res);
}).catch(err => {
console.log('Error -', err);
});
};
To handle multipart/form-data request in the ExpressJS, you need a plugin Multer.
const express = require('express');
const bodyParser = require('body-parser');
const multer = require('multer'); // for 'multipart' type request
const server = express();
const upload = multer();
// allow CORS requests in development mode
if (process.env.NODE_ENV === 'development') {
// Server run command - "NODE_ENV=development node server.js"
const cors = require('cors');
server.use(cors());
}
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({extended: true}));
// using Multer middleware form extracting 'FormData' payload
server.post('/', upload.none(), function(req, res) {
console.log('Received body', req.body);
... // other codes
});
Strategy 2(plain JSON) -
If that 'multipart/form-data' strategy was unintentional and you just want to send simple JSON, use below codes -
In Front-end, trigger API request as -
fetch(nodeUrl, {
method: "POST",
headers: {
'Content-Type': 'application/json', // this needs to be defined
},
body: JSON.stringify({ from: 'some#email.com', to: 'other#email.com' })
})
In server, just ignore codes related to Multer and only keep your API as -
server.post('/', function(req, res) {
console.log('Received body', req.body);
... // other codes
});
I ended up using a better fetch request, which was put together for me by selecting code -> Javascript Fetch in Postman(under the save button)
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("from", "example#email.com");
urlencoded.append("test", "test");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch("http:localhost:6060/, requestOptions)
.then(response => {
if (response.ok){
response.json().then(json => {
console.log(json)
})
}
})
.catch(error => console.log('error: ', error))
I saw many tutorials on jwt authentication but every video maker uses Postman to show what's happening and they pass on the header in the headers section while requesting a URL in Postman. I tried to do it with JavaScript but I was not able to do it.
I want to do jwt authentication but after token generation, I send it to client side to use it for further requests but I failed to do so after trying it a few times. I also tried to set req.headers in server side but it didn't do what I wanted to..
I want to set request headers for authentication of the form "Bearer {token}" for every request after token generation. How to do it with JS??
What I am most concerned about is that every tutorial does it with postman but they didn't show how they implemented it in their own app. I hope my question is clear.
You can easily add header on your http request like that
it has been solved here Node.JS: How to send headers with form data using request module
In vanilla nodejs:
const uri = "http://example.com";
const options = {
headers: {
"Authorization": "Bearer ...."
}
}
// require http/https lib
let req = require("http").request(uri, options, (res) => {
const chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.once("end", () => {
// concat body chunks
let body = Buffer.concat(chunks);
console.log(body.toString());
});
});
req.on("error", (err) => {
console.log(err);
});
req.end();
https://nodejs.org/dist/latest-v12.x/docs/api/http.html#http_http_request_options_callback
Something like that:
$.ajax({
url: url,
beforeSend: function(xhr) {
xhr.setRequestHeader("custom_header", "value");
},
success: function(data) {
}
});
First install jwt and express framework using npm then make a middleware file which will check if the tokek is set or not.
Middleware.js :
let jwt = require('jsonwebtoken');
const config = require('./config.js');
let checkToken = (req, res, next) => {
let token = req.headers['authorization']; // Express headers are auto converted to lowercase
if (token) {
if (token.startsWith('Bearer ')) { // Checks if it contains Bearer
// Remove Bearer from string
token = token.slice(7, token.length); //Separate Bearer and get token
}
jwt.verify(token, config.secret, (err, decoded) => { //Inser the token and verify it.
if (err) {
return res.json({
status: false,
message: 'Token is not valid'
});
} else {
req.decoded = decoded;
next();
}
});
} else {
return res.json({
status: false,
message: 'Access denied! No token provided.'
});
}
};
Next, create a config file which will contain the secrets.
Config js:
module.exports = {
secret: 'worldisfullofdevelopers'
};
Finally, create a token route which will create your token and after that the rest of the calls will be authenticated for that token.
Index.js :
const middleware = require('./middleware');
const jwt = require("jsonwebtoken");
const config = require('./config.js');
//Call token Route
app.use('/token', (req, res, next) => {
//Generate Token
let token = jwt.sign({ username: "test" },
config.secret,
{
expiresIn: '1h' // expires in 1 hours
}
);
//Send Token
res.json({
success: true,
message: 'Authentication successful!',
token: token
});
});
//Add Authentication to all routes
app.use(middleware.checkToken);
//===> All the routes after middleware will be checked for token
app.use('/getUser', (req, res, next) => {;
console.log('do something')
});
If I understand correctly, you want to set the HTTP header on the client, in order to pass an authentication token to the server. I would recommend that you use a library like **axios*.
Using axios, once you receive the toke, set the header for every outgoing communication with the following line of code:
axios.defaults.headers.common['Authorization'] = "Bearer " + token;
This will set the authentication http header to the form you need.
I'm getting a TypeError: Failed to fetch error when I attempt to send a post request using fetch on the front-end and an express route on the back-end.
I'm able to successfully create the new user in the db, but when attempting to obtain that new user data through the fetch promise, that's when the error is being thrown.
app.js
function createNewUser() {
let formUsername = document.getElementById('signup-username').value;
let formEmail = document.getElementById('signup-email').value;
let formPassword = document.getElementById('signup-password').value;
let url = "/users";
let newUserData = {
username: formUsername,
email: formEmail,
password: formPassword
}
fetch(url, {
method: 'POST',
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer',
body: JSON.stringify(newUserData),
}).then(res => res.json())
.then(response => console.log('Success: ', JSON.stringify(response)))
.catch(error => console.error('Error: ', error));
}
users.js
router.post('/users', function(req, res) {
User.create(req.body)
.then(function(user) {
res.json({
user: user
})
}
});
server.js
const express = require('express');
const app = express();
const fs = require('fs');
const path = require('path');
const bodyParser = require('body-parser');
const bcrypt = require('bcryptjs');
const auth = require('./auth');
const router = require('./routes/routes.js');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(router);
app.use('/', express.static(path.join(__dirname, 'public')));
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Methods",
"OPTIONS, GET, POST, PUT, PATCH, DELETE" // what matters here is that OPTIONS is present
);
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization", "Access-Control-Allow-Origin");
next();
});
app.listen(3000, function(){
console.log("Listening on port 3000");
});
I need to get that user object back in order to access its data.
Edit:
So, I've figured out that the issue has to do with how the request is submitted on the front-end. If I create the following function and then call it when app.js is loaded, then everything works:
function createNewUserTest() {
let formUsername = 'dd';
let formEmail = 'd#d.com';
let formPassword = 'secrete';
let url = "/api/users";
let newUserData = {
username: formUsername,
email: formEmail,
password: formPassword
}
fetch(url, {
method: 'POST',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newUserData),
})
.then(res => res.json())
.then(response => console.log('Success: ', response))
.catch(error => console.error('Error: ', error));
}
createNewUserTest();
But, if I try to call this function either through onsubmit in the form or onclick on the button in the html, or if I use an event listener (see below, which is in app.js), then I get the TypeError: Failed to fetch error:
let signupSubmitButton = document.getElementById('signup-submit');
signupSubmitButton.addEventListener('click', createNewUserTest);
This is even more baffling to me. I'm required to use Vanilla JS and I need to create the user through a form submission, but not sure what I need to adjust here.
Solution
Foiled by the event.preventDefault() again. This was all I needed.
let signupForm = document.getElementById('signup-form');
signupForm.addEventListener('submit', function(event) {
event.preventDefault();
let formUsername = document.getElementById('signup-username').value;
let formEmail = document.getElementById('signup-email').value;
let formPassword = document.getElementById('signup-password').value;
let url = "/api/users";
let newUserData = {
username: formUsername,
email: formEmail,
password: formPassword
}
fetch(url, {
method: 'POST',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newUserData),
})
.then(res => res.json())
.then(response => console.log('Success: ', response))
.catch(error => console.error('Error: ', error));
});
The issue was that the page was reloading, which kept me from getting the data back in time. The solution was to simply add event.preventDefault() inside the listener.
app.js
let signupForm = document.getElementById('signup-form');
signupForm.addEventListener('submit', function(event) {
event.preventDefault();
let formUsername = document.getElementById('signup-username').value;
let formEmail = document.getElementById('signup-email').value;
let formPassword = document.getElementById('signup-password').value;
let url = "/api/users";
let newUserData = {
username: formUsername,
email: formEmail,
password: formPassword
}
fetch(url, {
method: 'POST',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newUserData),
})
.then(res => res.json())
.then(response => console.log('Success: ', response))
.catch(error => console.error('Error: ', error));
});
The question is about "TypeError failed to fetch". The wording of the message sends one in the direction of network/server/CORS type issues as explored in other answers, but there is one cause I have discovered that is completely different.
I had this problem and took it at face value for some time, especially puzzled because it was provoked by my page POSTing in Chrome but not in Firefox.
It was only after I discovered chrome://net-internals/#events and saw that my request suffered from 'delegate_blocked_by = "Opening Files"' that I finally had a clue.
My request was POSTing a file uploaded from the user's computer via a file input element. This file happened to be a file open in Excel. Although it POSTed fine from Firefox, it was only when closed that it could be posted in Chrome.
Users of your web application need to be advised about this potential issue, and web developers should also be aware that "TypeError failed to fetch" can sometimes mean "TypeError didn't get as far as trying to fetch"
When it comes to CORS problems it's very often because the server doesn't know how to handle it properly. Basically every time you include a header like access-control-origin to your request it will instigate OPTIONS preflight request as well and if your server is not expecting that it will throw an error, because it was expecting a POST only requests.
In other words - try again without the "Access-Control-Origin": "*" part and see if it works or just try patching it on the server with something like this:
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Methods",
"OPTIONS, GET, POST, PUT, PATCH, DELETE" // what matters here is that OPTIONS is present
);
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
next();
});
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.