How to estabilish express session with passport.js trough multiple APIs - node.js

nodejs newbie here.
Currently i'm working on implementing local authentication via passport.js for admin user. I have the following setup: web client for admin, api for admin, and service api.
So the call for auth is goes like this:
clientAdmin > apiAdmin > serviceApi (auth) > apiAdmin > clientAdmin.
However, express-session is not working properly on this setup, its just doesen't store passport in session and never runs deserializeUser function. I assume that when serviceApi responds to apiAdmin, its trying to store a cookie in it, but apiAdmin can't store it.
What shall i do to get the cookie from response on apiAdmin side, and then pass it to the client? Or maybe i can kinda resend the response that i get from serviceApi to client?
P.S. If i do a call directly from client to service, ignoring adminApi, all works fine, so my middleware and passport config are configured properly. But i need to implement a setup with 2 apis.
Function on client that calls adminapi
const login = () => {
axios({
method: "POST",
data: {
username: username,
password: password,
},
withCredentials: true,
url: config.loginURL,
}).then((res) => {
if (res.data.userId) {
setUser({ id: res.data.userId });
}
});
};
Function on adminapi that calls serviceapi
loginHandler(req: express.Request, res: any) {
axios({
method: "POST",
data: req.body,
withCredentials: true,
url: config.loginURL,
})
.then((response) => {
res.send(response.data);
})
.catch((err) => console.error(err));
}
Function on serviceapi
loginHandler(req: any, res: any, next: express.NextFunction) {
passport.authenticate("local", (err, user) => {
if (err) throw err;
if (!user) res.json({ message: "Invalid credentials" });
else {
req.logIn(user, (err: Error) => {
if (err) throw err;
res.json({ message: "Authorized", userId: user.id });
});
}
})(req, res, next);
}

Related

How do I fix authentication not working when I send a request from react frontend but that works in backend?

