I deployed my app on heroku, its partially functioning as i have "server" side code that authenticates users with mongoDB atlas and performs api requests to Yelp's API. My app authenticates users just fine, but when i make a request to get nearby places, I get this error in the console.
GET https://my-first-trip.herokuapp.com/category/21.3420389/-157.7992015/pizza 503 (Service Unavailable)
Here is the code for my server.js located in the root directory:
require('dotenv').config()
const express = require('express');
const app = express();
const cors = require("cors");
const axios = require('axios');
const mongoose = require("mongoose")
const User = require('./models/user')
const session = require('express-session')
const bcrypt = require('bcrypt')
const MongoStore = require('connect-mongo')
const saltRounds = 6
const path = require('path')
mongoose.connect(process.env.ATLAS_URI)
app.use(express.json());
app.use(cors({
origin: ["http://localhost:3000"],
methods: ["GET", "POST", "DELETE"],
credentials: true
}))
// app.use(express.urlencoded())
app.set('trust proxy', 1) // trust first proxy
app.use(session({
secret: '',
resave: false,
saveUninitialized: true,
cookie: { secure: false },
store: MongoStore.create({
mongoUrl: process.env.ATLAS_URI,
// collectionName: 'users'
})
}))
app.use(express.static(path.join(__dirname, "client", "build")))
app.get("/category", (req, res) => {
const config = {
method: 'get',
url: '...' ,
headers: {
'Authorization': process.env.API_KEY
}
};
axios(config)
.then((response) => {
res.json(response.data)
// console.log(response.data);
})
.catch((error) => {
res.json(error) //this returns 'request failed with status code 400'
});
});
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
app.listen(process.env.PORT || 5000);
so the only route that doesnt work is app.get("/category/:lat/:lng/:searchCategory". In the heroku cli, I get code=H12 and desc=Request timeout. im pretty confident my yelp api query is a quick and inexpensive one. Im not sure how to debug this further.
EDIT: solved... my config var in heroku had extra quotes in my key. I entered 'key123' instead of just key123. I wish there was a way to tell what my error was. status code 400 (failed request ) was too broad
You can use Heroku Logging to find out about your error in detail:
I always use
heroku logs -n 1000 --tail
Or you can also use add-ons like papertrail
Edit: Try hardcoding your API call variables.
Related
I have a ExpressJS server and I would like to implement in Sessions however it doesn't seem to save the sessions.
The flow is to:
POST to /api/login
GET from /api/viewSession
However, the session['stuff'] returns undefined.
I suspected it might be because i'm trying to GET the session from a different URL. So I added a GET method to /api/login but it returned undefined too.
Could somebody point me in the right direction please? I'm a little lost after a few hours of Googling to no avail.
Here below is my code for index.js and my route api.js.
Also, I'm using
NPM - Version 8.3.1
Node - Version v16.14.0
npm i cors - Version 2.8.5
npm i express-session - Version 1.17.2
npm i express - Version 4.17.3
index.js
const express = require('express')
const formidable = require('express-formidable');
const cors = require('cors');
const session = require('express-session');
const api = require('./routes/api');
const app = express()
const port = 3000;
app.use(express.json());
app.use(formidable());
app.use(
cors({
origin: true,
optionsSuccessStatus: 200,
credentials: true,
})
);
app.options(
'*',
cors({
origin: true,
optionsSuccessStatus: 200,
credentials: true,
})
);
app.use(
session({
saveUninitialized: false,
secret: "anyrandomstring",
cookie: { maxAge: 36000000 }
})
);
//Routes
app.use('/api', api);
//Navigation
app.get('/', function (req, res) {
res.render('index');
res.send("Hi!");
})
//App Start
app.listen(port, () => {
console.log(`App Listening on port ${port}`);
})
api.js
"use strict";
const express = require("express");
let router = express.Router();
router
.route('/dump')
.post(async (req, res) => {
console.log(req.fields);
res.send({status: "ok"})
})
router
.route('/login')
.post(async (req, res) => {
//Saving in Session
req.session['stuff'] = "123456";
res.send("Ok");
})
router
.route('/viewSession')
.get((req, res) => {
console.log(req.session['stuff']);
res.send("ok");
})
module.exports = router;
Also, this is the way I send the POST/GET request
$.ajax({
url: "http://localhost:3000" + '/api/login',
type: "POST",
crossDomain: true,
dataType: "json",
data: {},
success: function (response) {
console.log(response);
}
})
If you're making cross-domain requests with XMLHttpRequest and you want to allow cookies to be set by the server handling the request, you need to set withCredentials : true.
Using jQuery:
$.ajax({
url: "http://localhost:3000" + '/api/login',
type: "POST",
crossDomain: true,
xhrFields: { withCredentials: true },
dataType: "json",
data: {},
success: function (response) {
console.log(response);
}
})
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
I'm currently running a webserver using the MERN stack, and I'm trying to get OAuth login working properly. However, when I click the "login with google" button, react loads the homepage (but the URL changes). Fetching the URL directly gets a 302 response from the server, but my front-end doesn't change.
Server.js
const express = require('express');
const path = require('path');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const logger = require('morgan');
const cors = require('cors');
const secure = require('express-force-https');
const passport = require('passport');
const cookieSession = require('cookie-session');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 5000;
const dbRoute = process.env.MONGODB_URI || 'NO DB ROUTE PROVIDED';
// db setup
mongoose.connect(
dbRoute,
{
useNewUrlParser: true,
useUnifiedTopology: true,
dbName: process.env.DATABASE_NAME,
}
);
let db = mongoose.connection;
db.once('open', () => console.log("Connected to the database"));
db.on('error', console.error.bind(console, "MongoDB connection error: "));
// middleware
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false })); // body parsing
app.use(bodyParser.json());
app.use(logger("dev"));
app.use(express.static(path.join(__dirname, "client", "build"))); // for serving up the clientside code
app.use(secure); // ensure that the connection is using https
app.use(cookieSession({ // cookies!
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
keys:['vcxzkjvasddkvaosd'] // yeah i'm sure that's secure enough
}));
// models
require('./models/rule');
require('./models/affix');
require('./models/user');
// passport security
require('./config/passport');
app.use(passport.initialize());
app.use(passport.session());
// routes
app.use(require('./routes'));
// The "catchall" handler: for any request that doesn't
// match one above, send back React's index.html file.
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});
app.listen(port);
console.log(`Server listening on ${port}`);
Route (There are a few index files in different folders, so the full path for this route it /api/user/google)
const mongoose = require('mongoose');
const passport = require('passport');
const router = require('express').Router();
const auth = require('../auth');
const User = mongoose.model('User');
router.get('/google',
passport.authenticate('google', {
scope: ['profile', 'email']
})
);
router.get('/google/callback',
passport.authenticate('google', { failureRedirect: '/affixes'}),
(req, res) => {
res.redirect('/?token=' + req.user.token);
}
);
Passport.js
const mongoose = require('mongoose');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
require('dotenv').config();
const User = mongoose.model('User');
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id).then((user) => {
done(null, user);
})
});
passport.use(new GoogleStrategy({
clientID: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
callbackURL: '/api/user/google/callback',
proxy: true
},
(accessToken, refreshToken, profile, done) => {
User.findOne({ googleId: profile.id })
.then((existingUser) => {
if (existingUser) {
done(null, existingUser);
} else {
new User({ googleId: profile.id }).save()
.then((user) => done(null, user));
}
});
}
));
Frontend login page (has a fetch button and a link button. As described above, different behavior)
import React from 'react';
import {
ComingSoon
} from '../Common';
import {
Button
} from '#material-ui/core';
const handleClick = () => {
fetch('/api/user/google')
}
export default function Login() {
return (
<>
<Button onClick={handleClick}>
Login with Google
</Button>
<button>Log in with Google</button>
</>
);
}
Update: Looks like some kind of CORS issue, although I still don't know how to fix it. Browser spits out
Access to fetch at '...' (redirected from 'http://localhost:3000/api/user/google') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Adding the requested header gives me
Access to fetch at '...' (redirected from 'http://localhost:3000/api/user/google') from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
It turns out I was quite wrong about the nature of this issue! The problem was that my fetch requests to my OAuth endpoint were calling my frontend, not my backend because the request included text/html in its Accept header. Using the react advanced proxy setup to route to the proper URI fixed the issue.
See: https://github.com/facebook/create-react-app/issues/5103 and https://github.com/facebook/create-react-app/issues/8550
Keycloak returns 403 Error when I send it token from my react frontend.
I have implemented the keycloak server and authentication in React. I can get the keycloak.token after a successful login and redirect in react.
I have followed tutorials I found and my setup is pretty much the same as this answer: Keycloak nodeJS always get 403
Unfortunately, I still get 403 - Access Denied - Error. I have setup a realm and below is my keycloak.json file and my code.
{
"realm": "Weal V3",
"bearer-only": true,
"auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external",
"resource": "node",
"verify-token-audience": true,
"use-resource-role-mappings": true,
"confidential-port": 0
}
My code
const express = require('express');
const session = require('express-session');
const Keycloak = require('keycloak-connect');
const cors = require('cors');
const morgan = require('morgan');
const app = express();
app.use(cors())
app.use(morgan('combined'))
const memoryStore = new session.MemoryStore();
app.use(session({
secret: 'some secret',
resave: false,
saveUninitialized: true,
store: memoryStore
}));
const keycloak = new Keycloak({
store: memoryStore
});
app.use(keycloak.middleware());
app.get('/public', (req, res) => {
res.json({ message: 'public' })
})
app.get('/secure', keycloak.protect(), (req, res) => {
console.log(req)
res.json({ message: 'secured' })
})
app.get('/admin', keycloak.protect(), (req, res) => {
res.json({ message: 'secret' })
})
app.listen(5500, () => console.log('Server started on port 5500'))
I expect that when I navigate to http://localhost:5500/secure with Authorization: Bearer + keycloak.token from the react application I should get { message: 'secured' }, but all I get is 403 Access Denied
I'm trying to deplay an SSR nuxt-legacy-edge project on a firebase Hosting.
Every things work fine in dev mode (npm run dev).
I'm using serverMiddleware /api defined in nuxtconfig.js
serverMiddleware: [
bodyParser.json(),
session({
secret: 'amdskfmdlkfdklfndfmdfndsmfndfnejnjheheuewytwgssa',
resave: true,
saveUninitialized: false,
cookie: { maxAge: (20 * 60 * 1000), secure: false }
}),
//{ path: '/api', handler: '~/api/index.js' },
'~/api'
]
and my /api/index.js is
const express = require('express')
const bodyParser = require('body-parser');
const router = express.Router()
const app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ extended: false }));
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
})
router.post('/confirmlogin', (req, res) => {
console.log('[API] - LOGIN POST', req.body.role)
req.session.userId = req.body.uid
req.session.role = req.body.role
req.session.token = req.body.token
req.session.profile=req.body.profile
res.cookie('access_token', req.body.token, {
maxAge: 3600000,
httpOnly: true
}) /* 1 hour */
return res.json({
status: 'success'
})
})
router.post('/confirmlogout', (req, res) => {
console.log('[API] - LOGOUT POST')
delete req.session.userId
delete req.session.role
delete req.session.token
delete req.session.profile
res.clearCookie('access_token');
return res.json({
status: 'success'
})
})
app.use('/', router);
module.exports = {
path: '/api',
handler: router
}
The firebase functions is defined as:
const functions = require('firebase-functions');
const { Nuxt } = require('nuxt-legacy-edge');
var express =require("express");
var app=express();
const config = {
dev: false,
buildDir: '.nuxt',
};
const nuxt = new Nuxt(config);
exports.webaffitto = functions.https.onRequest((req, res) => nuxt.render(req, res));
As shown in route api/confirmlogin and api/confirmlogout works fine in dev mode but if put in production using command
firebase serve --only functions,hosting or
using
firebase deploy
seem those routes are missing and I obtain a 404 (non found).
Help is appreciated.