How to refresh Azure AD B2C (Angular) SPA session data? - azure-ad-b2c

Scenario :
I am logged-in through an AD B2C account in my Angular application.
I have integrated msal-browser (^2.15.0) and msal-angular (^2.0.1), for authentication and to access session data.
I use Graph API (from my .NET server) to update my AD B2C User record from my Angular application.
Requirement :
Whenever I update the User record through Graph APIs (indirectly through my server) from my Angular application, I want my browser session to reflect those changes as well.
// user-data.service.ts
public updateUserRecord(userUpdateObj: CustomUserModel) {
this.http.put("https://my-server-calling-graph-api.com/user-data", userUpdateObj ,this.HTTP_OPTIONS)
.pipe(
map((updatedResult: CustomUserModel) => {
console.log('Updated User Record -> ', updatedResult);
// SESSION DATA
this.usersList = this.msalService.instance.getAllAccounts();
this.msalService.instance.setActiveAccount(this.usersList[0]);
const userCustomAttributes = this.usersList[0].idTokenClaims;
console.log('Existing User Record -> ', userCustomAttributes);
}),
retry(2)
);
} // FN
Question :
Is there a way to silently get the session data updated in the background through any msal service calls or should I redirect to login to receive the session data freshly?

The only way to achieve this is to call acquireTokenRedirect() or ssoSilent() MSAL method. These use cookie based logins which will then reprocess the user journey and fetch the latest data. ssoSilent() occurs in a hidden iframe. You may find, due to replication delays, that this still doesn't work reliably.
To avoid this issue, the end user can edit their user profile using an Edit Profile flow. This would be the recommended path.

Related

How to get current user's tenant id rather than application's tenant id in MS Teams tab app (React JS)

I am building a MS Teams Tab app using React JS, https://learn.microsoft.com/en-us/microsoftteams/platform/tabs/what-are-tabs. For each app, we need to create an App Registration in Azure console. Now, in my application I am trying to retrieve the logged in user's tenant ID rather than app tenant ID.
First thing I know is that I can get the tenant ID from SSO token. So I tried to retrieve the SSO token as follow using #microsoft/teams-js package.
initialize();
const authOptions: authentication.AuthTokenRequest = {
successCallback:(token) => {
// when I decode the token, the tid field is still giving me app registration tenant's id (user can be guest and can belong to different tenant)
},
failureCallback:(error) => {
},
};
authentication.getAuthToken(authOptions);
(Note the comment in the code above).
I can get the user's tenant ID if I force the user to login using MSAL with this libraries, #azure/msal-react and #azure/msal-browser. The access token gives me the tid of the user. But the thing is that I do not want to force the user to login. How can I get the user's tennant ID without forcing the user to login?

Cannot Logout from IdentityServer 4 through Azure SAML logout request

Context
I have a Service Provider (SP) based on IdentityServer 4 and Sustainsys.Saml2.AspNetCore2 that is configured to use Azure as an IdP (SAML2).
I also have a SPA with an api that connects to my SP (with oidp) to identify my user. The api then creates a JWT for my user to use.
I can login my user correctly.
Question
My issue comes with the logout. I want to use the logout url parameter of Azure to notify my SP about the logout. I manage to see the SAML Logout Request as a string when I configure an endpoint of mine but I can't exploit it and parsing it manually does't seem right.
Is there an existing endpoint that would come with my dependencies that I missed?
The goal here is to revoke all my user's application sessions (the apps to which my user is connected throug my SP).
Configuration
Idp configuration in the SP (called in Startup.cs).
The Saml2AuthModel comes from a config file.
public static AuthenticationBuilder AddSaml2Auth(this AuthenticationBuilder builder, Saml2AuthModel saml2AuthModel)
{
builder.AddSaml2(saml2AuthModel.Scheme, saml2AuthModel.DisplayName ?? saml2AuthModel.Scheme, options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.SPOptions.EntityId = new EntityId(saml2AuthModel.ServiceProviderEntityId);
options.SPOptions.ModulePath = "/" + saml2AuthModel.ModulePath ?? saml2AuthModel.Scheme ?? options.SPOptions.ModulePath;
var idp = new IdentityProvider(
new EntityId(saml2AuthModel.IdentityProviderEntityId),
options.SPOptions
);
idp.MetadataLocation = saml2AuthModel.IdentityProviderMetadataLocation;
options.IdentityProviders.Add(idp);
});
return builder;
}
The Sustainsys.Saml2 library has support for single logout. To enable it, you need to set up a service signing key. The reason is that logout requests and responses should be signed. So the library doesn't expose the logout endpoints if it has no signing keys available.

How implement an access token, so I can bypass login page (sailsjs)

