NodeJS/Express - Cannot Read All Req.Headers - node.js

Building a simple ToDo app with ReactJs frontend and NodeJs/Express backend. I configured my frontend to include userId as a request header:
export default function authHeader() {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
// return { Authorization: 'Bearer ' + user.accessToken }; // for Spring Boot back-end
return {
'x-access-token': user.accessToken,
'userid': user.id
}; // for Node.js Express back-end
} else {
return {};
}
}
This header is included with the Axios request:
// List all Group Members
listMembers() {
return http.get(`/group`, { headers: authHeader() });
}
Consequently, I can see the headers in the request:
Part of my auth middleware references the access token:
verifyToken = (req, res, next) => {
let token = req.headers["x-access-token"];
if (!token) {
return res.status(403).send({
message: "No token provided!"
});
}
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
return res.status(401).send({
message: "Unauthorized!"
});
}
req.userId = decoded.id;
next();
});
};
The req.headers lines near the top is able to pull the token from header "x-access-token" with no problem. HOWEVER - for debug purposes, I have tried to pull the header userId value instead using let token = req.headers["userid"]; but this simply comes back as undefined in my debug tools. Why can it pull x-access token header but not userId? My eventual goal is to refer to the req.header userId value in backend SQL queries, this is just a test.

It ended being a capitalization error somewhere in pipeline. #Sardar's comment was on the money, make sure everything is spelled correctly and matches case.

Related

Using Firebase Auth with an external database

I'm currently using Firebase to authenticate my users in a React/Node app, but I also want to store additional user data in my own database and I'm doing so by storing the Firebase uid on each user and I wanted to get some input on my implementation to make sure I'm on the right track.
My frontend code is as follows:
This is used as an onClick on a "Continue with Google" button:
const googleSignIn = async () =>
signInWithPopup(auth, new GoogleAuthProvider());
When the above popup promise completes, auth.onAuthStateChanged is triggered in the following useEffect, which (on login/signup) would trigger the function applicationAuthentication, passing in the user object returned from Firebase:
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(firebaseUser => {
if (!firebaseUser) {
return dispatch(logUserOut());
}
return applicationAuthentication(firebaseUser);
});
return unsubscribe;
}, []);
The applicationAuthentication looks as follows:
const applicationAuthentication = async (firebaseUser: User) => {
try {
const idToken = await firebaseUser.getIdToken();
const { data } = await axios.get('/api/users/authenticate/signin', {
headers: {
Authorization: `Bearer ${idToken}`
}
});
const { user, error } = data;
if (error) {
throw new Error(error.message);
}
dispatch(logUserIn({ user, accessToken: idToken }));
} catch (error: any) {
dispatch(setUserError(error.message));
console.log(error.message);
}
};
In my node express server, the following happens at the route /api/users/authenticate/signin; this is where I communicate with my own database by using the data access methods findUserByFirebaseUID and createUser using the uid from the token to check if the user exists, and if not, creating a new one (note the middleware that's checked first as noted below):
usersRouter.get(
'/authenticate/signin',
async (req: Request, res: Response, next: NextFunction) => {
try {
const uid = res.locals.uid; // set by token middlewear function
let firstLogin = false;
let user = await findUserByFirebaseId(uid);
if (!user) {
firstLogin = true;
user = await createUser(uid);
}
res.json({ user, firstLogin });
} catch (error) {
next(error);
}
}
);
Which uses the following authenticate middleware function to authenticate the user with firebase-admin:
const authenticate = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const idToken = req.headers.authorization.split(' ')[1];
const decodedToken = await admin.auth().verifyIdToken(idToken);
if (decodedToken) {
const { uid } = decodedToken;
res.locals.uid = uid;
return next();
}
return res.status(400).json({ message: 'Unauthorized Request' });
} catch (error) {
next({ message: 'Invalid Token' });
}
};
app.use(authenticate);
Does this overall flow of using the uid to check my own database seem correct? And am I implementing the token middleware correctly?
I'd love to hear any thoughts on this!
Yes, the way you pass the ID token from the client to the server, and then decode it (in the middleware) on your server to securely determine the UID is similar to how Firebase's own services do this.
If you pass the ID token to other requests to to authorize them, consider keeping a cache of recent raw and decoded ID tokens, to prevent having to decode them on each request.

JSON.parse: unexpected character at line 1 column 1 of the JSON data when serving the build

