Microsoft ad b2c issue: policy is missing - node.js

I made a chat bot which I want to authorize user using Azure ad b2c using OIDCStrategy. In the console, it always logs authentication failed due to: In collectInfoFromReq: policy is missing. The policy has been set in Azure. And I can't find somewhere to declare the policy in the code. Here is my server:
server.get('/login',
passport.authenticate('azuread-openidconnect',
{
failureRedirect:'/fail'
}),
function(req,res,next){
console.log('Login was called');
res.redirect('/',next);
}
)
server.post('/api/auth', passport.authenticate('azuread-openidconnect'));
And connection to the azure ad b2c:
passport.use(new OIDCStrategy({
redirectUrl:'http://localhost:3978/api/auth',
allowHttpForRedirectUrl:true,
clientID:'5fe844d7-e4d1-4c4c-ba70-078297b00abc',
clientSecret:'?aTvTEbwcNfUF2,^',
identityMetadata: 'https://login.microsoftonline.com/nuffieldbot.onmicrosoft.com/v2.0/.well-known/openid-configuration',
skipUserProfile: true,
responseType: 'code',
responseMode: 'form_post',
isB2C:true,
scope:['email','profile','offline_access','https://outlook.office.com/mail/read'],
loggingLevel:'info',
tenantName:'nuffieldbot.onmicrosoft.com',
passReqToCallback:true
},function(req, iss, sub, profile, accessToken, refreshToken, done){
log.info('Example:Email address we received was:', profile.email);
process.nextTick(function(){
findByEmail(profile.email,function(err,user){
if (err) {
return done(err);
}
if (!user){
users.push(profile);
return done(null, profile);
}
return done(null, user);
})
})
}
));
Where can I declare this policy in my code?

For B2C, we must have policy , if you have not set policy name in query string of request , it will throw error : In collectInfoFromReq: policy is missing . Please refer to source code of oidcstrategy.js :
// for B2C, we must have policy
if (self._options.isB2C && !params.policy)
return next(new Error('In collectInfoFromReq: policy is missing'));
You must set policy in request :
Sign In
You could also refer to code sample : Azure Active Directory OIDC Web Sample

Related

Error "Failed to serialize user into session" when using passport.js for Azure AD

