Reputation: 45
following this tutorial I have build my azure ad login to use the graph api https://learn.microsoft.com/en-us/graph/tutorials/node?tutorial-step=3
The response from azure looks got to me and I can't find any error message but it always triggers the failure redirect and therefore it does not proceed with the signInComplete function.
I was not able to figure out what's wrong. Any hint where I could look to at least to get an idea what triggered the failureRedirect?
// Callback function called once the sign-in is complete
// and an access token has been obtained
async function signInComplete(iss, sub, profile, accessToken, refreshToken, params, done) {
logger.debug("signInComplete function called")
if (!profile.oid) {
logger.error("No OID found in user profile.")
return done(new Error("No OID found in user profile."));
}
return done(null);
}
// Configure OIDC strategy
passport.use(new OIDCStrategy(
{
identityMetadata: `${config.OAUTH_AUTHORITY}${config.OAUTH_ID_METADATA}`,
clientID: config.OAUTH_APP_ID,
responseType: 'code id_token',
responseMode: 'form_post',
redirectUrl: config.OAUTH_REDIRECT_URI,
allowHttpForRedirectUrl: true,
clientSecret: config.OAUTH_APP_PASSWORD,
validateIssuer: false,
passReqToCallback: false,
scope: config.OAUTH_SCOPES.split(' '),
loggingLevel: 'warn'
},
signInComplete
));
office365Signin: function (req, res, next) {
passport.authenticate('azuread-openidconnect',
{
response: res,
prompt: 'login',
failureRedirect: '/',
failureFlash: true,
successRedirect: '/'
}
)(req,res,next);
},
office365SigninCallback: function(req, res, next) {
passport.authenticate('azuread-openidconnect',
{
response: res,
failureRedirect: process.env.FRONTEND_URL+'fail',
failureFlash: true,
successRedirect: process.env.FRONTEND_URL
}
)(req,res, next);
//failureRedirect gets always trigger even if the req.body looks correct and no error message is send
//Therefore we just check the body and trigger next if the body looks correct
var util = require('util');
logger.debug("office365SigninCallback res: "+util.inspect(res.req.body));
if (res.req.body.code && res.req.body.id_token) {
logger.debug("req.body seems correct there");
}
},
Logger office365SigninCallback res.req.body output:
office365SigninCallback res: { code: 'OAQABAAIAAAAm-06blBE1TpVMil8KPQ41qTM1deUhK_bLgEaGpsiIg5_3sa0ZNEBusd3m4rpBCrXflEsSvEtyjWWzqDhQ_9MybvYdqiR5B2FB59Msd7g8uL5YFcAExrGDqLzYo8xVIaZexHej_K3gDdJFfXbZZsiL6umdepdEXa1pyPIv4S8xVRHPcTyoB80RxpPp97uBCZagR7WstIF0QkfauUxklwlmOygAWjFvIMTuSijkkVZZ-04MbSX6wT3vBwJmQ2-kj6x_W_9fdCbYtdavgR6ZlYKtdiAxVm-3qULweEfvFo8RVC5xV2wdaPKqqYh41lcAq_1NHCiTdUcmmxbk8177WGzabDbH-rM-jRzORamSbLg_0vF48KWqu9zSgiCTX4RW556akFo6pcSkpriJWZH1aVl1cSMTWM64zb9tRM08O7hJ9YyFGTM-n6RpIiA3h9-Xh1E_TEZ8sG0noVId3yN8-gJXZ-pEB7Bur8s5C3DFOOlPgqgdEDj16tM8Wg0RinaL8P1BJ18k_Y_pr-huHMzhKaVCLYCX1Urq8fDomv0UAVchDQNIjdQ5PfiiYIT-0GYYzR5BB_5wGKJgwZypae89RRXpNJw-XOY5dv10jsUk3jEHRXW5xle2HtpM5DgCs6VbuxwVuaJfrRhfNdy7WkoOT3caV-4qTYfpfqwKvX_YtdD15RMpg-BVZQyI8b12meomlHdRi2aiqwqpTfJas0mrE7jHeScQErWx0qWAhvnZS8JJbauJGbXvjCbl2Tcoh19ngaggAA',
id_token:
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkN0VHVoTUptRDVNN0RMZHpEMnYyeDNRS1NSWSJ9.eyJhdWQiOiI1YTYxYTMwZC00MTAyLTQwMGUtYTVmNS05NDJlYzAxNDE3ODciLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vZTFkMDE4YTktOWRlZC00Yjg4LTllYTItNDJkYTAwY2Q1ZjVhL3YyLjAiLCJpYrRVl4UHJjRk9aVk5tX1N6UHNyX0ZrIiwib2lkIjoiZXQiOjE1ODk4ODA3MTIsIm5iZiI6MTU4OTg4MDcxMiwiZXhwIjoxNTg5ODg0NjEyLCJhaW8iOiJBVFFBeS84UEFBQUEzb0dsSkdWK1dYYlE0elZJNHZic2ZnejZvUWZqYy9XUkNtS1QrYlRORHVjM2dYdll4L3F4OTdEMm5VeTRUUUxNIiwiY19oYXNoIjoiU1k4alM1ZjlxMkg4TU9oSmVIcjNhQSIsIm5hbWUiOiJUaW0gU3RlaW4iLCJub25jZSI6IkZfWjctTmpGSU5jgyYWRkNGMtOGM2MS00YjYxLWJmYWItZmQzNmE2YzY0MjY5IiwicHJlZmVycmVkX3VzZXJuYW1lIjoidHN0ZWluQFZBTlRBLklPIiwic3ViIjoiZ3Q3RExJZ1Y0MEJDZlRxRXhhSlM5Z09qcjhlRWU3TFF0cTJLQk9yNThmbyIsInRpZCI6ImUxZDAxOGE5LTlkZWQtNGI4OC05ZWEyLTQyZGEwMGNkNWY1YSIsInV0aSI6IjlyRHBOM0hBLTAtU3Y2T25XWmpVQUEiLCJ2ZXIiOiIyLjAifQ.uCG5x4cesT2925Kr_lXloYWxgIsPfsRX2FKd4t8ASDeQXg9PdvjTsTvnzzBqFDtW77obSX7bO75a-0XjA9TIh4-kMTgJWm8PlnHCWaHRQgfNlTmjp99oUf0msZx6OhyZ0-xFMMe6DTShFfBhHjF2ds17zw-oynv6PaygSox4s94qvL2e8ULi2wfpm4AYQwxXeUQba9dhoQu8AsCozY-6NyWIGc2alzg7TK5qBpuY16BScGsUkmChGFZ9lF9vD-uM8x0JYg0G6Uvc_aDNIWnt9B7VRH-U9sIFXtL9doaJXvRl2aPQnj6x0rtfgfJ4zonrJZQEn7e8y7XPIcnU0gMO9g',
state: 'GNy7cIjlBvfga4FhQiapnWnDAn8itXtk',
session_state: 'c88bace3-4039-9922-6f06-dcd6ba1a62ac' }
Based on the documentation the response looks correct to me: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
Upvotes: 2
Views: 2156
Reputation: 64700
Leaving this here for myself: if you add loggingLevel: 'info'
and loggingNoPII: false
to the options for the strategy, you can get useful debugging logs.
In my case, the error was:
jwt issuer is invalid
A search eventually led me to https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/2363: needed to adjust the app registration manifest to
"accessTokenAcceptedVersion": 2,
Once I did that the problem went away.
Upvotes: 1
Reputation: 47
If you come by this in question in google I had the same error, and it turned out I was using the Azure Secret ID, not the Secret Value
Add loggingNoPII: false
to your OIDC strategy, this will print out any errors.
Upvotes: 0
Reputation: 2166
I am grabbing this code straight from one of my projects in production. I was facing a similar problem before but it kind of resolved by itself when we were tweaking the done
callback for some reason.
Maybe this can serve as an example.
Passport.js
'use strict';
//Dependencies
const passport = require('passport');
const OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
//Custom Modules
const msService = require('./msService');
const DB_Connection = require('../dbConnection');
//DB Setting
require('../models/MS_User');
const User_DB = DB_Connection.model('user');
//Winston Logger
const logger = require('../log');
const passLog = logger.get('passportLog');
//Azure AD Creds
const loginCredentials = require('../creds/oicdCreds');
// Session
//Take in outlook id => keep the session data small
passport.serializeUser((outlookid, done) => {
done(null, outlookid);
});
//Deserialize when needed by querying the DB for full user details
passport.deserializeUser((outlookid, done) => {
User_DB.findOne({ outlookId: outlookid })
.then(user => {
done(null, user);
})
.catch(err => passLog.error(`Error Deserializing User: ${outlookid}:` + ' ' + err));
});
//Export the passport module
module.exports = (passport) => {
//OpenIdConnect
passport.use(new OIDCStrategy(loginCredentials,
//Verify callback for passReqToCallback: false
(iss, sub, profile, access_token, refresh_token, params, done) => {
//Get Calendar ID
msService.getCalId(access_token, calId => {
//Create or update the user
User_DB.findOneAndUpdate({ outlookId: profile.oid }, {
name: profile.displayName,
outlookId: profile.oid,
email: profile._json.email,
lastLogin: profile._json.ipaddr,
accessToken: access_token,
calId: calId
}, { upsert: true, returnNewDocument: false })
.catch(err => passLog.error(`Error Adding / Rnewing User: ${profile.oid}:` + ' ' + err));
//Return Profile ID for Serialization
done(null, profile.oid);
});
}));
};
The routes
//AD OpenIdConenct
//Login
router.get('/auth/outlook/login',
//Using MS Azure OpenId Connect strategy (passport)
passport.authenticate('azuread-openidconnect')
);
//Callback Handling
//Using MS Azure OpenId Connect strategy (passport)
router.post('/auth/outlook/callback', passport.authenticate('azuread-openidconnect', { failureRedirect: '/auth/outlook/login' }), (req, res) => {
//Redis
client.keys('*', (err, keys) => {
sessionLog.info(`Login Active Session: ${keys.length}`);
});
res.redirect('/profile_info');
}
);
Upvotes: 1