So I have this app that interacts with an api on localhost, I'm using express, mongodb and react for the frontend. passport local auth for authentication. I have a problem where authentication doesn't persist when I use the fetch api in react. When I use postman to make the post request everything is alright and my auth status returns true. Pretty sure passport initialization is in order because in postman it works just fine. The confusing thing is that I use the same body for both.
POST request in express:
router.post('/login', user_controller.user_login_post, (req, res) => {
console.log(req.body);
if (!req.user) {
console.log('User not found!');
res.send(req.body);
} else {
console.log('Signed in');
res.send(req.body);
}
});
login_post in controller:
exports.user_login_post = passport.authenticate('local');
```
Auth checking in express/passport:
```
app.get('/api/checkauthentication', (req, res) => {
req.isAuthenticated()
? res.status(200).json({ authenticated: true })
: res.status(401).json({ authenticated: false });
});
```
Function I'm calling on submit in React:
```
const login = (data) => {
fetch('/api/users/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
credentials: 'include',
})
.then((response) => response.json())
.then((data) => console.log('DATA: ', data))
.catch((err) => {
console.log('Error: ', err);
});
};
```
Also 'Signed in' gets logged out but auth only persists when I make the request from postman.
This problem appears because you using localhost. Hope it helps somebody.

How to send/extract JWT token in nodejs with passport-jwt?

I've tried to check if they're online examples of how to use JWT extractors to get the token from the request but I failed to understand how to send the token with the request after the user logins.
When I use Postman, there's a tab called Authorization where I can choose the type Bearer Token which enabled me to add the token with the Authorization and the request http://localhost:5000/profile went successfully.
However, the browser stills showing me only Unauthorized when I try to access the profile http://localhost:5000/profile after successful login.
POSTMAN SCREEN-SHOT:
BROWSER SCREEN-SHOT:
I've followed the passpot-jwt documentation configuration:
passport.use(
new JWTStrategy(
{
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: "mysecret",
},
function (jwtPayload, done) {
return User.findOne({ username: jwtPayload.username })
.then((user) => {
return done(null, user);
})
.catch((err) => {
return done(err);
});
}
)
);
And my login route looks like :
Router.post("/", (req, res, next) => {
passport.authenticate("local", { session: false }, (err, user, info) => {
if (err) return next(err);
if (!user) {
return res.redirect("/login?info=" + info);
}
req.logIn(user, { session: false }, (err) => {
if (err) return next(err);
const token = jwt.sign({ username: user.username }, "mysecret");
res.json({ user, token: `Bearer ${token}` });
});
})(req, res, next);
});
The issue is:
I was trying to access the profile without adding the Authorization in the header from the server itself. The Authorization contains the generated token.
With Postman I was able to do that with the UI as explained above. However, in the code, I needed to create a middleware before accessing the profile route.
app.use(
"/profile",
(req, res, next) => {
req.headers.authorization = `Bearer ` + req.cookies["authentication-token"];
next();
},
profileRouter
);
After login, you can send Authorization Token in headers
(function () {
fetch("http://localhost:5000/profile", {
method: "GET",
headers: {
"Content-Type": "text/plain",
"X-My-Custom-Header": "value-v",
Authorization:
"Bearer " + Token,
},
});
})();
Hope you got some idea.

How to handle login requests to a Node server using PassportJS for Google and Facebook OAUth?

I am using Passport solely for redirecting the OAuth requests to Google/Facebook permissions page and generating a JWT token in the success callback. I am explicitly setting {session: false} in the routes using passport.authenticate().
The success callback function looks something like:
router.get(
"/auth/facebook/callback",
passport.authenticate("facebook", {
failureRedirect: "/api/auth/fail",
session: false,
}),
async (req, res) => {
const user = { email: req.user?.email };
res.json({
status: true,
message: "User logged in",
data: { token: jwt.sign(user, sign, { expiresIn: "14d" }) },
});
}
);
I am using my own authentication middleware which looks like:
const authMiddleware = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
if (!req.body.token) {
return res.json({ status: false, message: "Login required" });
}
req.currentUser = jwt.verify(req.body.token, sign);
next();
} catch (err) {
console.log("ERROR:" + err);
return res.json({ status: false, message: "Please login again" });
}
};
Google has a specific way of implementing Cross-client authentication similar to this use case mentioned n Google OAuth 2.0 documentation. However, I could not find something similar for Facebook.
Is there a way to handling the redirect on a Flutter app? Or should I think of a different implementation on the server-side of things? If the latter, what is recommended in this use case?

Passport.js logout session destroy works with POSTMAN but not with the React app. How to fix this

I have an Express backend and trying to set up Passport authentication. Login process works fine and the session token is stored as a cookie. When logging out, clearCookies don't destroy the cookies in the browser. However the process works just fine when I make the calls via Postman.
I've set { withCredentials: true }in the axios config and { credentials: true } in the CORS settings in the backend. I've also set the path in the clearCookies options.
// BACKEND LOGOUT
exports._logout = (req, res) => {
req.session.destroy(err => {
if (err) res.status(422).send(err);
req.logout();
res.clearCookie("connect.sid", {domain: 'localhost', path:
'/'}).status(200).send('Logged out!.')
});
};
// FRONTEND LOGOUT (REACT/REDUX)
export function logOut() {
return {
type: 'LOG_OUT',
payload: new Promise((resolve, reject) => {
axios
.post(process.env.ACCOUNT_HOST + `/logout`, {withCredentials: true})
.then(response => {
console.log(response);
resolve(response.data);
})
.catch(error => {
notification.error({
message: 'Log In Error',
description: error.message,
duration: null
});
reject(error);
});
})
};
}
When I call the endpoint with Postman it clears the cookies and isAuth function returns the expected. Any ideas why it's not clearing the cookies in the browser? Thank you.

react & node API - fetch function error

I'm just new in React and NodeJS express programming and I'm stuck in some error for hours.
when I'm trying to fetch, sometimes I got a response (but not the my response)
and sometimes I got 'failed to fetch'.
I have tried to understand 'cors' concept without a lot of understanding,
but I have commit app.use(cors()) that allowed every source.
note that my server sometimes got 'OPTIONS' request, and sometimes getting 'POST' request.
in my React, I have this function:
fetch('http://localhost:3000/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify ({
user: this.state.username,
pass: this.state.password,
})
}).then(res => {
console.log(res.msg);
})
.catch(function(err) {
alert(err);
})
}
in my NodeJS I have this function:
router.post('/', (req, res) => {
let user = {
username: req.body.user,
password: req.body.pass
}
UserModel.findOne({'username': user.username, 'password' : user.password}, function(err, user) {
if (err) {
res.json({
ok: false,
msg: 'login failed, try later'
});
} else if (user) {
let token = jwt.encode(user, secret);
res.json({
msg: 'logged in',
ok: true
});
} else {
res.json({
ok: false,
msg: 'invalid input'
});
}
});
Im very confused, I hope you can help me.
Thanks.
You aren't accessing the JSON of the response correctly. You need to call the Body#json method of the response to actually access the JSON content. Also note that it returns a promise that resolves to the JSON content of a response:
fetch('…', { … })
.then(res => res.json())
.then(json => {
console.log(json.msg);
});
Also, I recommend sending an HTTP status code along with the JSON instead of having an ok property. That is a standard way and has builtin support through the Response object:
res.status(200).json({ … });

Resources