NodeJS Express Routing - Serving Both Frontend and Backend on same NodeJS App - node.js

I'm currently trying to wrap my head around the best way to achieve running my frontend and backend on the same NodeJS app.
I currently have two Express routers, one for my frontend and another for the backend.
Below is an extract of my UI/frontend route. On visiting / I want to redirect to /dashboard, I have a middleware that checks if you have accessed dashboard but not authed then you are re-directed to the /login route.
const routes = (app) => {
const ui = Router();
ui.use(function (req, res, next) {
console.log(req.originalUrl)
if (req.originalUrl === '/login' || req.originalUrl === '/register') {
console.log('public route hit')
return next();
}
if (req.isAuthenticated()) {
console.log('user is authed')
return next();
}
console.log('re-directing to login')
return res.redirect('/login');
});
ui.get('/', (req, res) => {
res.redirect('/dashboard'); //If root is navigated to go to dashboard
});
ui.get('/dashboard', async (req, res) => {
res.render('static/dashboard.html', {
locals: {
...globalLocals,
pageName: 'Dashboard'
},
partials: {
...globalPartials
}
});
});
app.use('/', ui);
};
An extract of my API routing is:
const routes = (app) => {
const api = Router();
api.get('/logout', async (req, res, next) => {
await req.logout();
return handleResponse(res, 200, 'success');
});
app.use('/api/v1/', api);
};
The main issue I am having is that by having my frontend router path using / it then runs my authentication middleware on any API calls to /api/v1/***, which results in any login attempts on /api/v1/login result in a re-direction to /login.
How do I get around this issue? Should I have my frontend/UI routing on it's own path, such as /app/ giving me /app/dashboard?

Related

How do Allow only Admins to have access to the Admin page in Nodejs `AdminBro`

How do Allow only Admins to have access to the Admin page in AdminBro? Nodejs
All that I want is for only Admins to have access to the adminBro page, is there any specific method to get this done?
I did this in my app.js file but it's not working
app.get("/admin", function (req, res, next) {
res.locals.login = req.user;
if (res.locals.login.roles == "admin") {
app.use("/admin", adminRouter);
} else {
res.redirect("/");
}
});
You cannot use new app.use inside app.get, as (req, res, next) are already consumed. You have two of choice:
Your route in if condition body
if (res.locals.login.roles === 'admin') {
// your admin route logic
res.send('admin page')
} else {
res.redirect('/')
}
I'm used to use small middleware function like this one:
const isAdmin = (req, res, next) => {
if (req.user.roles === 'admin') {
return next();
}
res.redirect('/');
};
Then you use it in whichever route this way:
app.get('/admin', isAdmin, adminRouter)

Node Express custom 404 page

I have a pretty standard Node Express app with a global 404 handler like.
app.get('*', function(req, res){
res.status(404);
res.render('404page');
});
Now in some routes I have code like
app.get('/store/:product', function (req, res) {
if (productNotFound) return res.sendStatus(404);
});
What I would like is the res.sendStatus(404) to redirect to the error page WITHOUT having to change it to res.sendStatus(404).render('404page').
Is that possible?
Just override .sendStatus of res object.
Register this middleware right after creating the express app:
app.use((req, res, next) => {
const sendStatus = res.sendStatus.bind(res);
res.sendStatus = (status) => {
if (status === 404) {
return res.status(404).render('404page');
}
return sendStatus(status);
}
next()
});

Next.js dynamic route - 404 on page reload after added SLUG

I'm used Next.js and Node.js for my project.
What we have:
Pages structure:
pages/products/PageSomeName.js
pages/products/PageEnotherName.js
pages/products/PageName.js
Pages name in products folder is can be different
routes.js
routes.add("/products/:id", "/products/[id].js");
server.js
app.prepare().then(() => {
const server = express();
server.use(
"./images",
express.static(path.join(__dirname, "images"), {
maxAge: dev ? "0" : "365d"
})
);
server.use(bodyParser.json());
server.get("*", (req, res) => {
return handle(req, res);
});
const PORT = process.env.PORT || 9001;
server.listen(PORT, err => {
if (err) throw err;
console.log(`> Read on http://${process.env.SITE_URL_FULL}`);
});
});
Link component
<Link href={`/products/${data.link}`} as={`/products/${data.slug}`}/>
data array
export const storeProducts = [
{
id: 1,
title: "Title1",
link: "product_name_some",
slug: "product-1-slug",
},
{
id: 2,
title: "Title2",
link: "product_name_different_some_name",
slug: "product-2-slug"
},
There was a problem when I added slug for my links.
By client side everything works fine. I take localhost:3000/product-2-slug in browser url. But after reload, I take 404 error page from Next.js
What I must added by server side in server.js for normal server reloading?
Maybe I need change Link component or next-route settings in routes.js
Thanks!
In server.js you should have a routes as below.
server.get("/products/product1", (req, res) => {
return handle(req, res);
});
server.get("/products/product2", (req, res) => {
return handle(req, res);
});
server.get("/products/product3", (req, res) => {
return handle(req, res);
});
A good way to handle the same is to create a generic products.js inside the pages directory and on server handle as below.
server.js
server.get("/products/:id", (req, res) => {
return handle(req, res, { id: req.params.id });
});
and in
pages/products.js we can create a getInitialProps method
static getInitialProps (context) {
// parameters reqturned from server are accessible via
const id = ctx.query.id
}

Passport JS authentication returning false when logging in from front-end side

I am currently developing a website using MERS stack. I am using express-session and passport.js for my back-end authentication. When I try to log in from my back-end server, the API works fine. However, when I try to send a POST request from my client-side (React), it is not authenticated.
I have tried to console.log the request from front-end and back-end, and the request is identical. One thing I notice is when I do not put an authentication middleware in my API, my front-end gets the data after being redirected to the API; the opposite happens when I put the middleware.
//This is my POST code
router.post(
"/userLogin",
passport.authenticate("local", {
successRedirect: "/api/user",
failureRedirect: "/api/user/asktologin"
}),
(req, res) => {}
);
//This is my middleware
const isLoggedIn = (req, res, next) => {
if (req.isAuthenticated()) {
console.log(req.isAuthenticated);
} else {
console.log(req);
}
};
Your isLoggedIn middleware is not calling the next function in the stack. It should look like this
const authenticationMiddleware = (req, res, next) => {
if (req.isAuthenticated()) {
console.log(req.isAuthenticated);
next()
} else {
console.log(req);
res.send(400);
}
};
// Then you configure it like so
app.use(authenticationMiddleware);
// Your "router" config goes here
post("/userLogin",
passport.authenticate("local", {
successRedirect: "/api/user",
failureRedirect: "/api/user/asktologin"
}),
(req, res) => {
// Do stuff
}
);
For more detailed information on how to use middlewares, make sure you check out the docs.

Express.Router middleware is being called on all routes after routes are registered

I have a simple app which has a login page and a section for authenticated users from where unauthenticated users are being redirected back to login. At the end of all my routes, I have app.all('*', ...) which returns 404 error page.
When I am authenticated, everything works fine, but if I log out and try to fetch unexisting route, I am redirected to the login page instead of getting 404 response.
I understand that this is because I added middleware which handles redirection but I would expect it to work only on routes specified in that one exact router - not in all routes (even the app-level ones) which are placed right after middleware.
The only solution I see at the moment is to use this authCheck middleware in every restricted route instead of registering it globally using router.use but I am still wondering if there is a better way?
const express = require('express')
module.exports = (app) => {
const authRouter = express.Router()
// ================================
// ========= Public Routes ========
// ================================
app.get('/login', () => { /* ... login route */ })
app.post('/login', () => { /* ... login route */ })
// ====================================
// ========= Restricted Routes ========
// ====================================
authRouter.use((req, res, next) => {
req.isAuthenticated()
? next()
: res.redirect('/login')
})
authRouter.get('/', () => { /* restricted route */ })
// register restricted routes
app.use(authRouter)
// ========================================
// ============ Other routes ==============
// ========================================
// error 404 route <--- this works only for authenticated users
app.get('*', (req, res) => {
res.status(404)
res.render('error404')
})
}
Thanks for any ideas..
try this, you can create an array of all URLs without auth and check inside middleware.
const authMiddleware = (req, res, next) => {
/* Urls Without Auth */
const withoutAuth = ['/login'];
if (withoutAuth.includes(req.url) || req.isAuthenticated()) {
next();
} else {
res.redirect('/login');
}
};
app.use(authMiddleware);
const express = require('express')
// Note: FWIW I think it's better to organize your middleware
// into separate files and store them into separate directory
// So you could use it like that:
// const verifyAuthMiddleware = require('./middleware/verifyAuthMiddleware);
const verifyAuthMiddleware = (req, res, next) => {
req.isAuthenticated()
? next()
: res.redirect('/login')
};
module.exports = (app) => {
// ================================
// ========= Public Routes ========
// ================================
app.get('/login', () => { /* ... login route */ })
app.post('/login', () => { /* ... login route */ })
// ====================================
// ========= Restricted Routes ========
// ====================================
// You can add the middleware like that
app.get('/', [verifyAuthMiddleware], () => { /* restricted route */ });
// Or you can add a middleware to a group of routes like that
app.use('/user', [verifyAuthMiddleware]);
// And then every route that starts with "/user" uses the middleware
app.get('/user/settings', () => {});
app.get('/user/wallet', () => {});
app.get('/user', () => {});
app.post('/user/wallet', () => {});
// etc
// ========================================
// ============ Other routes ==============
// ========================================
// error 404 route <--- this works only for authenticated users
app.get('*', (req, res) => {
res.status(404)
res.render('error404')
})
}
thank you for your replies. In the end, I did it like this:
function authCheck (req, res, next) {
return req.isAuthenticated()
? next()
: res.redirect('/login')
}
// Product routes
const prodRouter = express.Router()
prodRouter.get('/', products.getlist)
prodRouter.get('/:uuid/:tab?', products.getbyId)
prodRouter.post('/:uuid/:tab?', products.update)
// Register product routes
app.use('/products', [authCheck, prodRouter])
// For all other routes return 404 error page
app.get('*', (req, res) => {
res.status(404).render('error404')
})
With this approach, the authCheck middleware is being used only on /product routes so when I access /missing-page I get correctly the Error 404 page.

Resources