I am trying to follow the Office 365 Graph API tutorial here.
However I keep running into the below error after I sign-in.
Error: Failed to serialize user into session
at pass (C:\Users\SODS\devzone\src\nodejs\o365-graph\graph-tutorial\node_modules\passport\lib\authenticator.js:271:19)
at serialized (C:\Users\SODS\devzone\src\nodejs\o365-graph\graph-tutorial\node_modules\passport\lib\authenticator.js:276:7)
at passport.serializeUser (C:\Users\SODS\devzone\src\nodejs\o365-graph\graph-tutorial\app.js:20:3)
at pass (C:\Users\SODS\devzone\src\nodejs\o365-graph\graph-tutorial\node_modules\passport\lib\authenticator.js:284:9)
at Authenticator.serializeUser (C:\Users\SODS\devzone\src\nodejs\o365-graph\graph-tutorial\node_modules\passport\lib\authenticator.js:289:5)
at IncomingMessage.req.login.req.logIn (C:\Users\SODS\devzone\src\nodejs\o365-graph\graph-tutorial\node_modules\passport\lib\http\request.js:50:29)
at Strategy.strategy.success (C:\Users\SODS\devzone\src\nodejs\o365-graph\graph-tutorial\node_modules\passport\lib\middleware\authenticate.js:235:13)
at verified (C:\Users\SODS\devzone\src\nodejs\o365-graph\graph-tutorial\node_modules\passport-azure-ad\lib\oidcstrategy.js:98:21)
at Strategy.signInComplete [as _verify] (C:\Users\SODS\devzone\src\nodejs\o365-graph\graph-tutorial\app.js:63:10)
at process._tickCallback (internal/process/next_tick.js:68:7)
The serializeUser method looks like below:
//Configure passport
//In-memory storage of users
let users = {};
//Call serializeUser and deserializeUser to manage users
passport.serializeUser((user, done) => {
//Use the OID prop of the user as the key
users[user.profile.oid] = user;
done(null, user.profile.oid);
});
passport.deserializeUser((id, done) => {
done(null, users[id]);
});
The problem might be that user.profile.oid seems to be undefined inside the callback function of serializeUser (tried also replacing arrow functions with normal ones but didn't work either).
But the oid is being set during the sign-in and I can see it when I trace the values in the code below.
//Callback when the sign-in is complete and an access token has been obtained
async function signInComplete(iss, sub, profile, accessToken, refreshToken, params, done) {
console.log('iss profile oid -->' + profile.oid);
if (!profile.oid) {
return done(new Error("No OID found in user profile."), null);
}
try {
const user = await graph.getUserDetails(accessToken);
if (user) {
//Add properties to profile
profile['email'] = user.mail ? user.mail : user.userPrincipalName;
}
} catch(err) {
done(err, null);
}
//Create simple oauth token from raw tokens
let oauthToken = oauth2.accessToken.create(params);
//Save the profile and token in users storage
users[profile.oid] = {profile: oauthToken};
return done(null, users[profile.oid]);
}
Which is the call for the OIDCStratergy.
//Configure OIDC stratergy
passport.use(new OIDCStaregy(
{
identityMetadata: `${process.env.OAUTH_AUTHORITY}${process.env.OAUTH_ID_METADATA}`,
clientID: process.env.OAUTH_APP_ID,
responseType: 'code id_token',
responseMode: 'form_post',
redirectUrl: process.env.OAUTH_REDIRECT_URI,
allowHttpForRedirectUrl: true,
clientSecret: process.env.OAUTH_APP_PASSWORD,
validateIssuer: false,
passReqToCallback: false,
scope: process.env.OAUTH_SCOPES.split(' ')
},
signInComplete
));
So the question is is the undefined users.profile.oid the issue here? If so where & why does it get lost?

Provider/Home realm discovery in B2C

We have an Azure AD B2C instance which is configured with 2 providers:
AAD
custom OIDC
I am using this sample SPA: https://github.com/Azure-Samples/active-directory-b2c-javascript-msal-singlepageapp
That sample has following login procedure:
function login() {
clientApplication.loginPopup(applicationConfig.b2cScopes).then(function (idToken) {
clientApplication.acquireTokenSilent(applicationConfig.b2cScopes).then(function (accessToken) {
updateUI();
}, function (error) {
clientApplication.acquireTokenPopup(applicationConfig.b2cScopes).then(function (accessToken) {
updateUI();
}, function (error) {
logMessage("Error acquiring the popup:\n" + error);
});
})
}, function (error) {
logMessage("Error during login:\n" + error);
});
}
This is what can happen in the application:
User presses the login button. The login pop-up will be shown.
User logs in with his AAD account.
Application tries to acquire token silently for the current AAD
user. This fails and a pop-up window will be shown.
User logs in with custom OIDC account.
In this situation the AAD user is logged in (id_token) but the access_token represents the custom OIDC user.
I have tried
clientApplication.acquireTokenPopup(
applicationConfig.b2cScopes,
applicationConfig.authority,
clientApplication.getUser(),
{ 'prompt': 'none' }
).then(...);
but did not worked. It presents the list of the providers to choose from.
How can I achieve that acquireTokenPopup will redirect me to AAD without home realm discovery/provider discovery?
Try passing in the parameter "domain_hint" and set the value to the value that you provided in the AAD technical profile for

WSO2 SAML2BearerGrantHandler SAML Token Issuer verification failed or Issuer not registered

I configured the WSO2 Identity Server to be a Key manager to WSO2 API Manager.
This is What I have been done
I Created Application in API Manager and Published it. [OK]
I Created API and subscribed in the Store. [OK]
I am using NodeJS Application as Client Application and Go as a Resource Provider
This is the Passport Configuration using passport-saml strategy:
passport.use(new SamlStrategy({
path: configSaml.path,
entryPoint: configSaml.entryPoint,
issuer: configSaml.issuer,
decryptionPvk: configSaml.decryptionPvk,
validateInResponseTo: true
},
function(profile, done) {
done(null, profile)
}
));
It works greate
This where I got the Problem when I try to get the Access token from Saml 2.0 bearer, this is the code:
app.post('/login/callback',function(req, res, next) {
passport.authenticate('saml', function(err, user, info) {
Log.d("OAuth Callback", arguments);
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
//req.logIn(user, function(err) {
// if (err) { return next(err); }
// return res.send(user);
//});
request.post({
url: "https://localhost:9444/oauth2/token",
headers: {
'Content-Type' : 'application/x-www-form-urlencoded',
'charset': "UTF-8",
"Authorization": "Basic " + new Buffer("Ihd6BrTufShu7GPR4QXlypoIlsca:gTyBdDVd0zGQZRr29NhPoooHpeka").toString("base64")
},
body:"grant_type=urn:ietf:params:oauth:grant-type:saml2-bearer&assertion=" +
require("base64-url").encode(user.getAssertionXml()) +
"&scope=PRODUCTION"
}, function(err,httpResponse,body){
Log.d("Http Error", err);
res.json(body);
})
})(req, res, next);
});
the body response:
"{\"error\":\"invalid_grant\",\"error_description\":\"Provided Authorization Grant is invalid.\"}"
the Console debug it prints:
[2016-01-12 10:57:27,645] DEBUG - SAML2BearerGrantHandler SAML Token Issuer verification failed or Issuer not registered
[2016-01-12 10:57:27,645] DEBUG - AccessTokenIssuer Invalid Grant provided by the client, id=Ihd6BrTufShu7GPR4QXlypoIlsca, user-name=null to application=admin-AT-safarifoneict.com_SamarimartWebPortal_PRODUCTION
[2016-01-12 10:57:27,646] DEBUG - AccessTokenIssuer OAuth-Error-Code=invalid_grant client-id=Ihd6BrTufShu7GPR4QXlypoIlsca grant-type=urn:ietf:params:o auth:grant-type:saml2-bearer scope=PRODUCTION
Any Help will be appreciated
"Identity Provider Entity Id" value in your IDP configuration must be equal to the issuer value of the SAML2 Assertion. Please refer this for more details.
When you configure WSO2IS as Key Manager, your /token end point would be in WSO2IS. So; your SAML2 SSO provider & OAuth2 provider is same (WSO2IS). In that case, you do not need to configure a new IDP configuration.
However, you can verify this by extracting the SAML2 Assertion, Just find the issuer value in SAML2 Assertion and verify it is equal to the value which you have configured as "Identity Provider Entity Id"

Passport - Azure AD - Application is not supported for this API version

When I use NPM package "passport-azure-ad" to try and connect to Azure AD, I am getting the error below. I have successfully connected to Facebook, Google, and MSFT Live, successfully, but cannot figure out why Azure AD doesn't like my configuration.
What does this error mean???
Error:
Application xxx is not supported for this API version.
I've looked at quite a few articles and the GitHub repo, but each is slightly different, and doesn't make it clear which options are required.
https://github.com/AzureADQuickStarts/B2C-WebApi-Nodejs/blob/master/node-server/app.js
https://github.com/Azure-Samples/active-directory-node-webapp-openidconnect/blob/master/app.js
Here's my configuration:
var OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
var WINDOWS_AD_CLIENT_ID = "xxxx"
var WINDOWS_AD_CLIENT_SECRET = "xxxx"
passport.use(new OIDCStrategy({
callbackURL: "/dealer/auth/azuread/callback"
, realm: 'xxxxx' //tenant Id
, clientID: WINDOWS_AD_CLIENT_ID
, clientSecret: WINDOWS_AD_CLIENT_SECRET
, identityMetadata: 'https://login.microsoftonline.com/common/.well-known/openid-configuration'
//, tenantName: 'xxxx.onmicrosoft.com'
//, policyName: 'B2C_1_DealerSignin'
//, validateIssuer: true
//, audience: 'http://localhost:3000/dealer'
//oidcIssuer: config.creds.issuer,
, skipUserProfile: true // for AzureAD should be set to true.
, responseType: 'id_token' // for login only flows use id_token. For accessing resources use `id_token code`
, responseMode: 'form_post' // For login only flows we should have token passed back to us in a POST
//scope: ['email', 'profile'] // additional scopes you may wish to pass
},
function(iss, sub, profile, accessToken, refreshToken, done) {
console.log("Windows AD Profile retrieved")
return done(null, profile);
}
));
And Routes:
router.get('/auth/azuread',
passport.authenticate('azuread-openidconnect', { scope: 'email profile' }),
function(){
console.log("Azure AD endpoint invoked.")
});
router.post('/auth/azuread/callback',
function(req, res, next) {
console.log("Azure AD Auth callback is invoked")
next()
},
passport.authenticate('azuread-openidconnect'),
function(req, res) {
console.log("Azure AD Auth callback is finished")
res.redirect('/dealer');
}
);
I ran into the same issue whenever I created my application under:
Portal.Azure.com -> Azure AD -> App Registrations
or the equivalent on the Classic Portal.
I had to create my app under:
apps.dev.microsoft.com
For it to work. Hopefully this helps someone else.

Node.js passport-saml redirects to localhost:3000/login/callback all the time

I am using the tutorial from www.npmjs.org/package/passport-saml for the SAML. I am a beginner in SAML.
The tutorial says
The SAML identity provider will redirect you to the URL provided by the path configuration
I already have a OpenIdp account. It seems I can successfully login but the redirect URL always sends me to localhost:3000/login/callback which is not present in my code because I changed the 'path' to '/users/login-user-db-saml' or 'www.passporttoken.com:1234/users/login-user-db-saml' (both doesn't work and still sends me to the default login/callback).
I have the code below. What I am doing wrong?
/**start FOR SAML**/
passport.use(new SamlStrategy(
{
path: '/users/login-user-db-saml',
entryPoint: 'https://openidp.feide.no/simplesaml/saml2/idp/SSOService.php',
issuer: 'passport-saml'
},
function(profile, done) {
findByEmail(profile.email, function(err, user) {
if (err) {
return done(err);
}
return done(null, user);
});
})
);
app.post('/users/login-user-db-sam',
passport.authenticate('saml', { failureRedirect: '/users/login-user-saml', failureFlash: true }),
function(req, res) {
res.redirect('/');
}
);
app.get('/users/login-user-saml',
passport.authenticate('saml', { failureRedirect: '/users/login-user-saml', failureFlash: true }),
function(req, res) {
res.redirect('/');
}
);
/**End for SAML**/
I removed the 'path' from the SAML configuration, and instead use a 'callbackUrl' with the full path to the callback specified. I also set 'issuer' as shown below:
saml : {
entryPoint : 'https://openidp.feide.no/simplesaml/saml2/idp/SSOService.php',
issuer : 'http://192.168.56.101:3000',
callbackUrl : 'http://192.168.56.101:3000/login/callback'
}
You should also configure your SAML SP at OpenIdP on the metadata configuration page: https://openidp.feide.no/simplesaml/module.php/metaedit/edit.php - set the AssertionConsumerServiceURL on the SAML 2.0 tab to be your callbackUrl, and set the entityID to be the 'issuer' above.
Have you considered making your SAML Login route a POST request?
SAML wants it to be POST
The problem is in your strategy configuration; especially issuer. Your configuration point to the entity 'passport-saml', which is configured as is. Define your own entity and create settings you need.

Resources