i'm struggling to fix json parse errors in my PERN app, i really don't know what's wrong with the code, cause it's throwing errors when i serve the build. I send the json response after the authentication and if i check on postman i see that everything is correct when i'm in developing mode.
async function isAuth() {
try {
const response = await fetch("http://localhost:5000/auth/verify", {
method: "GET",
headers: { token: localStorage.token }
})
const parseRes = await response.json()
parseRes === true ? setIsAuthenticated(true) : setIsAuthenticated(false)
} catch (error) {
console.error(error.message)
}
}
This is the method i use to check if the token is correct or not (i'm using jwt tokens for authentication).
The error is here :
const parseRes = await response.json()
The server code is this:
router.get("/verify", authorization, async (req, res) => {
try {
res.json(true)
} catch (error) {
console.error(error.message);
}
})
This method use the authorization middleware to check if the token is correct or not.
Authorization middleware:
const jwt = require("jsonwebtoken")
require("dotenv").config()
module.exports = async (req, res, next) => {
try {
const jwtToken = req.header("token")
if (!jwtToken) {
return res.status(403).json("Not Authorized")
} else {
const payload = jwt.verify(jwtToken, `${process.env.ACCESS_TOKEN_SECRET}`)
req.user = payload.user
next()
}
} catch (error) {
console.error(error.message);
res.status(403).json("Not Authorized")
}
}
I have the same error when i'm trying to do the login and the registration.
If you need more code just tell me, i really need some help.
Thank you in advance.
try add the following header:
Content-Type: 'application/json'
happen to me one time and this line fix it
Update:
the problem is probably localStorage.token you cant access like this localStorage and can only be by localStorage.getItem("token")
and update and add response without the .json() so we can check why we get what you get.

.save() is not a function (Mongoose)

So, I'm not 100% why this isn't working as intended. I have an Edit Profile React component (I'm learning how to build a SSR-based application currently, using the MERN stack) - but when I submit the edit, I get an error that "user.save is not a function - Code:
From the routes:
router.route('/api/users/:userId')
.get(authCtrl.requireSignin, userCtrl.read)
.put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.update)
.delete(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.remove)
The API Helper:
const update = async (params, credentials, user) => {
try {
let response = await fetch('/api/users/' + params.userId, {
method: 'PUT',
headers: {
"Accept": 'application/json',
Authorization: 'Bearer ' + credentials.t
},
body: user
})
return await response.json()
} catch (err) {
console.log(err)
}
}
And lastly, the actual controller, that handles all the logic behind the update: (This function sanitizes the password information before passing it back to the client, hence the undefineds)
const update = (req, res) => {
let form = new formidable.IncomingForm()
form.keepExtensions = true
form.parse(req, async (err, fields, files) => {
if (err) {
return res.status(400).json({
error: "Photo could not be uploaded"
})
}
let user = req.profile
user = extend(user, fields)
user.updated = Date.now()
if(files.photo){
user.photo.data = fs.readFileSync(files.photo.path)
user.photo.contentType = files.photo.type
}
try {
await user.save()
user.hashed_password = undefined
user.salt = undefined
res.json(user)
} catch (err) {
console.log(err)
return res.status(400).json({
error: errorHandler.getErrorMessage(err)
})
}
})
}
This isn't a production level application, just for me learning how to do this from scratch (without CRA, and all contained in one project using SSR)
EDIT: After some digging, console.logs and console.dirs, I discovered that the updates passed from the component aren't even being passed to the controller. The stale data (from the database) are logging, but req.profile is completely empty. I may re-visit this code completely and make some major changes to it.. All part of learning, right?
Here are the auth methods that were requested (I'm using Session Storage for now, but that may change to localStorage):
import User from '../models/user.model'
import jwt from 'jsonwebtoken'
import expressJwt from 'express-jwt'
import config from './../../config/config'
const signin = async (req, res) => {
try {
let user = await User.findOne({email: req.body.email})
if (!user) {
return res.status(401).json({error: "User not found"})
}
if (!user.authenticate(req.body.password)) {
return res.status(401).send({error: "Email and Password do not match"})
}
const token = jwt.sign({_id: user._id}, config.jwtSecret)
res.cookie('t', token, {expire: new Date() + 9999})
return res.json({
token,
user: {
_id: user._id,
name: user.name,
email: user.email
}
})
} catch (err) {
return res.status(401).json({error: "Could not sign in"})
}
}
const signout = (req, res) => {
res.clearCookie('t')
return res.status(200).json({message: "Signed out"})
}
const requireSignin = expressJwt({
secret: config.jwtSecret,
algorithms: ['sha1', 'RS256', 'HS256'],
userProperty: 'auth'
})
const hasAuthorization = (req, res, next) => {
const authorized = req.profile && req.auth
&& req.profile._id == req.auth._id
if (!(authorized)) {
return res.status(403).json ({error: "User is not authorized"})
}
next()
}
export default {
signin,
signout,
requireSignin,
hasAuthorization
}
Possible places where you could have a mistake: (code is not shown)
If your req.profile isn't a mongoose object, this won't work
let user = req.profile
From your other posts, I think you're probably getting req.profile from your jwt. That means this is not a mongoose object. What you'll need to do is either:
As you mentioned, use findByIdAndUpdate passing the id and the object to be updated. Note that if you have a mongoose middleware for save it won't run here
Do a user = await User.findById(id), update the user as you see fit, then use user.save. This gives you a bit more control over it, but runs 2 operations.
This has been solved.. My issue was apparently with the form not passing the request body properly to the API, which was caused by a faulty install of a dependency. Once I got that solved, the rest fell into place, and I can now do what I need to do with ease..
Thank you all who attempted to troubleshoot this with me.

Unexpected token in JSON argument in POST request

I am trying to make a POST request to a nodejs server from an Ionic application work, but I stumbled on this error.
Http failure response for http://127.0.0.1:3000/services/user/signin: 500 Internal Server Error", SyntaxError: Unexpected token u in JSON at position 0;
I have access to both the application and the API server. Task at hand, trying to send credentials to the server, which will check if those credentials are recognized and will send a response. The server side code works just fine, since I have a web app accessing to the same resources and working like a charm.
Here goes the code.
Home page:
doLogin() {
this.remoteService.login(this.user);
}
user is a key value array
user = { email:'', password:'' };
Now for the login function in the remoteService injectable:
login(user){
let headers = new HttpHeaders();
headers.append('Content-Type', 'application/json');
console.log(JSON.stringify(user));
this.http.post(serviceUrl+'/user/signin', JSON.stringify(user), {headers: headers}).subscribe(
function(response) { console.log("Success Response" + response)},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
console.log(err);
} else {
console.log(err);
}
}
);
}
I used this line
console.log(JSON.stringify(user));
to test the function argument for JSON correctness, and it's a charm.
Finally, this is the nodejs server code
if(label === 'signin') {
return function (req, res, next) {
{
var user = JSON.parse(req.query.user);
db.query('SELECT username as id FROM user where email = ? and password = ?', [user.email,user.password], function (err, result) {
if (err) {
next(err);
}
else {
if(result.length === 1) {
delete user.password;
req.session.user = result[0];
req.result = 400;
}
else
{
req.result = 404;
}
next();
}
});
}
};
}
Could you please help me fix the nasty error? I have been banging my head on this all day long and a big part of the last night.

