So, after reading my stackoverflow posts, auth0 community posts and trying multiple ideas i’m still stumped.
I’ve had an nodejs express app (and rest api) running for many years, and one thing that has never worked (and that i’ve lived with) is that if a user visits a url that i’ve protected with middleware it always redirected back to the root of the site after login rather than the requested url.
i’d like to say i’ve narrowed down the problem to a particular area, but i’m not sure i have;
anyway;
i have all the usual setup in my app.js file:
var strategy = new Auth0Strategy(
{
domain: process.env.AUTH0_DOMAIN,
clientID: process.env.AUTH0_CLIENTID,
clientSecret: process.env.AUTH0_CLIENT_SECRET,
callbackURL: process.env.AUTH0_CALLBACK_URL || 'http://127.0.0.1:3000/callback'
},
function (accessToken, refreshToken, extraParams, profile, done) {
// accessToken is the token to call Auth0 API (not needed in the most cases)
// extraParams.id_token has the JSON Web Token
// profile has all the information from the user
return done(null, profile);
}
);
passport.use(strategy);
// You can use this section to keep a smaller payload
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (user, done) {
done(null, user);
});
// config express-session
var sess = {
secret: 'ThisisMySecret',
cookie: {
httpOnly:false,
secure:false
},
resave: false,
saveUninitialized: false
};
if (app.get('env') === 'prod') {
console.log("prod environment")
app.set('trust proxy', 1); // trust first proxy
sess.cookie.secure = true; // serve secure cookies, requires https
sess.proxy = true;
// console.log("session:sess");
// console.log(sess);
}
app.use(session(sess));
app.use(passport.initialize());
app.use(passport.session());
a login route:
router.get('/login', function(req, res, next) {
req.session.returnTo = req.query.returnTo; // Store the returnTo value in session
console.log("inside login route: " + req.session.returnTo)
const state = uuidv4();
passport.authenticate('auth0', {
scope: 'openid email profile',
state: state // Pass the returnTo value as the state parameter
})(req, res, next);
});
a callback route:
router.get('/callback', function(req, res, next) {
console.log("inside callback route: " + req.session.returnTo)
passport.authenticate('auth0', function(err, user, info) {
console.log("inside callback authenticate function: " + req.session.returnTo)
console.log(user)
console.log(info)
if (err) { return next(err); }
if (!user) {
console.log(user)
console.log(info)
res.render('beta/failed-login', {
static_path:'/static',
theme:process.env.THEME || 'flatly',
pageTitle : "Access Denied",
pageDescription : "Access Denied",
query:req.query
});
} else {
req.logIn(user, function (err) {
if (err) {console.log(err); return next(err); }
console.log("inside callback route: " + req.session.returnTo)
console.log("user inside callback route: " + user)
const returnTo = req.session.returnTo || '/'; // Retrieve the returnTo value from session
delete req.session.returnTo; // Remove the returnTo value from session
res.redirect(returnTo);
});
}
})(req, res, next);
});
some middleware:
function secured(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
console.log("query in middleware: " + req.query.state)
console.log("originalUrl in middleware: " + req.originalUrl)
const returnTo = req.query.state || req.originalUrl;
req.session.returnTo = returnTo; // Store the returnTo value in session
console.log("returnTo in middleware: " + returnTo)
console.log("session.returnTo in middleware: " + req.session.returnTo)
res.redirect('/login?returnTo=' + encodeURIComponent(returnTo));
}
and an example protected route (of many):
router.get('/user', secured,async function (req, res) {
const { _raw, _json, userProfile } = req.user;
console.log(req.user)
res.render('beta/user', {
userProfile: JSON.stringify(userProfile, null, 2),
static_path:'/static',
theme:process.env.THEME || 'flatly',
pageTitle : "User Profile",
pageDescription : "User Profile",
});
});
and i get the following in my logs when i visit /user
Server running at http://127.0.0.1:3000/
query in middleware: undefined
originalUrl in middleware: /user
returnTo in middleware: /user
session.returnTo in middleware: /user
inside login route: /user
inside callback route: /user
inside callback authenticate function: /user
false
{ message: 'Unable to verify authorization request state.' }
false
{ message: 'Unable to verify authorization request state.' }
what i have found is that if i remove
state: state // Pass the returnTo value as the state parameter
from the object passed to the passport.authenticate call in the /login route the login works, but now it doesn’t redirect…
so i’m stumped, i get this behaviour on both the local environment and my production environment which is hosted on heroku (not sure if that’s related…) i had hoped it was just a local build problem related to https which i haven’t put effort into configuring locally yet, but my heroku build is setup for https (https://stockport-badminton.co.uk)
any ideas?