I have a SailsJS website for which I implemented authentication through a form where user needs to fill in email and password. copied from ActivityOverloard 2.0 example code
Login
login: function(req, res) {
console.log("Login hehe!!");
// Try to look up user using the provided email address
User.findOne({
email: req.param('email')
}, function foundUser(err, user) {
if (err) return res.negotiate(err);
if (!user) return res.notFound();
console.log("found email");
// Compare password attempt from the form params to the encrypted password
// from the database (`user.password`)
require('machinepack-passwords').checkPassword({
passwordAttempt: req.param('password'),
encryptedPassword: user.encryptedPassword
}).exec({
error: function(err) {
console.log("There was an error with password");
return res.negotiate(err);
},
// If the password from the form params doesn't checkout w/ the encrypted
// password from the database...
incorrect: function() {
console.log("Password doesn't checkout w/ the encrypted");
return res.notFound();
},
success: function() {
console.log("Good password");
var now = new Date();
User.update(user.id, { online: true, lastLoggedIn: now }, function() {
// Store user id in the user session
req.session.me = user.id;
User.publishUpdate(user.id, {
online: true,
id: user.id,
name: user.name,
lastLoggedIn: now,
action: ' has logged in.'
});
// All done- let the client know that everything worked.
return res.ok();
});
}
});
});
my page is protected with login
myPage: function(req, res) {
if (!req.session.me) {
return res.view('login'); // not authenticated will take you to the login page
}
// It's authenticated, it runs the code below
// DO SOMETHING
Now a very special use case, I need to open my page without user interaction (It can't be through a form) but I still need it to be protected. I'd need to pass some kind of access token.
I understand that passing an "access token" as query param is most probably not a good idea isn't it?
In fact, I don't know how to resolve my problem and allow to access myPage other than a session based authentication through a user interaction via a form ...
It seems to me that I'd need to first get a token programmatically and then open a browse to my page ... I bet there is some best practices to address my problem out there.
Any pointers? may be someone can fill the knowledge gap.
Realisticly speaking, you have multiple options with regards to passwordless or formless logins in node.js/express.js and therefore sails.js, as sails is built on top of both.
How you would approach the solution, really depends on the scale and use of your application/applications. For example; will the same login credentials be used to access multiple applications or a single web application, will the application be available only in an intranet or across the whole WWW.
Regardless of the scenario above, there will next to always be some initial setup required by the user, whether that is an initial sign up with an identity provider or an initial sign up with your application. The sign up form, will not dissappear entirely, rather it will become a one time event.
So let's look at some options and how we might introduce them into an express/sails application/s, I will start with the most basic and work down in difficulty.
Option 1:
Make use of the sails session store. From your code, you have already started doing this. The logic works something like this:
Your user signs up or logs in for the first time. At this stage you set the users session to be authenticated.
// Store user id in the user session
req.session.me = user.id;
req.session.authenticated;
You set a policy on all the pages which require authentication. Luckily, sails has already done some of the heavy lifting here by creating a sessionAuth policy in the folder api/policies. To make use of them open the config/policies.js file and add this policy to your protected pages
'my_app' : {
'route_to_protect' : 'sessionAuth'
},
Finally, you will want to make this session cookie last a really long time, to do this open config/session.js and edit the cookie maxAge to suit your needs. For example, if you want to force the user to login every 365 days, you might do some like this:
// milliseconds * seconds * minutes * hours * days
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365
},
The draw back to this option is that your sessions will be lost if the application is restarted and all users will have to log in again.
Option 2:
Use a simple third party library like passwordless. Passwordless offers token-based authentication for express web applications and as sails is built on top of express...
The general jist of passwordless is when a user signs up, you deliver them a link to your application via email, this will log them in and in turn set up there session. Passwordless makes use of mongo as a session store, so you can either install mongo or use something like mLab which is a Mongo Database-as-a-Service provider. For a complete run through on using passwordless, take a look at their getting start page here.
Now for the more featureful based options.
Option 3:
If you are developing an application that is public facing, making use of Passport.js with sails is a great option.
Passport is authentication middleware for Node.js. Extremely flexible and modular, Passport can be unobtrusively dropped in to any Express-based web application. A comprehensive set of strategies support authentication using a username and password, Facebook, Twitter, and more.
Passport works with Sails just like it does with Express.
There are already a shed load of guides on setting up passport out in the ether. But a great step-by-step is available here and is also the one referenced by sails in there official documentation here.
Passport is in all essence an authentication middleware. It allows users to identify themselves based on this authentication, you can develop the correct authorization functionality in your application.
Option 4:
Make use of SAML or OAuth. From a development and implementation perspective, these are by far the biggest undertaking out of the options provided.
SAML and OAuth are authorization middleware which refers to rules that determine who is allowed to do what. Both have a very similar setup and make use of an Identity Provider(IdP) and Service Provider(SP), where the IdP represents an online service that authenticates users in the flow and the SP represents an application that relies on a trusted IdP for authentication and authorization.
I am more familiar with SAML, so what follows is with reference to considerations when implementing SAML in a project.
You will first need to register your application (SP) with an IdP. With regards to IdP's, what you choose is based on the scale and requirements of your application, there are free online IdP's like ZXIDP and SSOCircle or if your application required a dedicated IdP you could look at something like OpenSSO. You could also consider creating your own Node.js IdP using the saml-idp package.
Integrating SAML into a sails application is not overly difficult. Make use of the saml2-js package.
Once all configured, the logic works something like this.
User opens their web-browser and goes to yoururl.
To authenticate the user yoururl constructs a SAML Authnrequest, signs, encrypts and encodes it.
Then yoururl redirects the user's to the IdP to authenticate.
The IdP validates the request, in the first signup/login, the IdP will ask the user to enter their username and password, after that it will use the sessioning and other than the address change in the browser address bar the user will not see much.
If the user is successfully authenticated, the IdP generates a SAML token that includes information about the user (username, etc) and redirects them with this token back to yoururl.
Finally yoururl verifies the SAML token, extracts the identity information about the user including authorisations and logs them in.

Group authorization using Azure AD ADAL.JS - NodeJS, ReactJS

I've seen that when using ADAL.js, you cannot get group membership claims due to some URL limitation.
https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/239
I am using oauth-bearer authentication from the frontend, that is, the frontend triggers a login via the AD login page.
The client then pass the access token to the backend.
What I want to do:
I want to filter some data in my backend endpoints depending on group membership.
e.g. if you are a member of group "London" in AD, you should only see things related to London in our DB queries.
Super simple using e.g. Okta or Auth0, not so much with Azure AD.
I also want to accomplish the same thing on the frontend, that is, show and hide menu items depending on group membership.
(All access is still checked on backend also)
The documentation is sparse and not very helpful.
"You should use Graph API".
How?, how do I talk to graph api using the token I get from the frontend?
This is the setup I have for my Node+Express endpoints:
app.use(
"/contacts",
passport.authenticate("oauth-bearer", { session: true }),
contacts
);
How, where and when should I call the graph API here?
Our system is super small so I don't mind using session state.
Can I fetch this information when the user logs in?
How should that flow be? client logs in, once logged in, call the backend and request the groups?
When you get the access token from Azure AD after the user logged in, you can find the group membership of the user by doing a GET request to https://graph.microsoft.com/v1.0/me/memberOf with the access token like this:
function getGroupsOfUser(accessToken, callback) {
request
.get('https://graph.microsoft.com/v1.0/me/memberOf')
.set('Authorization', 'Bearer ' + accessToken)
.end((err, res) => {
callback(err, res);
});
}
This sample assumes you are using the NPM package superagent.
And the required permissions to call this API are listed here.

Azure AD OpenIDConnect + ASP.NET Core - Authenticate and Extra Permissions/Token?

I am using the following bits against my Azure AD to authenticate with ASP.NET Core.
https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapp-openidconnect-aspnetcore/
https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect-aspnetcore
I have the basic login/auth working after creating an Azure AD app. User can login/logout.
My question is given this, what's the best way when a user Auth's to log to a DB? I thought about making the redirect URL to an endpoint, saving, then just redirecting back to "Home" but is that ideal?
Also, is it possible to retrieve a bearer token via this approach? Or does this require another type of call or extending "scope"? So that for example I could retrieve the authenticated users Manager.
https://graph.microsoft.com/v1.0/me/manager
My question is given this, what's the best way when a user Auth's to log to a DB? I thought about making the redirect URL to an endpoint, saving, then just redirecting back to "Home" but is that ideal?
This way only able to log those who already sign-in your app successfully. It is not able to log those users who are attempt to sign-in your app but enter the wrong password.
Azure AD already provide lots of report to gain visibility into the integrity and security of your organization’s directory.( refer here)
And if you are using the Azure AD Premium, you can review the sign-in activities via the Azure new portal below:
And if you want to store the sign-in activity in your web app, you can write the custom code after the token is verified. Here is the code for your reference:
// Configure the OWIN pipeline to use OpenID Connect auth.
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = Configuration["AzureAD:ClientId"],
Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAd:Tenant"]),
ResponseType = OpenIdConnectResponseType.IdToken,
PostLogoutRedirectUri = Configuration["AzureAd:PostLogoutRedirectUri"],
Events = new OpenIdConnectEvents
{
OnRemoteFailure = OnAuthenticationFailed,
OnTokenValidated = context => {
//write the custom code to store users login-in
return Task.FromResult(0); }
},
});
Also, is it possible to retrieve a bearer token via this approach?
Yes. We can get the token after receive the authorization code. You can refer the code sample here to acquire the token from asp.net core app.

Resources