How can I communicate with Cloudfront and the ec2 node server? - node.js

using stack
Client: React, Redux, axios
Server: AWS-EC2, Route 53, S3, CloudFront, NodeJS, express
First, I bought a domain from route53.(ACM certificate issuance completed)
Second, I registered the build file in the bucket as a static website in S3.
Third, linked the Route 53 and S3 bucket to CloudFront.
Fourth, EC2 set ELB and EIP.
Fifth, ec2 contains node.js epxress server.
Sixth, CloudFront, Redirect from S3 (www.domain.link => domain.link)
was set to
The code of the problematic Client and Server is as follows.
Client.js
import axios from "axios";
import { TYPES, MAF } from "./types";
const API_AUTH = "https://www.domain.link/auth";
const API_USER = "https://www.domain.link";
//필수!!
axios.defaults.withCredentials = true;
export function loggedIn(data) {
return (dispatch) => {
axios.post(`${API_AUTH}/login`, data).then((res) => {
console.log(res);
dispatch({
type: TYPES.LOGIN_SUCCESS,
// payload: res.data.userData,
});
dispatch({
type: MAF.HIDE_MODAL,
});
});
};
}
export function register(data) {
return (dispatch) => {
axios.post(`${API_AUTH}/register`, data).then((res) => {
dispatch({
type: TYPES.REGISTER_SUCCESS,
payload: res.data,
});
});
};
}
server.js
./routes/user.js
const router = require("express").Router();
const {
login,
register,
logout,
profile,
} = require("../controller/userController/userController");
const { authorization } = require("../config/JWTConfig");
router.post("/auth/login", login);
router.post("/auth/register", register);
router.get("/auth/logout", authorization, logout);
router.get("/auth/profile", authorization, profile);
module.exports = router;
./app.js
const express = require("express");
// const passportConfig = require("./passport/index");
const passport = require("passport");
const http = require("http");
const https = require("https");
const path = require("path");
const fs = require("fs");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const logger = require("morgan");
require("dotenv").config();
const authRoute = require("./routes/users");
const mainRoute = require("./routes/main");
const port = process.env.PORT || 3000;
const app = express();
const whitelist = [
"http://localhost:3000",
"http://*.doamin.link",
"http://doamin.link",
"http://doamin.link/*",
];
const corsOption = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1 || !origin) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
credentials: true,
methods: ["GET", "POST", "PUT", "DELETE", "OPTION"],
};
app.use(cookieParser());
app.use(logger("dev"));
app.use(cors(corsOption));
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(mainRoute);
app.use(authRoute);
let server;
if (fs.existsSync("./cert/key.pem") && fs.existsSync("./cert/cert.pem")) {
const privateKey = fs.readFileSync(__dirname + "/cert/key.pem", "utf8");
const certificate = fs.readFileSync(__dirname + "/cert/cert.pem", "utf8");
const credentials = { key: privateKey, cert: certificate };
server = https.createServer(credentials, app);
server.listen(port, () => console.log("https server Running"));
} else {
server = app.listen(port, () => {
console.log(`http server Running`);
});
}
module.exports = server;
When I click the Postman or browser login button, this error occurs.
Access to XMLHttpRequest at 'https://www.domain.link/login'
from origin 'https://domain.link' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
createError.js:16 Uncaught (in promise) Error: Network Error
at e.exports (createError.js:16)
at XMLHttpRequest.p.onerror (xhr.js:84)
domain.link or www.domain.link or the above error occurs.
Postman
How do I get CloudFront + S3 to communicate with EC2?
in your browser or postman
www.domain.link
After connecting to domain.link
When you make a login button or post request
I hope it works well.
If something is missing, please let me know what is missing. I will add more.

You specify allowed methods for CloudFront in your cache behavior. By default only GET and HEAD are allowed:

Related

When I run the server, it gives me a message "Cannot GET /". What am I doing wrong?

