I'm building node js server with apollo and front end with next.js/apollo client. My problem is that on localhost everything is works as it must be, but after deploying to Heroku cookies not set. I'm getting the data from server and redirects me to the home page, but after page reload it redirects me to login page again.
This is apollo server code
import cors from 'cors'
import Redis from 'ioredis'
import dotenv from 'dotenv'
import express from 'express'
import mongoose from 'mongoose'
import session from 'express-session'
import connectRedis from 'connect-redis'
import { ApolloServer } from 'apollo-server-express'
import typeDefs from './schema'
import resolvers from './resolvers'
dotenv.config({
path: `.env.${process.env.NODE_ENV}`,
})
const port = process.env.PORT || 4000
const dev = process.env.NODE_ENV !== 'production'
const RedisStore = connectRedis(session)
const startServer = async () => {
await mongoose
.connect(process.env.DB_URL, { useNewUrlParser: true })
.then(() => console.log(`🔗 MongoDB Connected...`))
.catch(err => console.log(`❌ MongoDB Connection error: ${err}`))
const app = express()
const server = new ApolloServer({
typeDefs,
resolvers,
playground: !dev
? false
: {
settings: {
'request.credentials': 'include',
},
},
context: ({ req, res }) => ({ req, res }),
})
app.disable('x-powered-by')
app.set('trust proxy', 1)
app.use(
cors({
credentials: true,
origin:
process.env.NODE_ENV === 'production'
? process.env.FRONT_END_URL
: 'http://localhost:3000',
}),
)
app.use((req, _, next) => {
const authorization = req.headers.authorization
if (authorization) {
try {
const cid = authorization.split(' ')[1]
req.headers.cookie = `cid=${cid}`
console.log(cid)
} catch (err) {
console.log(err)
}
}
return next()
})
app.use(
session({
store: new RedisStore({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
pass: process.env.REDIS_PASS,
}),
name: process.env.SESS_NAME,
secret: process.env.SESS_SECRET,
saveUninitialized: false,
resave: false,
cookie: {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days
secure: false,
},
}),
)
server.applyMiddleware({ app, cors: false })
app.listen({ port }, () =>
console.log(
`🚀 Server ready at http://localhost:${port}${server.graphqlPath}`,
),
)
}
startServer()
and this is the resolvers
import gravatar from 'gravatar'
import bcrypt from 'bcrypt'
import { User } from '../models'
import { isAuthenticated, signOut } from '../auth'
import { loginSchema, registerSchema } from '../utils'
export default {
Query: {
users: (parent, args, { req }, info) => {
isAuthenticated(req)
return User.find({})
},
user: (parent, { id }, context, info) => {
isAuthenticated(req)
return User.findById(id)
},
me: (parent, args, { req }, info) => {
isAuthenticated(req)
return User.findById(req.session.userId)
},
},
Mutation: {
signUp: async (parent, args, { req }, info) => {
// isAuthenticated(req)
args.email = args.email.toLowerCase()
try {
await registerSchema.validate(args, { abortEarly: false })
} catch (err) {
return err
}
args.avatar = await gravatar.url(
args.email,
{
protocol: 'https',
s: '200', // Size
r: 'pg', // Rating
d: 'mm', // Default
},
true,
)
args.password = await bcrypt.hash(args.password, 12)
const user = await User.create(args)
req.session.userId = user.id
return user
},
signIn: async (parent, args, { req }, info) => {
// isAuthenticated(req)
const { email, password } = args
try {
await loginSchema.validate(args, { abortEarly: false })
} catch (err) {
return err
}
const user = await User.findOne({ email })
if (!user || !(await bcrypt.compare(password, user.password))) {
throw new Error('Incorrect email or password. Please try again.')
}
req.session.userId = user.id
return user
},
signOut: (parent, args, { req, res }, info) => {
return signOut(req, res)
},
},
}
and here is apollo client on front end (I'm using with-apollo-auth example from next.js)
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { createHttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context'
import fetch from 'isomorphic-unfetch'
let apolloClient = null
if (!process.browser) {
global.fetch = fetch
}
function create(initialState, { getToken }) {
const httpLink = createHttpLink({
uri:
process.env.NODE_ENV === 'development'
? 'http://localhost:4000/graphql'
: process.env.BACK_END_URL,
credentials: 'include',
})
const authLink = setContext((_, { headers }) => {
const token = getToken()
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
}
})
return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser,
link: authLink.concat(httpLink),
cache: new InMemoryCache().restore(initialState || {}),
})
}
export default function initApollo(initialState, options) {
if (!process.browser) {
return create(initialState, options)
}
if (!apolloClient) {
apolloClient = create(initialState, options)
}
return apolloClient
}
Thanks in advance.
Related
Hi guys i am newbie in react js, i have a problem about getting data from server side and routing in reactjs, i am using axios and jsonwebtoken to get the data, but it's not working,
i have check the inspect from browser and there's no error in there, but my website is still not responding
maybe you can help me to fix this problem
thanks in advance
Here is my code from "AuthController.js"
import UserModel from "../Models/userModel.js";
import bcrypt from 'bcrypt'
import jwt from "jsonwebtoken";
// Registering a new User
export const registerUser = async (req, res) => {
const { username, password, firstname, lastname } = req.body
const salt = await bcrypt.genSalt(10)
const hashedPass = await bcrypt.hash(password, salt)
// req.body.passowrd = hashedPass
const newUser = new UserModel({
username,
password: hashedPass,
firstname,
lastname
})
try {
// check username
const oldUser = await UserModel.findOne({ username })
if (oldUser) {
return res.status(400).json({ message: "The username is already exist" })
}
const user = await newUser.save()
const token = jwt.sign({
username: user.username, id: user._id
}, process.env.JWT_KEY, { expiresIn: '1h' })
res.status(200).json({ user, token })
} catch (error) {
res.status(500).json({ message: error.message })
}
};
// login user
export const loginUser = async (req, res) => {
const { username, password } = req.body;
try {
const user = await UserModel.findOne({ username: username })
if (user) {
const validity = await bcrypt.compare(password, user.password);
validity ? res.status(200).json(user) : res.status(400).json("Wrong Password")
const token = jwt.sign(
{ username: user.username, id: user._id },
process.env.JWTKEY,
{ expiresIn: "1h" }
);
res.status(200).json({ user, token });
} else {
res.status(404).json("User not found");
}
} catch (error) {
res.status(500).json({ message: error.message })
}
}
here is my Auth Reducer
const authReducer = (state = { auhtData: null, loading: false, error: false }, action) => {
switch (action.type) {
case "AUTH_START": return { ...state, loading: true, error: false };
case "AUTH_SUCCES":
localStorage.setItem("profile", JSON.stringify({ ...action?.data }))
return { ...state, authData: action.data, loading: false, error: false };
case "AUTH_FAIL": return { ...state, loading: false, error: true }
default: return state;
}
}
export default authReducer
my Auth action
import * as AuthApi from '../api/AuthRequests.js';
export const logIn = (formData) => async (dispatch) => {
dispatch({ type: "AUTH_START" });
try {
const { data } = await AuthApi.logIn(formData);
dispatch({ type: "AUTH_SUCCESS", data: data });
} catch (error) {
console.log(error);
dispatch({ type: "AUTH_FAIL" });
}
};
export const signUp = (formData) => async (dispatch) => {
dispatch({ type: "AUTH_START" })
try {
const { data } = await AuthApi.signUp(formData)
dispatch({ type: "AUTH_SUCCES", data: data })
} catch (error) {
console.log(error)
dispatch({ type: "AUTH_FAIL" })
}
}
my Auth Request for API
import axios from 'axios'
const API = axios.create({ baseURL: "http://localhost:5000" });
export const logIn = (formData) => API.post('/auth/login', formData);
export const signUp = (formData) => API.post('/auth/register', formData);
here is my index.js
import express from "express";
import bodyParser from "body-parser";
import mongoose from "mongoose";
import dotenv from 'dotenv';
import cors from 'cors'
import AuthRoute from './Routes/AuthRoute.js'
import UserRoute from './Routes/UserRoute.js'
import PostRoute from './Routes/PostRoute.js'
//Routes
const app = express();
// Middleware
app.use(bodyParser.json({
limit: '30mb', extended: true,
}))
app.use(bodyParser.urlencoded({
limit: '30mb', extended: true,
}))
app.use(cors())
dotenv.config();
mongoose.connect(process.env.MONGO_DB, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => app.listen(process.env.PORT, () => console.log(`Listening to ${process.env.PORT}`)))
.catch((error) => console.log(error));
// usage of routes
app.use('/auth', AuthRoute)
app.use('/user', UserRoute)
app.use('/post', PostRoute)
It's probably because of validity ? res.status(200).json(user) : res.status(400).json("Wrong Password"). You are sending a response multiple times.
Try with:
export const loginUser = async (req, res) => {
const { username, password } = req.body;
try {
const user = await UserModel.findOne({ username: username })
if (user) {
const validity = await bcrypt.compare(password, user.password);
if (!validity) return res.status(400).json("Wrong Password");
const token = jwt.sign(
{ username: user.username, id: user._id },
process.env.JWTKEY,
{ expiresIn: "1h" }
);
res.status(200).json({ user, token });
} else {
res.status(404).json("User not found");
}
} catch (error) {
res.status(500).json({ message: error.message })
}
}
var passport = require("passport");
var passportJWT = require('passport-jwt');
var User = require("../models/user");
var config = require('../config');
var ExtractJwt = passportJWT.ExtractJwt;
var Strategy = passportJWT.Strategy;
var opts = {
secretOrKey: config.secret,
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
}
module.exports = function () {
var strategy = new Strategy(opts, function(payload, done) {
User.findById(payload.id, function(err, user) {
if (user) {
return done(null, {id: user.id, payload: user});
}else {
return done(new Error("User not found"), null);
}
});
});
passport.use(strategy);
return {
initialize: function() {
return passport.initialize();
},
authenticate: function () {
const a = passport.authenticate("jwt", {session: false});
return a;
}
};
};
You can use Passport and middleware.
Also it seems your process.env.JWTKEY is missing.
So please check the value via console.log(process.env.JWTKEY)
For whatever reason, the token generated by jsonwebtoken never expires.
Here is my code so far.
auth.ts Middleware.
// Libs
import { Express, Request, Response, NextFunction } from "express";
import { PassportStatic } from "passport";
import { Strategy as JWTStrategy, ExtractJwt } from "passport-jwt";
// Users
import { usersDB } from "../users";
const setupAuth = (api: Express, passport: PassportStatic) => {
const strategy = new JWTStrategy(
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: "123456qwerty",
algorithms: ["HS256"],
},
(payload, cb) => {
try {
const { sub } = payload;
const user = usersDB.find((u) => u.username === sub);
if (user) {
return cb(null, user);
} else {
return cb(null, false);
}
} catch (e) {
return cb(e);
}
}
);
api.use(passport.initialize());
passport.use(strategy);
};
export default setupAuth;
Login route
import { Request, Response } from "express";
import { usersDB, validatePassword } from "../../users";
import { genJWT } from "../../utils/auth";
const login = (req: Request, res: Response) => {
const { username, password } = req.body;
const user = usersDB.find((u) => u.username === username);
if (!user) {
return res
.status(401)
.json({ status: "fail", message: "Invalid username or password" });
}
if (!validatePassword(password, user.salt, user.hash)) {
return res
.status(401)
.json({ status: "fail", message: "Invalid username or password" });
}
const token = genJWT(user.username);
res.status(200).json({ status: "success", token });
};
export default login;
And the jwt token generator
import jwt from "jsonwebtoken";
export const genJWT = (username: string) => {
const token = jwt.sign({ sub: username, iat: Date.now() }, "123456qwerty", {
expiresIn: "1min",
algorithm: "HS256",
});
return token;
};
Then the secured routes
// Lib
import { Express } from "express";
import { PassportStatic } from "passport";
// GET
import root from "./GET/root";
import currentUser from "./GET/current-user";
import privateContent from "./GET/private-content";
// POST
import register from "./POST/register";
import login from "./POST/login";
import logout from "./POST/logout";
const setupRoutes = (api: Express, passport: PassportStatic) => {
api.get("/", root);
api.get(
"/current-user",
passport.authenticate("jwt", { session: false }),
currentUser
);
api.get(
"/private-content",
passport.authenticate("jwt", { session: false }),
privateContent
);
api.post("/register", register);
api.post("/login", login);
api.post("/logout", logout);
};
export default setupRoutes;
So the API is working, able to generate jwt token, able to authenticate with the token. It is able to validate too if I modify the token. But the problem is I can forever use the token. It never expires.
Is there something I missed?
Thanks in advance.
Ok when I removed
iat: Date.now()
from the jwt.sign, now the token does expire. So never put iat, let jsonwebtoken generate it.
The problem is that I cannot get the cookie in the server. It is set in the browser (I can see it in devtools). When using Postman it works fine (including the middleware). But for some reason when making the request from the browser to the middleware (authMiddleware) the token comes back undefined.
Anyone with any ideas would be great. I have been stuck on this for days! :O
App.js (server)
const express = require('express');
const cookieParser = require('cookie-parser');
const authRouter = require('./routes/authRoutes');
const cocktailRouter = require('./routes/cocktailRoutes');
require('dotenv').config();
const cors = require('cors');
const app = express();
mongoose.connect(process.env.DBURI, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true })
.then((result) => {
console.log('listening');
app.listen(5000)
})
.catch((err) => console.log(err));
//middleware
app.use(express.static('public'));
app.use(express.json());
app.use(cookieParser());
app.use(cors({ origin: true, credentials: true }));
// ROUTER MIDDLEWARE
app.use('/api/cocktails', cocktailRouter);
app.use('/auth', authRouter);
authMiddleware
const User = require('../models/User');
require('dotenv').config();
//check user is authorized
const requireAuth = (req, res, next) => {
const token = req.cookies.jwt;
console.log('TOKEN', token)
//check json web token exists and is verified
if (token) {
jwt.verify(token, process.env.SECRET_KEY, (err, decodedToken) => {
if (err) {
// res.redirect('/auth/login');
console.log('not logged in')
} else {
console.log('logged in ')
next();
}
})
} else {
// res.redirect('/auth/login');
console.log('not logged in again')
}
};
module.exports = { requireAuth };
cocktailRoute
This is where I am implementing the requireAuth middleware:
const router = Router();
const { requireAuth } = require('../middleware/authMiddleware');
const { cocktail_get, cocktail_post, cocktail_put, cocktail_delete } = require('../controllers/cocktailController');
router.get('/', requireAuth, cocktail_get);
router.post('/', cocktail_post);
router.put('/:cocktailId', cocktail_put);
router.delete('/:cocktailId', cocktail_delete);
module.exports = router;
authController
const jwt = require('jsonwebtoken');
//handle errors function
const handleErrors = (err) => {
const { message } = err;
const errors = { email: '', password: '' }
//incorrect email
if (message === 'Incorrect email') {
errors.email = 'Invalid email';
};
//incorrect password
if (message === 'Incorrect password') {
errors.password = 'Invalid password';
};
//duplicate error = 11000
if (err.code === 11000) {
errors.email = 'That email is already registered';
return errors;
}
//validation errors
if (message.includes('user validation failed')) {
Object.values(err.errors).forEach(({ properties }) => {
errors[properties.path] = properties.message;
})
};
return errors;
}
//create jwt token
const maxAge = 3 * 24 * 60 * 60; // 3 days in seconds
const createToken = (id) => {
return jwt.sign({ id }, process.env.SECRET_KEY, { expiresIn: maxAge })
};
module.exports.register_post = async (req, res) => {
const { name, email, password } = req.body;
try {
const user = await User.create({ name, email, password });
const token = createToken(user._id);
res.cookie('jwt', token, { httpOnly: false, maxAge: maxAge * 1000 });
res.status(201).json({ user: user._id, token });
} catch (err) {
const errors = handleErrors(err);
res.status(400).json({ errors });
}
};
module.exports.login_post = async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.login(email, password);
const token = createToken(user._id);
res.cookie('jwt', token, { httpOnly: false, maxAge: maxAge * 1000, secure: false });
res.status(200).json({ user: user._id });
} catch (err) {
const errors = handleErrors(err);
res.status(400).json({ errors });
}
};
module.exports.logout_get = (req, res) => {
console.log(res.cookie('jwt', '', { maxAge: 1 }))
res.cookie('jwt', '', { maxAge: 1 });
res.redirect('/');
}
FRONTEND (React)
LoginPage
const LoginPage = () => {
const [values, setValues] = useState({ email: '', password: '' });
const handleChange = (event) => {
const { name, value } = event.target;
setValues({ ...values, [name]: value });
};
const handleLoginFormSubmit = (event) => {
event.preventDefault();
const { email, password } = values;
try {
fetch(`${process.env.REACT_APP_API_URL}/auth/login`, {
method: "POST",
withCredentials: true,
credentials: 'include',
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({
email, password
})
})
} catch (err) { if (err.request) { console.log('REQUEST', err.request) } if (err.response) { console.log('RESPONSE', err.response) } }
};
return (
<div>
<h1>Login</h1>
<form onSubmit={handleLoginFormSubmit}>
<input type="text" value={values.email} name="email" placeholder="Enter your email" onChange={handleChange} />
<input type="password" value={values.password} name="password" placeholder="Enter your password" onChange={handleChange} />
<button>Login</button>
</form>
</div>
)
}
export default LoginPage
Whenever I try to run the function refreshStock() in an endpoint in one of the API endpoints /api/seller/deactivate it gives me this error:
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (net.js:1318:16)
at listenInCluster (net.js:1366:12)
at Server.listen (net.js:1452:7)
at C:\Users\***\Documents\GitHub\***\***\.next\server\pages\api\seller\deactivate.js:191:10
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command
It looks like it's trying to restart the server, but it happens after it compiles, is there something I'm doing wrong, I've followed a couple of tutorials on medium, and they give this same type of code, just not ES Modules. I want to use ES Modules because it is what my database functions are written in.
Server.js:
import express from 'express';
import { createServer } from 'http';
import next from 'next';
import models from './server/models';
import { genStock } from './server/lib/functions';
import { Server } from 'socket.io';
const port = parseInt(process.env.PORT || '3000', 10);
const dev = process.env.NODE_ENV !== 'production';
const nextApp = next({ dev });
const nextHandler = nextApp.getRequestHandler();
const app = express();
const server = createServer(app);
const io = new Server(server);
const Users = models.users;
io.use(async (socket, next) => {
const err = new Error('Unauthorized');
err.data = { message: 'Unauthorized, please try again later.' };
try {
if (!socket.handshake.auth.token) return next(err);
let user = await Users.findOne({
where: {
socket_token: socket.handshake.auth.token,
},
});
if (!user) {
console.log('unauthenticated socket');
socket.disconnect();
next(err);
}
await Users.update(
{ socket_id: socket.id },
{
where: {
socket_token: socket.handshake.auth.token,
},
},
);
next();
} catch (e) {
console.log(e);
next(e);
}
});
io.on('connection', async (socket) => {
// Works fine
const stock = await genStock();
socket.emit('updateStock', stock);
});
// Fails with address already in use :::3000
export async function refreshStock() {
const stock = await genStock();
io.emit('updateStock', stock);
}
nextApp.prepare().then(async () => {
app.all('*', (req, res) => nextHandler(req, res));
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
This is meant to refresh the stock after a seller deactivates their account and sends all users the new stock.
/api/seller/deactivate
....
await refreshStock();
....
I figured it out, I just split up the WebSocket server and the next.js one. I have whitelisted local IPs that may appear to only allow server-to-server communication. Although I don't think this is full-proof as there is most likely a better way to have this type of communication but for now it works.
/**
* This server cannot be imported in /api folders, it won't work.
* Although it can import other functions
* */
import express from 'express';
import { createServer } from 'http';
import session from 'express-session';
import { Server } from 'socket.io';
import { genStock } from './server/lib/stockFunctions';
import { sessionStore } from './server/lib/session';
import passport from './server/lib/passport';
import models from './server/models';
const authorizedIPs = ['::1', '127.0.0.1', '::ffff:127.0.0.1'];
const Users = models.users;
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: `http://localhost:3000`,
methods: ['GET', 'POST'],
credentials: true,
},
});
const wrap = (middleware) => (socket, next) => middleware(socket.request, {}, next);
io.use(
wrap(
session({
secret: "---",
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
path: '/',
sameSite: 'lax',
},
store: sessionStore,
}),
),
);
io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));
io.use(async (socket, next) => {
const err = new Error('Unauthorized');
err.data = { message: 'Unauthorized, please try again later.' };
try {
const user = socket.request.user;
if (!user) return next(err);
await Users.update(
{ socket_id: socket.id },
{
where: {
id: user.id,
},
},
);
next();
} catch (e) {
console.log(e);
next(e);
}
});
io.on('connection', async (socket) => {
const stock = await genStock();
socket.emit('updateStock', stock);
});
app.post('/refresh-stock', async function (req, res) {
const ip = req.ip;
if (!authorizedIPs.includes(ip)) {
console.log(ip);
return res.status(401).json({ success: false });
}
const newStock = await genStock();
io.emit('updateStock', newStock);
return res.status(200).json({ success: true });
});
httpServer.listen(3001);
console.log(`> Websockets ready on http://localhost:3001`);
I am making full stack app and learn from tutorials and videos . I have a problem with GET request to get information about user which is login in the system. I use Postman to check the requests. When I add user with /login , the Postman look user's accesstoken code. I copy his code and paste it in authorization key in headers in Postman and when I change the URL in localhost to /infor to get information about this user and send it. But it say me "Invalid Authentication". I can't find the wrong. I think the problem is in controllers/userCtrl.js in getUser function. Can you help me?
I put the code:
server.js
require('dotenv').config()
const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
const fileUpload = require('express-fileupload')
const cookieParser = require('cookie-parser')
const app = express()
app.use(express.json())
app.use(cookieParser())
app.use(cors())
// Use temp files instead of memory for managing the upload process.
app.use(fileUpload({
useTempFiles: true
}))
// Routes
app.use('/user', require('./routes/userRouter'))
// Connect to Mongodb
const URL = process.env.MONGO_URL
mongoose.connect(URL,{
useCreateIndex: true,
useFindAndModify: false,
useNewUrlParser: true,
useUnifiedTopology: true
}, err =>{
if(err) throw err;
console.log('Connected to MongoDB')
})
const PORT = process.env.PORT || 5000
app.listen(PORT, () => {
console.log('Server is running on port', PORT)
})
.env
MONGO_URL = ***********
ACCESS_TOKEN_SECRET = ***********
REFRESH_TOKEN_SECRET = *************
routes/userRouter.js
require('dotenv').config()
const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
const fileUpload = require('express-fileupload')
const cookieParser = require('cookie-parser')
const app = express()
app.use(express.json())
app.use(cookieParser())
app.use(cors())
// Use temp files instead of memory for managing the upload process.
app.use(fileUpload({
useTempFiles: true
}))
// Routes
app.use('/user', require('./routes/userRouter'))
// Connect to Mongodb
const URL = process.env.MONGO_URL
mongoose.connect(URL,{
useCreateIndex: true,
useFindAndModify: false,
useNewUrlParser: true,
useUnifiedTopology: true
}, err =>{
if(err) throw err;
console.log('Connected to MongoDB')
})
const PORT = process.env.PORT || 5000
app.listen(PORT, () => {
console.log('Server is running on port', PORT)
})
models/userModel.js
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true,
},
role: {
type: Number,
default: 0
},
cart: {
type: Array,
default: []
}
}, {
timestamps: true
})
module.exports = mongoose.model('Users', userSchema)
middleware/auth.js
const jwt = require('jsonwebtoken')
const auth = (req, res, next) => {
try{
const token = req.header("Authorization")
if(!token) return res.status(400).json({ msg: "Invalid Authentication" })
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if(!err) return res.status(400).json({msg: "Invalid Authentication" })
req.user = user
next()
})
} catch (err) {
return res.status(500).json({msg: err.message})
}
}
module.exports = auth
controllers/userCtrl.js
const Users = require('../models/userModel')
const bcrypt = require('bcrypt')
const jwt = require('jsonwebtoken')
const userCtrl = {
register: async (req, res) => { // async before a function means one simple thing: a function always returns a promise.
try{
const { name, email, password } = req.body
const user = await Users.findOne({ email }) // wait until the promise resolves
if(user) return res.status(400).json({msg: "The email already exists"})
if(password.length < 6)
return res.status(400).json({msg: "Password is at least 6 characteres long."})
//Password encryption
const passwordHash = await bcrypt.hash(password, 10)
const newUser = new Users({
name, email, password: passwordHash
})
// save mongodb
await newUser.save()
//then create jsonwebtoken to authentication
const accesstoken = createAccessToken({ id: newUser._id })
const refreshtoken = createRefreshToken({ id: newUser._id })
res.cookie('refreshtoken', refreshtoken, {
httpOnly: true,
path: '/user/refresh_token'
});
res.json({accesstoken})
} catch(err){
return res.status(500).json({msg: err.message})
}
},
login: async (req, res) => {
try{
const {email, password} = req.body;
const user = await Users.findOne({email})
if(!user) return res.status(400).json({msg: "User does not exist."})
const isMatch = await bcrypt.compare(password, user.password)
if(!isMatch) return res.status(400).json({msg: "Incorrect password"})
// if login success, create access token and refresh token
const accesstoken = createAccessToken({ id: user._id })
const refreshtoken = createRefreshToken({ id: user._id })
res.cookie('refreshtoken', refreshtoken, {
httpOnly: true,
path: '/user/refresh_token'
});
res.json({accesstoken})
} catch(err){
return res.status(500).json({msg: err.message})
}
},
logout: async (req, res)=> {
try{
res.clearCookie('refreshtoken', {path: '/user/refresh_token'})
return res.json({msg: "Logged out"})
}catch(err){
return res.status(500).json({msg: err.message})
}
},
refreshToken: (req, res) => {
try{
const rftoken = req.cookies.refreshtoken
if(!rftoken) return res.status(400).json({msg: "Please login or Register"})
jwt.verify(rftoken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if(err) return res.status(400).json({msg: "Please login or Register"})
const accesstoken = createAccessToken({id: user.id})
res.json({ accesstoken })
})
}catch (err) {
return res.status(500).json({msg: err.message})
}
},
getUser: async (req, res) => { // problem
try{
const user = await (await Users.findById(req.user.id)).isSelected('-password')
if(!user) return res.status(400).json({ msg: "Useer does not exist."})
res.json(req.user)
}catch (err) {
return res.status(500).json({msg: err.message})
}
}
}
const createAccessToken = (user) => {
return jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '1d' })
}
const createRefreshToken = (user) => {
return jwt.sign(user, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '7d' })
}
module.exports = userCtrl
For your middle ware for getting the token (auth function)
const { authorization } = req.headers
if (!authorization) {
console.log('[No Authorization Code]');
return res.status(401).send({ message: 'Unauthorized' });
}
if (!authorization.startsWith('Bearer')) {
console.log('[Authorization need to start with Bearer]')
return res.status(401).send({ message: 'Unauthorized' });
}
const split = authorization.split('Bearer ')
if (split.length !== 2) {
console.log('[Invalid Authorization Param')
return res.status(401).send({ message: 'Unauthorized' });
}
const token = split[1] //this is your token to use with jwt.verify
When you sending the token in postman, select Bearer Token
When you start creating your frontend, the codes should be equivalent to the following fetch request
fetch('/api/path', { method: 'GET', headers: { "Authorization": `Bearer ${token}`}}).(res => res.json())
May change method to your desire method (e.g get or post), and the token will be the the jwt token