Passport.js Azure AD auth always fails the second time - node.js

So i'm using Passport.js in Express to authenticate users in an app with the passport-azure-oauth2 strategy.
The first login works fine, the Microsoft portal takes me back to /cb which is
app.get("/cb", auth.passport.authenticate("provider", {
successRedirect: "/",
failureRedirect: "/login"
}), function (req, res) {
res.redirect("/");
});
And it successfully redirects me to '/'. At this point I can log out 'Logged in' via this
if(req.isAuthenticated()) {
console.log('Logged in');
} else {
console.log('Not logged in');
}
The issue comes when I either log out and try and log back in again, or try and log in with a different browser. The Microsoft portal always takes me back to '/login', leaving me in a loop.
The login script is simply:
app.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
On Chrome, when I log in (even successfully) it logs an error:
Set-Cookie header is ignored in response from url: https://login.microsoftonline.com/common/login. Cookie length should be less than or equal to 4096 characters.
I'm guessing the issue might be something to do with this? But i've tried everything I can think of to no avail.

it's been a while since you have asked this, but maybe it could be helpful for someone else.
After a day of debugging i found out that the service worker of my react app was messing with the passport authentication strategy.
As you have described, when you load the page for the first time the service worker is not registered yet, but when you get redirected to the homepage the script in the index.jsx enable the worker in the browser (i used it to display the install app button on chrome), so if you logout and try to login again this issue comes along.
If you are in a rush i suggest you to temporary disable the worker if it's not very important for the right functioning of your app and you'll see that everything works fine.
It works on localhost because the script detects your environment and it does not start.
Unfortunately i have not found a permanent solution yet.

Related

Passport-auth0 - infinite redirect loop, "Invalid authorization request state."

I'm trying to get a basic Auth0 app running. I followed the Node example on the Auth0 dashboard but I ran into an infinite redirect loop between /login and /callback. I tried to simplify the code using the getting started example of this repo's readme, right now my routing code looks like this:
app.get('/login',
passport.authenticate('auth0', {scope: 'openid email profile'}),
(req, res) => res.redirect("/")
)
app.get("/callback",
passport.authenticate('auth0', {failureRedirect: '/login'}),
(req, res) => {
if(!req.user) throw new Error("user null")
res.redirect("/")
}
)
Everything about my setup follows the instructions I got on my Auth0 dashboard.
I did some digging and found out that /login is called twice, then /callback is called twice, then /login twice and so on and so on. I also found out that if I give the /callback's passport.authenticate a callback, it receives these arguments: null, false, {message: "Invalid authorization request state."}
Google didn't find anything meaningful when I searched for the phrase "Invalid authorization request state." and I did everything according to the docs. Any idea what I'm doing wrong?
I have had the same issue with these error and was caused due to session loss on Kubernetes landing my calls to different instances each time (sort of like load balancing), what we did was to lower from 3 instances to 1 and that fixed it. Also you could set in your Auth0Strategy config the value of state: false to double check if this lets you move on.
It turns out I didn't have the /callback route set up properly. This is the proper setup:
router.get("/callback", (req, res, next) => {
passport.authenticate('auth0', (err, user, info) => {
if(err) return next(err)
if(!user) return res.redirect("/failure?info=" + JSON.stringify(info))
req.logIn(user, err => {
if(err) return next(err)
const returnTo = req.session.returnTo
delete req.session.returnTo
res.redirect(returnTo || '/secret')
})
})(req, res, next)
})
res.redirect should lead somewhere else and req.logIn needs to be called with the proper arguments
For those who are still having issues, the problem might lie elsewhere. When I ran into this it turned out to be caused by session cookies that weren't persisting! It turned out my reverse proxy (running on Apache) wasn't configured properly. I've described my solution here: https://stackoverflow.com/a/67891167/8221175
As for why this might happen for those interested, I've posted an explanation on one of the GitHub issues here: https://github.com/auth0/passport-auth0/issues/89#issuecomment-856971807
In short: The state query parameter in the response of a login call is saved in the session. If sessions don't persist, passport can't get that state data and it'll think someone is hijacking your session. That causes the “Invalid authorization request state.” message, as well as the user to be falsy in the passport.authenticate callback. If your code then redirects to '/login', the whole thing repeats, causing an infinite loop.
The fix is to make sure your cookies are saved properly and your sessions therefore persist. You can use the examples I've provided in the links.

