I want to create a simple application with auth system. When someone visits my page server must return authorization html page, than visitor can fill the inputs and send login and password through socket.io to server. And if the values are correct, server sends second html page to client. I know that I can use this code and redirect on client side:
app.get('/page1', function(req, res){
res.sendFile(__dirname + '/page1.html');
});
app.get('/page2', function(req, res){
res.sendFile(__dirname + '/page2.html');
});
But I want users to always stay on the same address, it must always be example.com/
You can use express middleware for authentication.
function authCheck(req,res,next){
//Your auth check will be here
if(isValidUser){
next();
}
else{
res.sendFile(__dirname + '/login.html');
}
}
app.get('/afterlogin', authCheck, function(req, res){
res.sendFile(__dirname + '/afterlogin.html');
});
You can use Passport for authentication provider.
Then you can register some authentication middleware like so:
// auth.js
module.exports = {
isGuest: (req, res, next) => {
if (req.isAuthenticated()) {
res.redirect('/');
} else {
next();
}
},
isAuthenticated: (req, res, next) => {
if (req.isAuthenticated()) {
next()
} else {
res.redirect('/users/login')
}
},
};
Then you can apply the middleware on your routes like so:
// users.js
let express = require('express'),
router = express.Router(),
usersController = require('../controllers/usersController'),
auth = require('../middlewares/auth');
router.get('/register', auth.isGuest, (req, res) => {
usersController.showRegisterPage(req, res);
});
router.post('/register', auth.isGuest, (req, res) => {
usersController.register(req, res);
});
router.get('/login', auth.isGuest, (req, res) => {
usersController.showLoginPage(req, res);
});
router.post('/login', auth.isGuest, (req, res) => {
usersController.login(req, res);
});
router.post('/logout', (req, res) => {
usersController.logout(req, res);
});
router.get('/profile/:username', auth.isAuthenticated, (req, res) => {
usersController.showProfilePage(req, res);
});
Hope this helps.
Related
In my Express application I implement routes in routes.ts:
var Router = express.Router();
Router.route('/models/:modelId')
.get(function (req, res) {
service.setParameter(req)
service.get(req,res)
});
Router.route('/models/:modelId')
.post(function (req, res) {
service.setParameter(req)
service.post(req,res)
});
And express.ts:
export const App = express()
App.use(express.json())
App.use(express.urlencoded({ extended: true }))
App.use(helmet())
App.use('/', Router)
At each router call I'd like to execute a piece of code service.setParameter(req) that gets particular parameter from 'params', but I don't want to add to each router method explicitly.
I tried adding it at as middleware before and after Router
App.use('/', Router)
App.use(function(req, res, next){
service.setParameter(req)
next()
})
But if I define it before Router then route hasn't been set yet, and I don't get the parameter I want, and if I define it after, then middleware is not executed.
How can execute service.setParameter(req) in a generic way so that it applies to all the routes?
In express.ts file, you can add a middleware that would do it before mounding the Router, and then just procced forward with next(). You can do it like this:
App.use('/*', (req, res, next) => {
service.setParameter(req);
next();
});
App.use('/', Router)
You need to place your custom middleware between the context path and your router inside app.use(..):
const router = express.Router();
router.post('/', (req, res) => {
service.post(req,res);
});
router.get('/', (req, res) => {
service.get(req,res)
});
app.use('/models', (req, res, next) => {
service.setParameter(req);
next();
}, router);
With above code the middleware will be excecuted for all requests to '/models'.
You can use app.use(async (req,res,next) => {...}) in order to declare a middleware that executes in all the requests, if you want this middleware to be called first, it must be declare before than your routes, the middleware have to call next() in order to continue with the execution flow, if you want to be called at the end of you request, you have to put at the end of your declarations but before of the error middleware, in that approach each route have to call next() at the end of your function.
First approach
const express = require('express');
const app = express();
const router = express.Router();
router.post('/', async (req, res) => {
await service.post(req,res);
});
router.get('/', async (req, res) => {
await service.get(req,res)
});
app.use((req,res,next) => {
console.log("always called");
next();
});
app.use('/',router);
Second approach
const express = require('express');
const app = express();
const router = express.Router();
router.post('/', async (req, res, next) => {
await service.post(req,res);
next();
});
router.get('/', async (req, res, next) => {
await service.get(req,res);
next();
});
app.use('/',router);
app.use((req,res) => {
console.log("always called");
});
Thanks for all the answers, they helped me better understand how routing works on Express.
I found another solution, which I think works best in my case - using Router.all() method:
const setRequest = function(req, res, next){
logger.setRequest(request)
next()
}
Router.route('/models/:model_id')
.all(setRequest)
.get(function (req, res) {service.execute()})
.put(function (req, res) {service.execute()})
const express = require('express')
const app = express()
app.get('/user/:uid', (req, res, next) => {
if (req.params.uid === 'lai9fox') next('route')
else next()
}, (req, res, next) => {
res.send(`<h1>hello, ${req.params.uid}</h1>`)
})
app.get('/user/:id', (req, res, next) => {
res.send(`<h1>Welcome you, ${req.params.uid} !</h1>`)
})
app.listen(3000, () => console.log('server is running at port 3000...'))
When I visit http://localhost:3000/user/lai, it correctly shows:
hello, lai
But when I visit http://localhost:3000/user/lai9fox, it shows:
Welcome you, undefined!
What's go wrong?
you need to change the req.params.uid for id
app.get('/user/:id', (req, res, next) => {
res.send(`<h1>Welcome you, ${req.params.id} !</h1>`)
})
I have a strange problem with my NodeJs - Express server which serves as a back-end for my mobile application.
The thing is that i send post requests to some endpoints like checkmail, checkusername from front-end using axios and it works, but the problem is it doesn't work for any other middleware function. I literally copied the same checkmail and just used different route and I get status 404 while with /checkmail it works!
Also, the /login does not work, im using express. Router in there.
Here is my app.js code:
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
var cors = require("cors");
const User = require("./models/user");
var AuthController = require('./auth/authController');
const app = express();
let server = require("http").Server(app);
app.use(cors());
app.use(
bodyParser.urlencoded({
extended: true
})
);
app.use(bodyParser.json());
//Check if e-mail is aready in use, return "success" if not
app.use("/signup", (req, res) => {
User.findOne({
email: req.body.email
},
function (err, user) {
if (user) {
res.send("error");
} else {
res.send("success");
}
}
);
});
//Check if e-mail is aready in use, return "success" if not
app.use("/checkmail", (req, res) => {
User.findOne({
email: req.body.email
},
function (err, user) {
if (user) {
res.send("error");
} else {
res.send("success");
}
}
);
});
app.use('/login', AuthController);
const port = process.env.PORT || 8500;
server.listen(port, () => { });
Middleware should have third parameter next.
app.use("/checkmail", (req,res,next)=>{
//do something
})
You must have third parameter in middleware, it's callback to tell application go to next route.
app.use('/signup', (req, res, next) => { // Normal middleware, it must have 3 parameters
User.findOne({
email: req.body.email
},
function (err, user) {
if (user) {
next(new Error('Some thing wrong')); // Failed, we will go to error-handling middleware functions
} else {
next(); // Success and we will go to next route
}
}
);
});
app.get('/signup', (req, res, next) => { // This is "next route"
res.send('Yay, welcome to signup')
});
app.use((err, req, res, next) => { // error-handling middleware function, It must have 4 parameters
res.status(500).send(err)
});
You can find document in here: https://expressjs.com/en/guide/error-handling.html
I'm using React Router with Passport.js to set up Facebook login. I've set up the express routes and passport config, but every time I hit the
<a href="/api/auth/facebook"> link on my client side, it makes a request to RR because of my express app with this line:
app.get('/*', function(req, res) {
res.sendFile(path.resolve(__dirname, '../client', 'build', 'index.html'))
});
As I am making a call to the server side route, it returns this error:
Warning: [react-router] Location "/api/auth/facebook" did not match any routes
How can I bypass React Router for this one particular route?
my user_routes.js file looks like:
'user strict';
var bodyparser = require('body-parser');
var User = require('../models/User.js');
module.exports = function loadUserRoutes(router, passport) {
router.use(bodyparser.json());
router.get('/auth/facebook', passport.authenticate('facebook', {
session: false,
successRedirect: '/chat',
failureRedirect: '/'
}));
router.get('/auth/facebook/callback', passport.authenticate('facebook', {
session: false,
successRedirect: '/chat',
failureRedirect: '/'
}));
router.post('/sign_up', passport.authenticate('local-signup', { session: false}), function(req, res) {
res.json(req.user);
});
router.post('/sign_in', passport.authenticate('local-login', { session: false}), function(req, res) {
res.json(req.user);
});
router.get('/signout', function(req, res) {
req.logout();
res.end();
});
//get auth credentials from server
router.get('/load_auth_into_state', function(req, res) {
res.json(req.user);
});
// get usernames for validating whether a username is available
router.get('/all_usernames', function(req, res) {
User.find({'local.username': { $exists: true } }, {'local.username': 1, _id:0}, function(err, data) {
if(err) {
console.log(err);
return res.status(500).json({msg: 'internal server error'});
}
res.json(data);
});
})
};
In express routes are matches in the order they get defined.
So before your /* route you need something to handle api requests.
app.get('/api/auth/facebook', passport.authenticate('facebook'))
app.get('/*', function(req, res) {
res.sendFile(path.resolve(__dirname, '../client', 'build', 'index.html'))
});
var express = require('express');
var router = express.Router();
router.use(function(req, res, next){
console.log(req.user)
if(!req.user){
res.redirect('/login');
}else{
res.locals.username = req.user.username;
return next();
}
});
//this won't work
router.get('/register', function(req, res, next) {
res.render('register');
});
The first block make sense and it's working,I able to have a login system with protected routes. But in the same time it ruined my second bit, it will show the login page althought I try to nagivate to localhost:3000/register.
When you're using router.use() you're telling the router to use that function middleware in all the next roter.get() routes. So here, the order makes sense. If you care about order you can do what #bloodyKnuckles do. Or if you want to keep that pattern for your routes you can do the following :
// Routes that don't need authorization like register
router.get('home',...);
router.get('register',...);
// Use your authorization middleware
router.use(function(req, res, next){
console.log(req.user)
if(!req.user){
res.redirect('/login');
}else {
res.locals.username = req.user.username;
return next();
}
});
// At this point you're using the authorization middleware.
// Any routes declared from here will call authorization middleware before its handler.
router.get('profile', ...);
Use the express route middleware option to distinguish protected routes from unprotected routes.
// unprotected routes (no auth middleware)
router.get('/login', function(req, res, next) {
res.render('login');
});
router.get('/register', function(req, res, next) {
res.render('register');
});
// protected route (see auth middleware here)
router.get('/userinfo', authorize, function(req, res, next) {
res.render('userinfo');
});
function authorize (req, res, next){
console.log(req.user)
if(!req.user){
res.redirect('/login');
}else{
res.locals.username = req.user.username;
return next();
}
}
Include your authorization middleware ONLY in the protected routes:
router.get(path, [middleware (optional),] callback)