I am trying to do a GET request in order to retrieve some images from my Cloudinary account. But when I run the server, I get a 400 status code on my UI with reading
Cannot GET /
How can I solve this issue?
const express = require('express');
const dotenv = require('dotenv');
const cors = require('cors');
const { json } = require('body-parser');
const axios = require('axios');
const app = express();
app.use(cors());
app.use(json());
const { parsed: config } = dotenv.config();
const BASE_URL = `https://api.cloudinary.com/v1_1/${config.CLOUD_NAME}/resources/image`;
const auth = {
username: config.API_KEY,
password: config.API_SECRET,
};
app.get('/photos', async(req, res) => {
const response = await axios.get(BASE_URL + '/resources/image', {
auth,
params: {
next_cursor: req.query.next_cursor,
},
});
return res.send(response.data);
});
app.get('/search', async (req, res) => {
const response = await axios.get(BASE_URL + '/resources/search', {
auth,
params: {
expression: req.query.expression,
},
});
return res.send(response.data);
});
const PORT = 7000;
app.listen(PORT, console.log(`Server running on port ${PORT}`));
If you open your server URL in browser you will get Cannot GET / because you don't have base route.
It's not needed in most cases, since you don't access your node server from browser, it just run's in the background.
You generally display your frontend (Angular, React) in browser.
But if you don't like the message you can add a base route.
app.get('/', (req, res) => res.send('Hello World'));
I'm not sure what are you trying to achieve, but at least you won't get this error.

Node.JS Express basic authentication on https

I'm trying to setup an https server and I want to use Basic auth on a route. For that I use a router to manage post requests. If I use the code below however I receive a 200 OK answer without setting the Authorization header. If instead I use app.listen(PORT) I actually need to send it (desired behavior). Why does this solution ignore basic auth?
const https = require("https");
const fs = require("fs");
const basicAuth = require('express-basic-auth');
const express = require('express');
function getUnauthorizedResponse(req) {
return req.auth
? ('Credentials ' + req.auth.user + ':' + req.auth.password + ' rejected')
: 'No credentials provided'
}
var app = express();
const router = express.Router();
router.post('/test', function(req, res) {
response.writeHead(200, {'Content-Type': 'text/plain', 'Content-Length': message.length + ''});
response.write("OK");
response.end();
});
app.use(
'/',
basicAuth({
users: {
'username':'pwd'
},
challenge: true,
unauthorizedResponse: getUnauthorizedResponse
}),
router
);
const credentials = {
key: fs.readFileSync('sslcert/server.key', 'utf8'),
cert: fs.readFileSync('sslcert/server.cert', 'utf8')
};
https.createServer(credentials, app).listen(3000);

Getting a cors error running vue with express

