Yet Another Heroku Problem....
When working in development mode, users can login to the app no problem.
In Production Mode on Heroku, this is not the case - a POST request to /api/login returns a 500 Internal Server Error.
In Production Mode, my app seems to be connected to the correct database as /api/users returns the full list of registered users.
Heroku just seems to be throwing a 500 error just for the hell of it.
Any ideas why?
The App:
app.js
require('dotenv').config()
const config = require('./utils/config')
const express = require('express')
require('express-async-errors')
const app = express()
const mongoose = require('mongoose')
const cors = require('cors')
const morgan = require('morgan');
const middleware = require('./utils/middleware')
const blogsRouter = require('./controllers/blogs')
const usersRouter = require('./controllers/users')
const loginRouter = require('./controllers/login')
mongoose.connect(config.MONGODB_URI)
.then(() => {
console.log('connected to MongoDB')
})
.catch((error) => {
console.log('error connection to MongoDB:', error.message)
})
app.use(middleware.tokenExtractor)
app.use(cors())
app.use(express.static("build"))
app.use(express.json())
app.use('/api/blogs', blogsRouter)
app.use('/api/users', usersRouter)
app.use('/api/login', loginRouter)
app.use(middleware.errorHandler)
app.use(middleware.unknownEndpoint)
app.use(morgan(':method :url :body'));
module.exports = app
index.js
require('dotenv').config()
const app = require('./app')
const http = require('http')
const logger = require('./utils/logger')
const config = require('./utils/config')
const server = http.createServer(app)
const PORT = process.env.PORT
server.listen(PORT, () => {
logger.info(`Server running on port ${config.PORT}`)
})
loginRouter
const jwt = require('jsonwebtoken')
const bcrypt = require('bcrypt')
const loginRouter = require('express').Router()
const User = require('../models/user')
loginRouter.post('/', async (request, response) => {
const { username, password } = request.body
const user = await User.findOne({ username })
const passwordCorrect = user === null
? false
: await bcrypt.compare(password, user.passwordHash)
if (!(user && passwordCorrect)) {
return response.status(401).json({
error: 'invalid username or password'
})
}
const userForToken = {
username: user.username,
id: user._id,
}
const token = jwt.sign(userForToken, process.env.SECRET)
response
.status(200)
.send({ token, username: user.username, name: user.name })
})
module.exports = loginRouter
The loginRouter contains the following line necessary for generating a token:
const token = jwt.sign(userForToken, process.env.SECRET)
Heroku does not know what process.env.SECRET is unless you tell it.
Go to your app page in Heroku ---> Settings ---> Reveal Config Vars
Click Add
Type SECRET into KEY and your value into VALUE
This then fixes the code.
Related
I have a React/Node app that posts to MongoDB Atlas using Axios. Locally, the app makes calls successfully, but after deploying it on Heroku, I'm getting the following error message in the console:
POST http://localhost net::ERR_CONNECTION_REFUSED; Uncaught (in promise) o {message: 'Network Error', name: 'AxiosError', code: 'ERR_NETWORK
React Frontend:
const handleClick = (event) => {
event.preventDefault();
const newConnection = {
firstName: input.firstName,
lastName: input.lastName,
email: input.email,
comments: input.comments,
date: new Date()
}
axios.post('http://localhost:3001/create', newConnection)
window.alert("Thank you! We will be in touch soon.")
}
Backend Route:
const express = require("express");
const router = express.Router();
const Connection = require("../models/connectionModel");
router.route("/create").post((req, res) => {
const firstName = req.body.firstName;
const lastName = req.body.lastName;
const email = req.body.email;
const comments = req.body.comments;
const date = req.body.date;
const newConnection = new Connection({
firstName,
lastName,
email,
comments,
date
});
newConnection.save();
})
module.exports = router;
Backend Server:
const express = require('express');
const app = express();
const cors = require('cors');
const mongoose = require('mongoose');
const path = require('path');
const port = process.env.PORT || 3001;
app.use(cors());
app.use(express.json());
app.use(express.static(path.resolve(__dirname, "./frontend/build")));
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
mongoose.connect(process.env.MONGODB_URI);
app.use("/", require("./routes/connectionRoute"));
app.listen(port, function() {
console.log('express server is running on port 3001')
})
After combing the web for solutions, I confirmed that the express server is running on port 3001 and that I have CORS enabled. It's possible that there's an issue with the url I'm using in the axios.post request, but I'm unsure how to proceed.
Any help would be much appreciated!
You are trying to call localhost. once you deploy you need to call to API url.
create a way to get the API_URL from environment variable for react.
API_URL=https://your-heroku-url.com
axios.post(`${API_URL}/create`, newConnection)
I am getting the above error message in my Postman while setting up a new POST request using my heroku url as initial and current values in Globals.
Postman returns 'Route does not exist' and when I check the Postman console I get "JSONError: Unexpected token 'R' at 1:1 Route does not exist ^"
Postman worked fine up until now I am trying to use heroku with it. The POST request still works fine while the url is local://host/api/v1 ... however with heroku url is gives back that error message.
I don't know what I am doing wrong and I have been stuck since last week, help will be appreciated. Thank you
THIS IS MY app.js CODE
require('dotenv').config();
require('express-async-errors');
//extra security packages
const helmet = require('helmet')
const cors = require('cors')
const xss = require('xss-clean')
const rateLimiter = require('express-rate-limit')
const express = require('express');
const app = express();
//connecting dB
const connectDB = require('./db/connect')
const authenticateUser = require('./middleware/authentication')
//import routes
const authRouter = require('./routes/auth')
const jobsRouter = require('./routes/jobs')
// error handler
const notFoundMiddleware = require('./middleware/not-found');
const errorHandlerMiddleware = require('./middleware/error-handler');
app.set('trust proxy', 1)
//built in mW
app.use(
rateLimiter({
windowMs: 15 * 60 * 1000, //15 minutes
max: 100, //limit each IP to 100 request per wMs
})
);
app.use(express.json());
app.use(helmet());
app.use(cors());
app.use(xss());
// routes - full path
app.use('/api/v1/auth', authRouter)
app.use('/api/v1/jobs', authenticateUser, jobsRouter) //authUser is added to this route to prevent unauthorized access to the Jobs route and prevent editing
app.use(notFoundMiddleware);
app.use(errorHandlerMiddleware);
const port = process.env.PORT || 5000;
const start = async () => {
try {
await connectDB(process.env.MONGO_URI)
app.listen(port, () =>
console.log(`Server is listening on port ${port}...`)
);
} catch (error) {
console.log(error);
}
};
start();
THIS is my auth code
const User = require('../models/User')
const {StatusCodes} = require('http-status-codes')
const { BadRequestError, UnauthenticatedError } = require('../errors')
const register = async (req,res) => {
const user = await User.create({...req.body})//'{...req.body}' enables mongoose to do the user validation
const token = user.createJWT()
res
.status(StatusCodes.CREATED)
.json({user:{name:user.name}, token})
}
const login = async (req,res) => {
const {email,password} = req.body
if(!email || !password){
throw new BadRequestError('Please provide email and password')
}
const user = await User.findOne({email})
if(!user){
throw new UnauthenticatedError('Invalid Credentials')
}
const isPasswordCorrect = await user.comparePassword(password)
if(!isPasswordCorrect){
throw new UnauthenticatedError('Invalid Credentials')
}
const token = user.createJWT()
res
.status(StatusCodes.OK)
.json({user:{name:user.name},token})
}
module.exports = {
register,
login,
}
THIS is my routes code
const express = require('express')
const router = express.Router()
const {login,register} = require('../controllers/auth')
router.post('/register', register)
router.post('/login', login)
module.exports = router
I finally solved the challenge. This is what I did ::
I generated a new SSH public key from my computer terminal which I then linked to my heroku settings.
I then deleted the old app I had created in my heroku, cleared all existing git repos within my app terminal and quit the app. Restarted my computer and then started the heroku create and deploy app process again from the beginning. Now everything works fine.
The problem was not having an SSH linking key.
Thank you #Chris for your support.
i'm building an auth app using expressjs however i got this error cannot read property username is undefined , i tested it using postman
here is the code
i always have this error
app.js
const express = require('express')
const app = express()
const port = 3000
const client = require('./config/database')
const routes = require('./urls/routes')
const bodyParser = require('body-parser')
//database connection
client.connect(err => {
if (err) {
console.error('connection error', err.stack)
} else {
console.log('connected')
}
})
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// routes
app.use('',routes)
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
auth.js
const db = require('../../config/database')
//register
module.exports = (res, req) => {
const username = req.body.username
const email = req.body.email
const password = req.body.password
values = [username, email, password]
const query = 'insert into users(username,email,password) values($1,$2,$3)'
db
.query(query, values)
.then(res => console.log(res.rows[0]))
.catch(e => console.error(e.stack))
}
//login
The parameters of a route should be REQUEST followed by RESPONSE. You are using the other way around. Must have been an oversight, nevertheless.
Hello I have a question about JWT, auth:
I created my auth class:
const passport = require('passport');
const Strategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
module.exports = (app) => {
const jwtConfig = app.config.jwt;
const Users = app.datasource.models.tb_users;
const options = {};
options.secretOrKey = jwtConfig.secret;
options.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
const strategy = new Strategy(options,(payload, done) => {
Users
.findOne({where: payload.id})
.then(user => {
if(user){
return done(null,{
id: user.id,
login: user.login
});
}
return done(null,false);
})
.catch(error => done(error,null));
});
passport.use(strategy);
return {
initialize: () => passport.initialize(),
authenticate: () => passport.authenticate('jwt', jwtConfig.session)
};
}
this is my app.js:
const express = require('express');
const bodyParser = require('body-parser');
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const authRouter = require('./routes/auth');
const authorization = require('./auth');
const config = require('./config/config');
const datasource = require('./config/datasource');
const Email = require('./utils/email');
const app = express();
const port = 3000;
app.set('port',port);
app.config = config;
app.email = new Email(app.config);
app.datasource = datasource(app);
console.log(app.config);
app.use(bodyParser.json({
limit: '5mb'
}));
const auth = authorization(app);
app.use(auth.initialize());
app.auth = auth;
indexRouter(app);
usersRouter(app);
authRouter(app);
module.exports = app;
And then I have my token login and validation method
app.route('/login')
.post(async (req,res)=>{
try {
const response = await usersControllers.signin(req.body);
const login = response.login;
console.log(login);
if(login.id && login.isValid){
const payload = {id: login.id};
res.json({
token: jwt.sign({data:payload}, app.config.jwt.secret,{expiresIn: '1h'})
});
}else{
console.log('entrou here');
res.sendStatus(HttpStatus.UNAUTHORIZED);
}
} catch (error) {
console.log('entrou here');
console.error(error.message);
res.sendStatus(HttpStatus.UNAUTHORIZED);
}
})
and in my method of searching all users I call this authorization:
app.route('/users')
.all(app.auth.authenticate())
.get((req,res)=>{
usersController
.getAll()
.then(data => {
res.json(data);
})
.catch(error=>{
console.log(error);
res.status(400);
});
})
here: .all(app.auth.authenticate())
At this point I start to get confused
in insomnia he if I go in the login route and then in the get all users route he gives unauthorized even though being authorized by the login route
I would like to know what I could do so I could use my get route with my token
Is this front end work?
Basically I would like to know how I would do to access other routes with my token other than insomnia
And what I could improve on my code is missing something to achieve this?
Not exactly clear what is being asked and you are referencing "insomnia" which is not represented in the code you have shared. You have asked about the token work on the frontend and will share some thoughts.
You need to save the token on the client when it is returned from POST to /login. As you have specified the that passport should attempt to extract the token from the request headers: options.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken(); you will need to send the token within your headers when you are attempting to access secured routes. This is added as key/value to "Authorization" in the format "Bearer " + token.
For example using chaihttp test runner to post data to a secured route:
chai.request(server)
.post(`/books`)
.set('Authorization', `Bearer ${token.token}`) // set the auth header with the token
.send(data)
...
I have making an API using express and node.
Here is my app.js
const express = require('express');
const bodyParser = require('body-parser');
const dotenv = require('dotenv');
// setup dotenv to read environment variables
dotenv.config()
// Load Environment Varibles
const env = require('./utils/env');
// INIT MONGODB CONNECTION
require('./mongoose');
// create a new express application
const app = express();
// setup bodyparser middleware to read request body in requests
// we're only reading JSON inputs
app.use(bodyParser.json());
// Listen to API routes
const apiRoutes = require('./routes')
app.use('/api', apiRoutes);
// Start listening to requests
app.listen(env.PORT, () => {
console.log(`Server started on PORT ${env.PORT}`);
});
And here is the API routes that are being imported
const express = require('express');
const apiController = require('./apiController');
const apiValidator = require('./apiValidator');
const router = express.Router();
router.post('/login', apiValidator.loginUserValidator, apiController.loginUserController);
router.get('/rand', (req, res) => {
res.send('Some randon text');
});
module.exports = router;
Here is the middleware
const {
failureResponse
} = require('./../utils/response');
const errorcodes = require('./../utils/errorcodes');
const loginUserValidator = (req, res, next) => {
const user = req.body;
if (!user.username) {
return res.status(400).json(failureResponse(errorcodes.ERROR_INVALID_BODY_PARAMETER, "Invalid username"));
}
if (!user.password) {
return res.status(400).json(failureResponse(errorcodes.ERROR_INVALID_BODY_PARAMETER, "Invalid password"));
}
if (user.authTokens) {
delete user.authTokens;
}
next();
};
module.exports = {
loginUserValidator
};
Here is the controller
const User = require('./../models/user');
const {
successResponse,
failureResponse
} = require('./../utils/response');
const errorcodes = require('./../utils/errorcodes');
const loginUserController = async (req, res) => {
try {
const user = req.body;
// find if the user already exists
const existingUser = await User.findOne({
username: user.username
});
if (existingUser) {
// user exists. generate token and login user
console.log('Existing user login');
const token = existingUser.generateAuthToken();
return res.status(200).json(successResponse(token));
} else {
console.log('New user login');
const savedUser = await new User(user).save();
const token = savedUser.generateAuthToken();
return res.status(200).json(successResponse(token));
}
} catch (e) {
console.log(e);
return res.status(400).json(failureResponse(errorcodes.ERROR_SERVER_ERROR, "Unable to login user"));
}
};
module.exports = {
loginUserController
};
Here the issue is when I try to hit the login route from Postman, I am getting an error which says Could not get any response.
But when I hit the rand route, the output is correct.
So the issue isn't the arrangement of the code.
Why am I not able to use the login route here?