I am following your post to implement Step-Up authentication but running into roadblocks. After login with username/password and okta verify, when user tries to access a risky transcation, i am asked for username and password again rather than just okta verify. Do you know what could be the reason. Below is my code.
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var session = require('express-session');
var passport = require('passport');
var qs = require('querystring');
var { Strategy } = require('passport-openidconnect');
const axios = require('axios');
const crypto = require('crypto');
// source and import environment variables
require('dotenv').config({ path: '.okta.env' })
//require('dotenv').config({ path: '.entra.env' })
const { ORG_URL, CLIENT_ID, CLIENT_SECRET, CALLBACK_URL } = process.env;
var indexRouter = require('./routes/index');
const { profile } = require('console');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret: 'CanYouLookTheOtherWay',
resave: false,
saveUninitialized: true,
// cookie: { secure: true, sameSite: false }
}));
// app.set("trust proxy", 1);
app.use(passport.initialize());
app.use(passport.session());
// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest
let logout_url, id_token;
let _base = ORG_URL.slice(-1) == '/' ? ORG_URL.slice(0, -1) : ORG_URL;
console.log ('Base URL - ' + _base);
axios
.get(`${_base}/.well-known/openid-configuration`)
.then(res => {
if (res.status == 200) {
let { issuer, authorization_endpoint, token_endpoint, userinfo_endpoint, end_session_endpoint } = res.data;
logout_url = end_session_endpoint;
console.log ('Authorization Endpoint - ' +authorization_endpoint);
const riskyActionInfo = {
action: 'Login Button',
referURL: '/',
targetURL: '/profile'
};
//const state = JSON.stringify(riskyActionInfo);
const state = JSON.stringify(riskyActionInfo);
// Set up passport
passport.use('oidc', new Strategy({
issuer,
authorizationURL: authorization_endpoint,
tokenURL: token_endpoint,
userInfoURL: userinfo_endpoint,
clientID: CLIENT_ID,
clientSecret: CLIENT_SECRET,
callbackURL: CALLBACK_URL,
scope: ['openid', 'profile', 'email'],
state: true,
nonce: true,
acrValues:'urn:okta:loa:1fa:pwd',
maxAge: 5,
}, (issuer, profile, context, idToken, accessToken, refreshToken, params, done) => {
console.log(`OIDC response: ${JSON.stringify({
issuer, profile, context, idToken,
accessToken, refreshToken, params
}, null, 2)}\n*****`);
id_token = idToken;
return done(null, profile);
}));
}
else {
console.log(`Unable to reach the well-known endpoint. Are you sure that the ORG_URL you provided (${ORG_URL}) is correct?`);
}
})
.catch(error => {
console.error(error);
});
passport.serializeUser((user, next) => {
next(null, user);
});
passport.deserializeUser((obj, next) => {
next(null, obj);
});
function ensureLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login')
}
app.use('/', indexRouter);
app.use('/login', passport.authenticate('oidc'));
app.use('/authorization-code/callback',
// https://github.com/jaredhanson/passport/issues/458
passport.authenticate('oidc', { failureMessage: true, failWithError: true }),
(req, res) => {
console.log ("IN Callback Handler");
const returnedState = req.query.state;
let st = req.authInfo.state;
console.log("State :" + st);
if (st === undefined) {
console.log("state is null or undefined");
res.redirect('/profile');
}
else {
console.log(JSON.parse(st).targetURL);
var url = JSON.parse(st).targetURL;
res.redirect(url);
}
}
);
app.use('/profile', ensureLoggedIn, (req, res) => {
res.render('profile', { authenticated: req.isAuthenticated(), user: req.user, title: 'Profile' });
});
app.use('/secure_page', ensureLoggedIn, (req, res) => {
res.render('secure_page', { authenticated: req.isAuthenticated(), user: req.user, title: 'Secure Page' });
});
app.use('/auth/2fa', ensureLoggedIn, (req,res) =>{
console.log ("******In MFA Page******");
const state = JSON.stringify({ action: 'secure_page', targetURL: '/secure_page' });
const nonce = crypto.randomBytes(16).toString('hex');
passport.authenticate('oidc', {
scope: ['openid', 'profile', 'email','offline_access'],
state: state,
nonce: nonce,
acrValues: 'phrh',
maxAge:'1',
})(req, res);
});
app.get('/logout', (req, res) => {
req.logout();
req.session.destroy();
res.redirect('/');
});
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message + (err.code && ' (' + err.code + ')' || '') +
(req.session.messages && ": " + req.session.messages.join("\n. ") || '');
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;```