I have a route to check if a user is logged in. It works well, but I don't understand what is the problem if I create a second route just below that calls it just to do the same thing. It seems like I can't access the cookie anymore in the second route, but I don't know why. Thanks for your help !
// This route works :
router.get('/loggedin', async (req, res) => {
try {
const token = req.cookies.jwt;
console.log("token : " + token) // Token is correct here in loggedin route, but is undefined if I use the route below
const decodedToken = jwt.verify(token, process.env.JWT_SECRET);
if (decodedToken) {
res.send(true);
}
else {
res.send(false);
}
}
catch (err) {
res.status(500).send(false);
}
});
// This route calls the route above and doesn't work
router.get('/loggedinbyanotherway', async (req, res) => {
const checking = await fetch(`${process.env.API_URL}:${process.env.PORT || 3000}/loggedin`)
console.log(checking.ok) // Returns false
const data = await checking.json()
console.log(data) // Returns false
res.send(data)
});
Your fetch request isn't providing any cookies, so how could the code handling the request read any cookies?
More to the point... This entire operation is unnecessary. Why make an HTTP request to the application you're already using? Instead, extract the functionality you want into a common function and just call that function from both routes. For example:
const isLoggedIn = (req) => {
const token = req.cookies.jwt;
const decodedToken = jwt.verify(token, process.env.JWT_SECRET);
if (decodedToken) {
return true;
} else {
return false;
}
};
router.get('/loggedin', async (req, res) => {
try {
res.send(isLoggedIn(req));
}
catch (err) {
res.status(500).send(false);
}
});
router.get('/loggedinbyanotherway', async (req, res) => {
const checking = isLoggedIn(req);
res.send(checking);
});
In the example it's not really clear why you need the second route or what else it offers, but I can only assume it's just a placeholder for some additional functionality you plan to add.
Either way, the point is that the application doesn't need to make an entire HTTP request to itself, since you're already in that application and have access to the same logic.
Related
I am using react as frontend and made api using express I have the following code I have stored jwt token in the cookies while logging in for the first then when trying to login in again I check if there is already a token in the cookies if there is token in the cookie (currently I am not verifying it I just want it to work) redirect the user to profile page but it doesn't work.
Although an XMLHttpRequest can be seen in the network tab (click for screenshot) but it doesn't work.
PS - I am using Axios in the frontend to make a get request.
loginRouter.get("/", async (req, res) => {
try {
const cookieFound = req.cookies["login-token"];
if (cookieFound) {
res.redirect("profile");
} else {
res.redirect("login");
}
} catch (error) {
console.log(error);
res.status(500).json("Ooops something went wrong!");
}
});
code to make a get request in the frontend
useEffect(() => {
Axios.get("/login");
}, []);
EDIT :-
Backend
loginRouter.get("/", async (req, res) => {
try {
const cookieFound = req.cookies["login-token"];
if (cookieFound) {
res.send("/profile");
}
// res.status(200).json(cookieFound);
} catch (error) {
console.log(error);
res.status(500).json("Ooops something went wrong!");
}
});
Client
useEffect(() => {
const alreadyLoggedIn = async () => {
const url = await Axios.get("/login");
window.location = url.data;
};
alreadyLoggedIn();
}, []);
To what you have entered I think you should change window.location = url.data to window.location = window.location.hostname + url.data;
In your current setup the total url will be set to /profile while you want yourwebsite.com/profile
I'm new to JavaScript and cannot seem to make this work , the topic of quiz depends on the user input... when the user presses next , I get the topic (this also takes user to the main quiz page), then i have to fetch data from the api with the topic as a parameter... I have to process the result of the fetch operation.. Then I have to pass that info to to the main quiz page... but the variable that is supposed to be populated by the fetch request is still undefined when i pass is to the main quiz page
var Allquestions;
var sheetdb = require('sheetdb-node');
// create a config file
var config = {
address: 'https://sheetdb.io/api/v1/9djmf8ydc7hwy',
};
//sheetdb
// Create new client
var client = sheetdb(config);
function downloadquestions(topic) {
console.log(topic);
client.read({ limit: 2, sheet: topic }).then(function(data) {
console.log(data + " in client.read func")
processQuestions(data);
}, function(err){
console.log(err);
});
}
async function processQuestions(data) {
console.log(data + "data in process");
Allquestions = JSON.parse(data);
console.log(Allquestions[0].Question + " This is defined");
}
app.get("/", (req, res) => {
res.render("pages/index", { title: "Home"});
});
// app.post("/" , urlencodedParser ,(req , res) => {
// console.log(req.body.topic);
// })
app.get("/questions", urlencodedParser , (req , res) => {
downloadquestions(req.body.topic);
console.log(Allquestions + " this is undefined");
res.render("/pages/quizpage" , {Allquestions})
})
There are a few issues with your code, you have a broken promise chain, client.read( is a promise, and that promise is going nowhere. You either return it, or await it. To be able to await your will need to also mark your route (req, res) as async too.
Your code is a little mixed up, you have Allquestions as a global var, this isn't great for multi-user, as the last topic is going to override this each time.
Also try and avoid swallowing exceptions in utility functions, try and keep your exception handling at the top level, eg. in your case inside your req/res handler.
So with all this in mind, your refactored code could look something like ->
const sheetdb = require('sheetdb-node');
// create a config file
const config = {
address: 'https://sheetdb.io/api/v1/9djmf8ydc7hwy',
};
//sheetdb
// Create new client
const client = sheetdb(config);
async function downloadquestions(topic) {
const data = await client.read({ limit: 2, sheet: topic });
return processQuestions(data);
}
function processQuestions(data) {
return JSON.parse(data);
}
app.get("/", (req, res) => {
res.render("pages/index", { title: "Home"});
});
app.get("/questions", urlencodedParser , async (req , res) => {
try {
const allQuestions = await downloadquestions(req.body.topic);
res.render("/pages/quizpage" , {Allquestions});
} catch (e) {
console.error(e);
res.end('There was an error');
}
})
recently I've been dabbling with express web servers to build a website that can be signed into with Discord's OAuth2 API. I've been using the npm module express-session to store sensitive information within the session for security purposes but am having trouble when it comes to redirecting after doing so. The problem I am having is that the code will complete and the redirect request will even be made (I can see the GET request be logged), but the client web browser does not move. Hopefully somebody will be able to help me out as to why, I believe it may be related to the redirect request not making it to the client but I'm not entirely sure how I would get it to the client. I have included the relevant routes below.
router.get('/callback', function(req,res) {
logDate(req)
res.render('callback')
});
router.post('/callback', bodyParser.json(), function(req,res) {
logDate(req)
ssn = req.session
var originalUrl = req.body.location
token = originalUrl.substring(originalUrl.indexOf("&") + 1);
token = token.substring(0, token.indexOf('&'));
token = token.replace('access_token=','');
ssn.token = token
res.redirect('/login');
});
router.get('/login', function(req,res) {
logDate(req)
ssn = req.session
var ssnToken = `Bearer ${ssn.token}`
function login(data, callback) {
axios.defaults.headers.common['Authorization'] = ssnToken
axios.get('https://discordapp.com/api/users/#me')
.then(response => {
ssn.login = "true"
ssn.username = response.data.username
ssn.discriminator = response.data.discriminator
ssn.userid = response.data.id
if (response.data.avatar) {
ssn.avatar = response.data.avatar
if (response.data.avatar.startsWith("a_")) {
ssn.pfp = `https://cdn.discordapp.com/avatars/${ssn.userid}/${ssn.avatar}.gif`
}
else {
ssn.pfp = `https://cdn.discordapp.com/avatars/${ssn.userid}/${ssn.avatar}.png`
}
}
else {
ssn.pfp = `https://cdn.discordapp.com/embed/avatars/${ssn.discriminator % 5}.png`
}
callback(data)
})
.catch(error => {
console.log(error);
});
}
login("hhhh", (result) => {
console.log(result)
console.log(ssn.login)
console.log(ssn.username)
console.log(ssn.discriminator)
console.log(ssn.userid)
if (ssn.avatar) {
console.log(ssn.avatar)
}
console.log(ssn.pfp)
res.redirect(‘/dashboard’)
});
});
By the way, the POST request is sent to /callback when 'callback' is rendered. It is send via an XMLHttpRequest by the client. The redirect I am referring to is the last one shown in the code block. Thank you for taking the time to read my first question.
Thanks,
Rhys
In EcmaScript 6 you can use the async / await keywords. So instead of using callbacks it easier to just transform your code into async / await form just like this:
router.get('/login', async (req, res) => {
logDate(req)
ssn = req.session
var ssnToken = `Bearer ${ssn.token}`
try {
axios.defaults.headers.common['Authorization'] = ssnToken
const response = await axios.get('https://discordapp.com/api/users/#me')
ssn.login = "true"
ssn.username = response.data.username
ssn.discriminator = response.data.discriminator
ssn.userid = response.data.id
if (response.data.avatar) {
ssn.avatar = response.data.avatar
if (response.data.avatar.startsWith("a_")) {
ssn.pfp = `https://cdn.discordapp.com/avatars/${ssn.userid}/${ssn.avatar}.gif`
} else {
ssn.pfp = `https://cdn.discordapp.com/avatars/${ssn.userid}/${ssn.avatar}.png`
}
} else {
ssn.pfp = `https://cdn.discordapp.com/embed/avatars/${ssn.discriminator % 5}.png`
}
} catch(ex) {
console.error('axios request ex:', ex)
}
console.log(result)
console.log(ssn.login)
console.log(ssn.username)
console.log(ssn.discriminator)
console.log(ssn.userid)
if (ssn.avatar) {
console.log(ssn.avatar)
}
console.log(ssn.pfp)
res.redirect(‘/dashboard’)
});
I am trying to verify if some data is in the session. If not the controller will redirect you to another route, to get that data.
The problem is that I am getting an error "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client"
I search over StackOverflow and I find that everyone that had this problem fix it using it async/await, but i was already using async await.
Your help will be trully appreciated!
Thank you very much!
Jose
dashboardCtrl.init = async (req, res) => {
//
var frontdata = req.session;
if (!frontdata.user) {
frontdata.user = await userfacebook.findOne({ where: { 'email': frontdata.passport.user } });
};
if (!frontdata.store) {
tmpstoredata = await userstore.findOne({ where: { 'userfacebookId': frontdata.user.id } });
if (!tmpstoredata) {
res.redirect('/toURL');
};
};
};
Note: I am using EJS MATE.
If i do this
dashboardCtrl.init = async (req, res) => {
//
res.redirect('/toURL');
};
Redirect works, the problem is using await. So i dont know how to continue
That error says that you have already sent an answer to the cliente. In other words, you are trying to declare for the second time -> **res.*****.
Check the flow again in case you have twice declared any action on express's "res".
The solution below allows you to have a good structured and readable asynchronous code.
dashboardCtrl.init = (req, res) => {
// I think destructuring looks good
let { user, store } = req.session;
(async () => {
try {
if (!user) user = await userfacebook.findOne({ where: { 'email': frontdata.passport.user } });
let tmpstoredata;
if (!store) tmpstoredata = await userstore.findOne({ where: { 'userfacebookId': frontdata.user.id } });
if (!tmpstoredata) res.redirect('/toURL');
} catch (err) {
// don't forget ;)
}
})()
};
Hope this can help you.
Greetings.
The code was OK
The problem was the EJS MATE
I replace it with EJS
I have this Node.js code that should, using Express.js http get, decode my jwt token from my http auth header.
When I write the code of decodeToken() inside of my express js get() method, everything works fine.
When I extract it to outside method (decodeToken() method) I get that undefined value from this method. Mind you, when this code was hard coded inside of the method get() it works.
Why is that, the async nature of Node.js? Does the method finish after the get() method was supposed to assign the token?
If I will use promises, will it solve that?
var jwtCheck = expressJwt({
secret: "some secret"
});
app.use('/test', jwtCheck);
app.get('/test', function (req, res) {
var token = req.get('Authorization').split("Bearer ")[1];
var information = decodeToken(token)
console.log("information: "+information);
if (information!=null) {
res.json(information);
}
else {
res.json('Failed to authenticate token.');
}
});
var decodeToken = function (token) {
console.log(token);
jwt.verify(token, secret, function (err, decoded) {
if (err) {
console.log('Failed to authenticate token.');
return null;
} else {
return (decoded);
}
});
}
var getToken = function (req) {
return req.get('Authorization').split("Bearer ")[1];
}
jwt.verify is async because you pass a callback to it so anything that calls it needs to handle that. You could look at promises to clean up the logic a little bit but just using the code you have you could modify it like this:
var jwtCheck = expressJwt({
secret: "some secret"
});
app.use('/test', jwtCheck);
app.get('/test', function (req, res) {
var token = req.get('Authorization').split("Bearer ")[1];
// pass callback to decodeToken that gets called after the token is verified
decodeToken(token, function(information) {
// this function won't execute until decodeToke calls it as `next()`
console.log("information: "+information);
if (information!=null) {
res.json(information);
}
else {
res.json('Failed to authenticate token.');
}
})
});
// accept `next` callback
var decodeToken = function (token, next) {
console.log(token);
jwt.verify(token, secret, function (err, decoded) {
if (err) {
console.log('Failed to authenticate token.');
return next(null);
} else {
return next(decoded);
}
});
}
var getToken = function (req) {
return req.get('Authorization').split("Bearer ")[1];
}