I have setup Passport.js using the local-stratagey, with my express server.
When I am logged in and make an async request within NextJS's getInitialProps, it correctly permits the GET request via the client render but not the server side render. That is to say, If I access the private route via the client side routing, it shows the route and permits the aync request, but when I hit the page directly from the browser it permits the route but not the async request via the server side call of getInitialProps.
To add to the complexity, I am using Next.js and it's new api routes. I catch the route in server.js first to authenticate, and then if it's authenticated I pass it on the Next.
// server.js routes
app.get('/apples', function(req, res) {
if (req.isAuthenticated()) {
console.log(`[${req.method}]`, 'SERVER - ROUTE AUTHENTICATED');
return handle(req, res);
} else {
console.log(`[${req.method}]`, 'SERVER - ROUTE NOT AUTHENTICATED');
res.redirect("/login");
}
});
app.get('/api/apples', function(req, res) {
if (req.isAuthenticated()) {
console.log(`[${req.method}]`, 'SERVER - API AUTHENTICATED');
return handle(req, res);
} else {
console.log(`[${req.method}]`, 'SERVER - API NOT AUTHENTICATED');
res.status(401).end();
}
});
// /pages/apples.js - the consuming page
Apples.getInitialProps = async () => {
const response = await fetch('http://localhost:3000/api/apples');
const apples = await response.json();
return { apples }
}
Any help would be much appreciated!
This thread solved my problem: https://spectrum.chat/next-js/general/halp-nextjs-express-passport-next-api-routes~f5f60d4a-cfea-422b-8dfe-ed243b598ce6
The TL;DR is that if you hit the server directly, return the data you want directly from the server, rather than via a subsequent call to the api. The api request should only be used when you hit a page via client side routing.
Related
I'm using nodejs, expressjs, javascript...
I have this "middleware" function to use when someone tries to access one route that doesn't have access when it is not logged in, but I can't send an alert, and redirect, as well as a response, how can I do it?
function isLoggedIn (req, res, next)
{
if (req.isAuthenticated()) {
return next();
}
res.send('<script>alert("You need to be authenticated to access this page")</script>');
res.redirect('/');
}
#Michael is right.
You should decouple the server logic from the client.
The server should know if you're logged in or not
The server should NOT know how you tell your users.
The middlwere should simple send 401 status if he's not authenticated. The client then checks if it was 200 (OK) or 401 (Unauthorized) and shows the user content according to the status
function isLoggedIn (req, res, next)
{
if (req.isAuthenticated()) {
res.status(200).send('Welcome')
} else {
res.status(401).send('You are not allowed')
}
}
I dont recommend doing that (sending html in server side response)
Instead:
Send a json with a variable that will tell you in the client side to make an alert:
res.json({ success: false });
Ans then in the client side you can alert a message if !success and redirect.
I have a Express JS code where I load a middleware that defines certain end-points on a router. The endpoints are specific to the user login and logout. I am adding a new authentication in which case I receive my auth token from a different service. When I receive the token from a different service I don't want those end-points to be loaded.
This is my server.js file
let app = express();
const authEndpoints = require('auth'); // this defines router endpoints
const alreadyAuth = require('checkHeaders'); // this middleware checks if request
// already has the auth headers and set res.locals.alreadyAuthenticated to true else false
app.use('/', alreadyAuth);
app.use('/',function(req, res, next) {
if(res.locals.alreadyAuthenticated === false)
next();
else {
console.log('authentication already exists skipping authEndpoints loading');
next('route');
}
}, authEndpoints); // login, logout
//continue here
app.use('/',nextMiddleware);
auth.js file
'use strict';
const express = require('express');
const path = require('path');
const router = express.Router();
router.get('/login', (req, res) => {
// some code
res.sendFile('login.html');
}
router.get('/logout', (req, res) => {
// some code
});
module.exports = router;
I see the console log that prints 'authentication already exists skipping authEndpoints loading' but the endpoints /login and /logout are still accessible.
Also when I comment the whole section
app.use('/',function(req, res, next) {
if(res.locals.alreadyAuthenticated === false)
next();
else {
console.log('authentication already exists skipping authEndpoints loading');
next('route');
}
}, authEndpoints); // login, logout
then the endpoints are not loaded.
Can someone please clarify If this not the way next('route') should be used.
From the top of my head, try adding a isAuthenticated (whatever is your equivalent to this) check to the /login and /logout routes code you listed. if it's authenticated, do a redirect to the protected page, else return the user the login form (or logout thing..). :)
I think that is better if you use the middleware "alreadyAuth" for every request, no matter the route:
app.use(alreadyAuth);
In this way you check the headers in every request for every route. In the "checkHeaders" middleware, you must use an if statement that redirect the user to the login page if is not authenticated, and use next() in the case that is already authenticated.
let checkHeaders = function(req, res, next) {
//some code that check headers
if(isAuthenticate === false) {
res.redirect("/login");
} else {
next();
};
}
Now, all the end points after this middleware are not accessible if the user is not authenticated. So you can use a logout endpoint, or whatever.
Good Luck!
Hello I would like to serve two different front-end applications on same route.
User will send a token in HTTP header and based on the information that token holds application should decide which folder should it serve statically. Is that possible?
I already have a middleware that parses token and provides me with user role.
Preferred behavior would be
module.exports = function (app) {
app.get('/admin' function(req, res) {
if(req.headers.security.role === 1) {
// serve superadmin page
} else if(req.headers.security.role === 2) {
// serve user page
} else {
// serve forbidden page
}
});
};
Of course it is, here is an example with a cookie sent with the request with nodejs.
app.get('*', function (request, response, next) {
let app = request.cookies.app;
if (app) {
response.sendFile(path.resolve(__dirname, 'app.html'))
} else {
response.sendFile(path.resolve(__dirname, 'app2.html'))
}
});
i'm newbie in using Authenticate a Node.js API with JSON Web Tokens, i read this document on scotch.io but i cant understand when Token is correct what happen after next() method
apiRoutes.use(function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.body.token || req.param('token') || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
next();
}
});
} else {
...
}
});
apiRoutes.get('/', function(req, res) {
...
});
apiRoutes.get('/users', function(req, res) {
...
});
apiRoutes.get('/check', function(req, res) {
...
});
app.use('/api', apiRoutes);
app.listen(port);
User must be pass other request after that (my mean is after token is correct)?
i think in this sample code / route will be call after check token and it was correct,ok? how can i choose other method to call, because / is calling after next()
actually next() asking for nodejs to go on for next step it's whatever in node.js async loop. Javascript actually single threaded so next will call whatever method placed at stack. if you will not call next() your program will stuck. and will not call any other method after this token middleware which you have passed to .use
In this particular scenario it will call the next route after verified by this token middleware, which you are passing as a anonymous function.
also have a look at this method
javascript node.js next()
I am working with a single app application framework called reactjs, the issue I encountered is setting httpOnly cookies, as they can not be set / read from a client side I needed to figure out a way how to use express for this.
One idea I came up with is to make a post request to a route like /cookie:data where data is value of token that needs to be stored in a cookie, so:
app.post('/cookie:data', function(req, res) {
// Set cookie here
res.send(200)
})
Issue I am hesitant about is that token contains unique user identifier that is used to secure api, and I am not sure if I am or am not exposing this by setting up a cookie this way.
Alternatively instead of using :data it would be beneficial to figure out how I can grab data (json object) from the post request
EDIT:
One issue I can think of is that anyone can post to this route and set different cookies? what would be a way of securing it?
EDIT 2:
This is the express setup I use to proxy api calls (only relevant for clarifying comments)
app.use('/api', function (req, res) {
let url = config.API_HOST + req.url
req.pipe(request(url)).pipe(res)
})
Say that you want to proxy all requests starting with /api to a third-party, except /api/users, which you want to perform 'manually' because it returns a token you need:
app.post('/api/users', function(req, res) {
let url = config.API_HOST + req.url;
let apiRequest = request.post(url, function(err, response, body) {
// Responses are just examples, you should tailor them to your situation
if (err) {
return res.sendStatus(500);
} else if (response.statusCode !== 200) {
return res.sendStatus(response.statusCode);
} else {
res.cookie('token', body).send('OK');
}
});
req.pipe(apiRequest);
})
app.use('/api', function (req, res) {
let url = config.API_HOST + req.url
req.pipe(request(url)).pipe(res)
})