How do I redirect a failed login attempt in node-oidc-provider

I'm setting up an OpenID Connect Provider using node.js, express.js, and node-oidc-provider. I've been working my way through the examples at https://github.com/panva/node-oidc-provider-example/tree/master/03-oidc-views-accounts, but it never deals with a failed authentication. How do I redirect the user back to the login page if they mis-type their password?
expressApp.get('/interaction/:grant', async (req, res) => {
// The initial route hit by the client (Relying Party) that renders the login view if needed.
...
});
expressApp.post('/interaction/:grant/login', parse, (req, res, next) => {
User.authenticate(req.body.email, req.body.password)
.then((users) => {
// returns an array of user objects that match the credentials
if(!users.length)
{
// What now? I can't just redirect back to /interaction/:grant - I get session not found
}
// the rest works well enough (for now)....
...
}).catch(next);
});
Just like in any express app. Think of it this way. Only resolve the interactions with success, or error if you wish to exit the interactions and return control back to the client.
I tend to develop interactions separately and only plug them to oidc-provider when they’re done.

node sessions not clearing in production

I am using client-sessions with node and express, and I am having trouble logging out users.
my logout function does a
req.session.user=null
that is:
userRouter.get('/logout', function(req, res, next) {
req.session.user = null
res.sendStatus(200)
});
(I also tried delete req.user, req.user.destroy(), and other variants).
in dev env, on localhost, it works great.
in production environment, logout is called, but the next time the user refreshes the page on the browser, the user is still logged in.
same code in dev and production.
Anybody has any idea?
thanks....
According to this blog post on client-sessions, you can use this:
userRouter.get('/logout', function(req, res, next) {
req.session.reset();
res.sendStatus(200);
});
so it the end the issue was that I did not have
headers: {
'Accept': 'application/json',
},
in the call from the client. I a bit puzzled by this, it seems as the server would set the session (through nginx) only if the client had this header, while in localhost it sets sessions correctly without this.

How to check if user is already logged into instagram

I am currently using node.js, express, and mongodb for an instagram app. I have found that many times I would like to know whether or not a user is already logged into instagram (be it through my app via the instagram-node authentication or through instagram's actual website).
Is there an easy way to do this?
I ended up using passport to solve this problem. Passport conveniently takes care of handling instagram authorization and they even include an example app to see how it all works. https://github.com/jaredhanson/passport-instagram/blob/master/examples/login/app.js
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
Is especially useful since it can be placed at the top of your routing file and all the routes underneath it will first check to see if the user is authenticated.

How can I specify what PassportJS does after a callback without using ExpressJS?

The PassportJS website has the following code example, where somebody can specify their own flow of logic after a callback goes through:
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/');
});
However, I am not using Express, and am finding it very difficult to replicate the above behavior.
Here is my current code that deals with facebook authentication callbacks:
if(path == '/auth/facebook/callback'){
passport.authenticate('facebook', {failureRedirect: '/failbook', 'successRedirect':'/success'})(req, res, next);
return;
}
I have no idea how to change it so that I can alter the flow of logic post-callback. I'd like to be able to check some variables that I have saved and redirect to different locations accordingly. Any thoughts on how I can go about doing this?
Also - on a related note - if I'm just refreshing the facebook access token (AKA if it has expired and I'm getting a new one), is it possible to not redirect the user anywhere at all, so that their browser doesn't go to a different page at any point in time during the refreshing process (which involves a re-authentication with facebook)?
check this https://groups.google.com/forum/#!topic/passportjs/HTG13e2pb74
its a group which uses passport with node "vanilla"

Resources