I am using the redirect model to have user to login via okta. As part of the the login process, the user enters login credentials and ask for 2FA using okta verify. Once the user successfully logged in have an authenticated session, when a user try to access a restricted resource which is the
/auth/2fa
i would like to invoke the step-up authentication again using okta verify only. But i get asked for password and okta Verify, which is similar to re-authentication rather than step up. What could i be doing wrong here?
I am not sure if it is due to a authentication policy setup. If so can you share me what is the right way to setup the policy? I am currently blocked here and not able to progress further.
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,
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:'0',
})(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;
``` Step-up Invocation not happening