My Express app calls a request.isAuthenticated() method. However, I don't know what it checks to determine whether it's authenticated. My app needs to authenticate via OIDC. How do I tell the isAuthenticated() method that it passed OIDC authentication?
Currently, I have it set to redirect to the OIDC authorize endpoint with an appropriate client_id, scope. The user's browser follows the redirect, the user successfully logs in. OIDC sends a redirect back to the callback provided by my Express app. The users browser reaches this endpoint successfully.
My consolidated file in one posting below. Because I'm new to Node, it's sloppier than I'd like. Also, because I can't get Visual Code to catch my breakpoints (see my other related post), I can only debug using console.log statements.
If I go to /cost-recovery in the browser, it goes to this route:
app.use('/cost-recovery*', saveUrlInSession, /*ensureAuthenticated*/ isLoggedIn,createProxyMiddleware(sprint_cost_recovery_options));
It saves the URL in the session, allowing the callback to go where I want. That works. In both the ensureAuthenticated and isLoggerdIn handlers, the system redirects to the OIDC/OpenId/?? ID login page. I'm able to log in, and it goes back to my callback page. In that callback route, the req.isAuthenticated() still says false.
Perhaps because I'm using this passport module, it has no idea that the login happened. There are cookies being set, in a pinch I can just check those in lieu of a working req.isAuthenticated() method, but I'd rather use the tools provided.
/**
* How the application respond to clients requests depending of the endpoint
*/
const userController = require('../controllers/userController');
var OpenIDConnectStrategy = require('passport-ci-oidc').IDaaSOIDCStrategy;
const strategyConfiguration = require('../../config/strategy.json');
console.log('strategyConfiguration=' + JSON.stringify(strategyConfiguration));
const passport = require('passport');
const https = require('https');
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) {
console.log('App setting title=' + app.get('title'));
console.log('App env=' + app.get('env'));
console.log('App setting query parser=' + app.get('query parser'));
console.log('App setting string routing=' + app.get('strict routing'));
console.log('App setting case sensitive routing=' + app.get('case sensitive routing'));
var http = require('http');
var url = require('url');
var currentOriginalUrl;
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (obj, done) {
done(null, obj);
});
// openid-client is an implementation of the OpenID Relying Party (RP, Client) server
// for the runtime of Node.js, support passport
//OAuth 2.0 protocol
//middleware Passport-OpenID Connect
const config = require('../configuration/config').getConfiguration();
console.log('config=' + JSON.stringify(config));
console.log('strategyConfiguration=' + JSON.stringify(strategyConfiguration));
var OpenIDConnectStrategy = require('passport-ci-oidc').IDaaSOIDCStrategy;
var Strategy = new OpenIDConnectStrategy({
discoveryURL: strategyConfiguration.discoveryURL,
clientID: strategyConfiguration.clientID,
scope: 'openid',
response_type: 'code',
clientSecret: strategyConfiguration.clientSecret,
callbackURL: strategyConfiguration.callbackURL,
skipUserProfile: true, /* this was true before */
CACertPathList: [
`/certs/DigiCertGlobalRootCA.crt`,
`/certs/DigiCertSHA2SecureServerCA.crt`,
]
},
function (iss, sub, profile, accessToken, refreshToken, params, done) {
process.nextTick(function () {
profile.accessToken = accessToken;
profile.refreshToken = refreshToken;
const userDetails = profile._json;
const userProfile = {
uid: userDetails.uid,
mail: profile.id,
cn: decodeURIComponent(userDetails.cn),
exp: 60 * 60 /*TODO: Get proper number of seconds. userDetails.exp */,
blueGroups: userDetails.blueGroups,
};
done(null, userProfile);
})
}
)
var proxy_server = require('http-proxy').createProxyServer({});
const originalUrl = new URL(config.host);
console.log('matched cost-recovery using original url: ' + originalUrl);
const newUrl = new URL(originalUrl);
newUrl.port = 8447;
console.log('matched cost-recovery new url' + newUrl);
function saveUrlInSession(request, response, next) {
if (request.params.state) {
console.log('Saving state=' + request.params.state + " in session");
request.session.savedUrl = request.request.params.state;
} else {
console.log('Saving originalUrl=' + request.originalUrl + " in session");
request.session.savedUrl = request.originalUrl;
}
if (next) {
return next();
} else {
console.log('@@ no next');
}
}
function ensureAuthenticated(req, res, next) {
if (!req.isAuthenticated()) {
console.log('@@ ensureAuthenticated reached. Not authenticated. redirecting to /login');
res.redirect('/login')
} else {
console.log('@@ ensureAuthenticated reached. Authenticated. Continuing to next handler');
return next();
}
}
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
console.log('@@isLoggedIn req.isAuthenticated()=true');
req.session.isAuthenticated = true;
res.locals.isAuthenticated = true;
res.locals.user = req.user;
next(); //If you are authenticated, run the next
} else {
console.log('@@isLoggedIn req.isAuthenticated()=false');
return res.redirect("/login");
}
}
function getUserProfile(req, res, next) {
console.log('@@ reached getUserProfile')
if (typeof req.user == 'undefined') {
res.status(401);
next();
}
return res.status(200).send(req.user);
}
function getUserName(req, res, next) {
console.log('@@ reached getUserName')
if (typeof req.user === 'undefined') {
res.status(401);
return next();
}
return res.status(200).send(req.user.cn);
}
var newURL = url.format({
protocol: config.protocol,
host: config.host,
pathname: config.originalUrl
});
console.log('newURL=' + newURL);
var newURL2 = new URL(newURL);
newURL2.port = "8447";
newURL2.protocol = "http";
console.log('newURL2=' + newURL2);
const sprint_cost_recovery_options = {
target: newURL2,
level: 'debug',
changeOrigin: true,
ws: true
}
console.log('@@ sprint_cost_recovery_options=' + JSON.stringify(sprint_cost_recovery_options));
passport.use(Strategy);
app.use(passport.initialize());
app.use(passport.session());
app.use(function (request, response, next) {
console.log('Common Route: Incoming request originalUrl:' + request.originalUrl);
console.log('Common Route: Incoming request previous Url:' + request.header('referer'));
console.log('Common Route: Incoming request url:' + request.url);
next();
});
app.get('/auth/sso/callback/:callback_uri?'
, function (request, response, next) {
console.log('CB-1 matched on originalUrl=' + request.originalUrl);
console.log('@@ CB-2. isAuthenticated=' + request.isAuthenticated());
console.log('@@ CB-2.5 request.account test=' + request.account);
console.log('@@ savedUrl in session=' + request.session.savedUrl);
//var redirectUrl = poppedUrlFromSession(request);
var redirectUrl = request.session.savedUrl;
if (!redirectUrl) {
redirectUrl = "/health-check";
}
console.log('@@ CB-3. redirectUrl=' + redirectUrl);
console.log('@@ CB-4. before passport.authenticate');
console.log('@@ CB-5. after passport.authenticate');
console.log('@@ CB-6. isAuthenticated=' + request.isAuthenticated());
console.log('@@ auth-sso-callback-2 bp1');
response.redirect(redirectUrl);
}
);
app.use('/login?:state?',
function (request, response, next) {
var stateIndicator = (request.params.state) ? " with state " + request.params.state : " with no state/redirect.";
console.log('@@ Reached login with ' + stateIndicator);
return next();
},
passport.authenticate('openidconnect', { state: Math.random().toString(36).substr(2, 10) }));
app.use('/rules/username', saveUrlInSession, ensureAuthenticated, userController.getUserName);
app.use('/rules/profile', saveUrlInSession, ensureAuthenticated, userController.getUserProfile);
app.use('/cost-recovery*', saveUrlInSession, /*ensureAuthenticated*/ isLoggedIn,createProxyMiddleware(sprint_cost_recovery_options));
app.use('/profile', saveUrlInSession, ensureAuthenticated, getUserProfile);
app.use('/username', saveUrlInSession, ensureAuthenticated, getUserName);
app.get('/successful-login', function (req, res) {
res.send('login succeeded');
});
app.get('/failure', function (req, res) {
res.send('login failed');
});
app.get('/health-check', (request, response) => {
response.send('Middleware is running.');
});
};