Endpoints not authenticating with Fetch API calls (using passport-google-oauth2)

I have passport set up to use the Google strategy and can direct to the /auth/google great. I currently have it so that when you log in using the google authentication oauth2, my endpoints will authenticate by checking for a req.user. This works when I'm just getting to the endpoints in my browser. If I go to /auth/google and then /questions, I'll be able to make that get request. However when I try to make a fetch request from redux, I will get an error message saying Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0. It comes up because the fetch API tries to get to my /questions endpoint, goes through my loggedIn middleware and then doesn't meet the if (!req.user) and gets re-directed instead. Any ideas on how to authenticate from the Fetch API with PassportJS and passport-google-oauth2?
The loggedIn function:
function loggedIn(req, res, next) {
if (req.user) {
next();
} else {
res.redirect('/');
}
}
Here is my code for my 'GET' endpoint.
router.get('/', loggedIn, (req, res) => {
const userId = req.user._id;
User.findById(userId, (err, user) => {
if (err) {
return res.status(400).json(err);
}
Question.findById(user.questions[0].questionId, (err, question) => {
if (err) {
return res.status(400).json(err);
}
const resQuestion = {
_id: question._id,
question: question.question,
mValue: user.questions[0].mValue,
score: user.score,
};
return res.status(200).json(resQuestion);
});
});
});
The redux fetch request:
function fetchQuestion() {
return (dispatch) => {
let url = 'http://localhost:8080/questions';
return fetch(url).then((response) => {
if (response.status < 200 || response.status >= 300) {
let error = new Error(response.statusText);
error.response = response;
throw error;
}
return response.json();
}).then((questions) => {
return dispatch(fetchQuestionsSuccess(questions));
}).catch((error) => {
return dispatch(fetchQuestionsError(error));
}
};
}
The Fetch API doesn't send cookies by default, which Passport needs to confirm the session. Try adding the credentials flag to ALL your fetch requests like so:
fetch(url, { credentials: 'include' }).then...
or if you aren't doing CORS requests:
fetch(url, { credentials: 'same-origin' }).then...

Resources