I'm running my Vue App on my express server (nodejs running on port 60702) like:
'use strict';
const fs = require('fs');
const path = require('path');
const express = require('express');
var https = require('https');
const morgan = require('morgan');
const cors = require('cors');
const bodyParser = require('body-parser');
const nconf = require('./config');
const pkg = require('./package.json');
const swaggerSpec = require('./swagger');
const swaggerUI = require('swagger-ui-express');
const app = express();
app.options('*', cors()) // include before other routes
// create a write stream (in append mode)
var accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), {
flags: 'a'
});
// setup the logger
app.use(morgan('combined', {
stream: accessLogStream
}));
// Enable CORS (cross origin resource sharing)
app.use(cors());
// Set up body parser
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(bodyParser.json());
// Load the Vue App
app.use(express.static(path.join(__dirname, '../../client/pvapp-client/dist')));
app.get('/api/version', (req, res) => res.status(200).send(pkg.version));
const userRouter = require('./routes/user');
const systemRouter = require('./routes/system');
const yieldRouter = require('./routes/yield');
const adjustmentRouter = require('./routes/adjustmentfactors');
app.use('/user', userRouter);
app.use('/system', systemRouter);
app.use('/yield', yieldRouter);
app.use('/adjustmentfactors', adjustmentRouter);
//Default route
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '../../client/pvapp-client/dist/index.html'));
});
//const listener = app.listen(nconf.get('port'), () => console.log(`Ready on port ${listener.address().port}.`));
https.createServer({
key: fs.readFileSync('certs/apache-selfsigned.key'),
cert: fs.readFileSync('certs/apache-selfsigned.crt')
}, app)
.listen(nconf.get('port'), function() {
console.log(`App listening on port ${nconf.get('port')}! Go to https://192.168.51.47:${nconf.get('port')}/`)
});
The User router is:
router.post('/login', async (req, res) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods', 'GET,POST");
let compareUser = await db.query('SELECT * FROM app_users WHERE username=? LIMIT 1', [req.body.username]); // use db.query() to retrieve the password
if (compareUser.length < 1) // compareUser is an array with at most one item
res.sendStatus(403);
let valid = bcrypt.compareSync(req.body.password, compareUser[0].password);
if (!valid)
res.sendStatus(403);
let user = new User(compareUser[0]);
const token = jwt.sign({
user
}, nconf.get('jwtToken'), {
expiresIn: '14d'
});
Object.assign(user, {
token
});
res.json(user);
});
The vue config is:
module.exports = {
baseUrl: process.env.NODE_ENV === 'production' ? '/vue' : '/',
devServer: {
port: 60702,
https: true,
disableHostCheck: true
}
};
Axios:
const apiClient = axios.create({
baseURL: `https://192.168.51.47:60702`,
withCredentials: false, // This is the default
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
export default {
// user Endpoints
getUser(email) {
return apiClient.get(`/user/${email}`)
},
registerUser(user) {
return apiClient.post(`/user/register`, user)
},
loginUser(user) {
return apiClient.post(`/user/login`, user)
},
But even if I included cors I'm getting:
Cross-source (cross-origin) request blocked: The same source rule
prohibits reading the external resource on
https://143.93.46.35:60702/user/login. (Reason: CORS request failed).
The axios call in vue also has the correct baseUrl with the port.
I checked the POST request to the backend at /user/login with Postman and get the exprected correct request, too.
It was solved by re-creating the dist folder with
npm run build
Thanks to #Dan for his help
Don't use apiClient. Do a get with the full url, rebuild your app,
delete old dist folder, CTRL+F5 refresh once loaded. In fact, put a
"?" on the end of the url and make sure you see it in Chrome headers

CORS origin undefined with simple nodejs server

I have a very simple nodejs server, but using the 'cors' package seems to not recognize the origin of the request for some reason.
Here is my nodejs server config:
const cors = require('cors');
const express = require('express');
const CORS_WHITELIST = [ 'http://localhost:5000' ];
const corsOptions = {
origin: (origin, callback) => {
console.log(origin) // ----> this is always 'undefined'
if (CORS_WHITELIST.indexOf(origin) !== -1){
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
};
const configureServer = app => {
app.use(cors(corsOptions));
};
module.exports = configureServer;
Here is my server starter file:
const express = require('express');
const SERVER_CONFIGS = require('./constants/server');
const configureServer = require('./server');
const configureRoutes = require('./routes');
const app = express();
configureServer(app);
configureRoutes(app);
app.listen(SERVER_CONFIGS.PORT, error => {
if (error) throw error;
console.log('Server running on port: ' + SERVER_CONFIGS.PORT);
});
I am running this server on localhost, but the origin in the cors callback is always 'undefined'. For example when I open http://localhost:5000 on the browser or do a curl call.
How can I use cors so that it doesn't block the request on localhost?
I read this issue and req.headers.origin is undefined question and also CORS and Origin header!
source:
The origin may be hidden if the user comes from an ssl encrypted website.
Also: Some browser extensions remove origin and referer from the http-request headers, and therefore the origin property will be empty.
There is a solution to solve this by adding a middleware:
app.use(function (req, res, next) {
req.headers.origin = req.headers.origin || req.headers.host;
next();
});
I hope these helps. The issue is in awaiting more info state!

increase express post body limit to proxied api

I have a seperate frontend and backend, where all requests to http://frontend.com/api are proxied to the backend. However we allow image uploads to be 10mb max, which gets limited by the 1mb internal limit of express on all request bodies.
I have the following config:
const express = require('express');
const consola = require('consola');
const { Nuxt, Builder } = require('nuxt');
const helmet = require('helmet');
// Express
const app = express();
const host = process.env.HOST || '127.0.0.1';
const port = process.env.PORT || 8080;
app.set('port', port);
// Import and Set Nuxt.js options
const config = require('../nuxt.config.js');
config.dev = !(process.env.NODE_ENV === 'production');
async function start() {
// Init Nuxt.js
const nuxt = new Nuxt(config);
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
}
// NOTE: Only in production mode
if (!config.dev) {
// Helmet default security + Referrer + Features
app.use(helmet());
}
// Proxy /api to proper backend
app.use('/api', proxy(process.env.API_ENDPOINT || 'http://localhost:3000'));
// Give nuxt middleware to express
app.use(nuxt.render);
// Listen the server
app.listen(port, host);
consola.ready({
message: `Server listening on http://${host}:${port}`,
badge: true,
});
}
start();
I have tried adding body-parser, until I found out this only works for non multipart/form type of requests. Considering that this isn't an express backend, but only used to serve SSR (with nuxt), I have no idea how to get this to work with something like multer or busboy.
Can this be done without having to setup nginx as a reverse proxy?
Express itself doesn't impose any limits on body size, because it doesn't process the request body at all.
However, some middleware do impose a limit, like body-parser and express-http-proxy, which is what you're using.
To increase the limit to 10MB:
app.use('/api', proxy(process.env.API_ENDPOINT || 'http://localhost:3000', {
limit: '10mb'
));
The way mine works is I define my api base url in a config file which I reference in an api/init.js file. This file is added to plugins in nuxt.config.js. This is that file:
import axios from 'axios'
import {baseURL} from '~/config'
import cookies from 'js-cookie'
import {setAuthToken, resetAuthToken} from '~/utils/auth'
import { setUser, setCart } from '../utils/auth'
axios.defaults.baseURL = baseURL
const token = cookies.get('x-access-token')
const currentUser = cookies.get('userDetails')
const currentCart = cookies.get('userCart')
if (token) {
setAuthToken(token)
setUser(currentUser)
setCart(currentCart)
} else {
resetAuthToken()
}
The backend runs on it's own server which I launch with node index.js and it is the base url that my init.js looks for. The backend index.js looks like this:
const mysql = require('mysql')
const express = require('express')
const bodyParser = require('body-parser')
const config = require('./config')
const jwt = require('jsonwebtoken')
const bcrypt = require('bcrypt')
const multer = require('multer')
const auth = require('./auth')
const files = require('./files')
const create = require('./create')
const get = require('./get')
const delet = require('./delet')
const blogFiles = require('./blogFiles')
const db = mysql.createConnection(config.db)
const app = express()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}))
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS')
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, x-access-token, userDetails, userCart')
if (req.method === 'OPTIONS') {
res.sendStatus(200)
}
else {
next()
}
})
app.use((err, req, res, next) => {
if (err.code === 'LIMIT_FILE_TYPES') {
res.status(422).json({ error: 'Only images are allowed'})
return
}
if (err.code === 'LIMIT_FILE_SIZE') {
res.status(422).json({ error: `Too Large. Max filesize is ${MAX_SIZE/1000}kb` })
return
}
})
app.use('/auth', auth({db, express, bcrypt, jwt, jwtToken: config.jwtToken}))
app.use('/files', files({db, express, multer}))
app.use('/blogFiles', blogFiles({db, express, multer}))
app.use('/create', create({db, express}))
app.use('/get', get({db, express}))
app.use('/delet', delet({db, express}))
app.get('/test', (req, res) => {
db.query('select 1+1', (error, results) => {
if (error) {
return res.status(500).json({type: 'error', error})
}
res.json({type: 'success', message: 'Test OK', results})
})
})
app.listen(config.port)
console.log('App is running on port ' + config.port)
The files.js handles file uploads and as you can see index.js requires that. It is in there that I use multer to handle the upload limit and such. This is file.js
module.exports = ({db, express, multer }) => {
const routes = express.Router()
const fileFilter = function(req, file, cb) {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']
if (!allowedTypes.includes(file.mimetype)) {
const error = new Error('Wrong file type')
error.code = 'LIMIT_FILE_TYPES'
return cb(error, false)
}
cb(null, true)
}
const MAX_SIZE = 250000
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '../frontend/assets/images')
},
filename: function (req, file, cb) {
cb(null, file.originalname)
}
})
const upload = multer ({
storage: storage,
fileFilter,
limits: {
fileSize: MAX_SIZE
},
})
routes.post('/upload', upload.single('file'), (req, res) => {
res.json({ file: req.file })
})
return routes
}
As you can see I set the MAX_SIZE for my file uploads here so guess you can set any limit and as multer is handling it, it will over ride any limits set by express.

Categories

Resources