I am building an Web app where i have okta to login using redirect model + OIDC. I am able to successfully login and perform 2fa and create an access and ID token.
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');
// source and import environment variables
require('dotenv').config({ path: '.okta.env' })
//require('dotenv').config({ path: '.entra.env' })
const { ORG_URL, CLIENT_ID, CLIENT_SECRET } = 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
}));
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);
// 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: '/authorization-code/callback',
scope: 'profile offline_access',
maxAge: 0
}, (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) => {
res.redirect('/profile');
}
);
app.use('/profile', ensureLoggedIn, (req, res) => {
res.render('profile', { authenticated: req.isAuthenticated(), user: req.user, title: 'Profile' });
});
app.use('/auth/2fa', ensureLoggedIn, (req,res) =>{
console.log ("******In MFA Page******");
passport.authenticate('oidc', {
scope: ['openid', 'profile'],
prompt: 'login',
callbackURL: '/authorization-code/callback/2fa'
})(req,res);
});
app.use('/authorization-code/callback/2fa',
// https://github.com/jaredhanson/passport/issues/458
passport.authenticate('oidc'),
(req, res) => {
console.log("In req");
console.log(req);
res.redirect('/secure_page')
}
);
app.get('/secure_page', (req, res) => {
if (!req.isAuthenticated()) {
return res.redirect('/login');
}
res.send('<h1>2FA Success! You are on a secure page.</h1>');
});
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;
Now customer is in Profile Page with a valid session which has a button to invoke 2FA. When user clicks on button it invokes the below code:
app.use('/auth/2fa', ensureLoggedIn, (req,res) =>{
console.log ("******In MFA Page******");
passport.authenticate('oidc', {
scope: ['openid', 'profile'],
prompt: 'login',
callbackURL: '/authorization-code/callback/2fa'
})(req,res);
});
It successfully runs the above code and displays the login screen first and then the 2FA using SMS. Once i have successfully entered the credentials it throws an error as below:
The ‘redirect_uri’ does not match the redirection URI used in the authorization request. (invalid_grant)
It never calls the below method:
app.use('/authorization-code/callback/2fa',
// https://github.com/jaredhanson/passport/issues/458
passport.authenticate('oidc'),
(req, res) => {
console.log("In req");
console.log(req);
}
);
I have both the URLs slated below specified in OKTA under the app.
http://localhost:3000/authorization-code/callback
http://localhost:3000/authorization-code/callback/2fa
Ideally i want the app to redirect to
http://localhost:3000/authorization-code/callback/2fa and execute the below code.
app.use('/authorization-code/callback/2fa',
// https://github.com/jaredhanson/passport/issues/458
passport.authenticate('oidc'),
(req, res) => {
console.log("In req");
console.log(req);
res.redirect('/secure_page')
}
);
app.get('/secure_page', (req, res) => {
if (!req.isAuthenticated()) {
return res.redirect('/login');
}
res.send('<h1>2FA Success! You are on a secure page.</h1>');
});
I am not sure what i am doing wrong here. Any help would be greatly appreciated.
1