Enabling multiple tenants using OWIN Active Directory bearer tokens - active-directory

I've recently started playing with Azure Active Directory to authenticate users against my website built on AngularJS.
Using blogs and sample code on GitHub, I've gotten it working with single-tenant using a combination of ADAL.js and Katana's Bearer Token AD integration.
However, I'm now running into some issues with supporting multiple tenants.
I've got a page set up that displays the user as ADAL sees them (found through the root scope's userInfo), as well as makes a call to my server that gets picked up by OWIN, and serializes context.Authentication.User.
Client-side, everything seems to be working properly. I can log in with any of my tenants, and it gives me the object I'd expect (with isAuthenticated: true, username populated, and all sorts of properties on profile describing the user, login, and tenant).
This is accomplished client-side by leaving off the tenant argument to my adalAuthenticationServiceProvider.init call, as described in the documentation.
Server-side, however, the UseWindowsAzureActiveDirectoryBearerAuthentication method doesn't like having no value for Tenant (in that it throws an exception). I've tried a few values for this, including the tenant with which my app was originally registered and, my logical favorite, "common," but no matter what I put in there (unless it's the tenant I'm trying to log in with, and if my ADAL is set up with that tenant), it seems to just skip over this.
For what it's worth, an actual API call is failing on the [Authorize] filter and returning a 401, which tells me this isn't an issue with my OWIN interceptor.
How can I tell UseWindowsAzureActiveDirectoryBearerAuthentication to support multi-tenant authentication?

I figured this out while writing the question. I think. But I spent all day on this finding almost no documentation on the matter, so I figured I'd post it anyway.
My solution (found through yet another blog post) was to include ValidateIssuer = false as a parameter. This makes sense, since we no longer want to validate that the tenant giving us a token is the one that we've listed.
Here's my code that solved the problem.
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"],
ValidateIssuer = false // This line made it work
},
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
Tenant = "common" // I don't know whether this has any impact,
// but it's a required parameter regardless.
});
I'd love if someone else wanted to correct me if this has any unforeseen circumstances--it's a tad daunting flipping a "validate" switch to off when you're working on authentication. But I think this all makes enough sense.

When you are developing a multi tenant application, you can no longer rely 100% on the default authentication logic. The default authentication logic assumes that you declare the azure AD tenant form where you want to receive tokens, and will enforce than only tokens form that tenant are accepted. This is done by examining the metadata document associated to every tenant, which contains (among other things) the identifier of the tenant itself - that identifier must be present in the token you receive, in the iss claim: any other value means that the token comes form another tenant, hence it must be rejected.
By definition, multitenant applications must accept tokens from multiple tenants. This is done by using a parametric endpoint (the common endpoint, see this post) which allows you to "late bind" which tenant will be used to issue a token. However the common endpoint will serve a generic metadata document, which cannot contain a specific iss value: instead, it contains a placeholder that at runtime will always be substituted with the issuer identifier of the tenant you actually got the token from.
This means that in multitenant apps you have to take over the tenant validation logic. If you are just debugging you can turn it off, as you appear to have done - that will prevent the default issuer validation logic from kicking in and refusing the incoming token because its iss value does not correspond to the placeholder found for common. In more realistic cases, however, you will write your own logic in the TokenValidationParameters.IssuerValidator delegate. For example, you might want to compare the iss value in the incoming token against a list of tenants that bought a monthly subscription to your service. HTH

Related

Salesforce Server-to-Server integration without any user involved

I am working on a integration with Salesforce using REST APIs and, as part of the project, I need to send updates to Salesforce and these updates are not user triggered, they are system triggered.
Because of that, what I expect to see on Salesforce Field History is not a user name but the name of our Connected App (the app that made the update).
What I see today is the user name because the way the integration was made initially using OAuth Authorization Code flow.
To change that part of the project, I followed the link (OAuth 2.0 JWT Bearer Flow for Server-to-Server Integration): https://help.salesforce.com/articleView?id=sf.remoteaccess_oauth_flows.htm&type=5
Making that, I was expeting to generate a token for a System, not for a User, but that's not what happened: when I used the token generate from the JWT Bearer Flow and ran the update, the Field History still shows the user name.
What could I do then?
Which are the options in Salesforce to achieve the behavior I'm expecting?
The most important, in my opinion, is to have a Token for our system, not for a user.
Thanks!
Everybody is an user in Salesforce. Even if you access unauthenticated pages (some contact us form? case or lead capture) - it gets tracked under special Guest User.
It sounds stupid but gives you unified interface to control permissions (Profiles/Permission sets). You want guests to access only FAQ articles and make cases? Sure thing, do it in profile, don't get paranoid about people trying to guess right URLs. You think an app was hacked? You can terminate the session just like any other "user". Want to allow login only in certain hours and from certain IP? Sure.
An app connecting with JWT will still need username (main difference being it's "just" certificate for signing the request instead of password).
Your best bet is to create dedicated "Mr System", "SystemX integration" account. It sounds like waste of license but in the long run saves you questions "why did you edit my account at 1 am" and you could even use it as backup account if you use SSO and it ever fails...

Best practise for populating access token with external claims

We have implemented IdSrv4 on top of AspNetCore Identity and we use ADFS as external IdP. From ADFS we wan´t to get the users AD-groups, upn and som other claims. The claims will be used both inside our IdSrv4 implementation, but will also be sent to our API-resources as part of the access token.
The current situation in our IdSrv4 implementation:
ADFS has been configured so that it emits the claims that we want and in our IdSrv4 implementation those claims are received as expected in the "ExternalLoginCallback" method of the AccountController.
IProfileService has been implemented in order to fill the "IssuedClaims" list with claims.
BUT, I haven't managed to build the connection between those to steps. What is the preferred way to preserve the claims received in "ExternalLoginCallback" and put them into the generated access_token in the IProfileService class?
Right now I have managed to get it working by saving the token using the method "UpdateExternalAuthenticationTokensAsync", which will save the token in the database. Then in the profile service I fetch the token and read the claims into the emitted token.
But this doesn't feel right and while searching for the proper way I´ve seen examples use the class IdentityServerUser that has "AdditionalClaims" property, but I can't find a way to plug that type into the event flow.
Also, when configuring the external IdP you have these "ClaimActions" that can be mapped, but I don't understand what they are.
Finally, I assume that the database tables "IdentityClaims" and "ClientClaims" with corresponding entities should be used for this purpose but I can´t figure out how. Or should they be saved in the "AspNetUserClaims" table to save the actual claim type/values and not only claim mappings?
So basically, there must be a best practice for this scenario that seem to avoid me and I would be greatful if someone could share it.
The main issue in my problem was that the problems I first encountered with persisting the Claims in the AspNetIdentity-Db led me to a wild goose chase.
Returning to this after a week or so made me give this another shot. Turns out that the DI injected "_userManager" wasn't "connected" to the current DI injected "_signInManager". If someone has an explanation for this, please share!
What did work was to use the "_signInManager.UserManager" to update Claims on the user. This properly stores the Claims in the "AspNetUserClaims" table, and can then be retrieved in the profile service.
UPDATE 1:
Of course there was a logical answer to that as well. A user manager is created by default even if you don't call "AddUserManager" on your identity setup during startup. BUT, in my case I have extended the IdentityUser class and now by doing it like this it all works as excpected (where "UserIdentity" is my derived class):
.AddIdentity<TUserIdentity, TUserIdentityRole>(options =>
{
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<TIdentityDbContext>()
.AddSignInManager<SignInManager<UserIdentity>>()
.AddUserManager<UserManager<UserIdentity>>()
.AddDefaultTokenProviders();

Identifying non-logged in users in a stateless RESTful API

I'm developing a stateless RESTful API which will be consumed by an iOS app and an AngularJS browser app. In this API, auth tokens are required for any actions relating specifically to an authenticated user (adding new content, editing details etc).
Now, my application also requires non-authenticated users to be able to add items to their shopping carts. This is where I'm unsure. Since the application is stateless and therefore has no sessions - I'm not sure how to identify the user if they haven't already logged in and been given an access token.
One solution I'm considering is generating some other lower class of token that will identify this non-logged-in user. Then I can send this with every request to fetch and modify the cart.
The issue described here is not really that you need a token, but that you need a way to uniquely identify specific shopping carts.
My assumption based on the question is that there is some generic /cart endpoint, that has a different content based on who's interacting with it. This design is a bit problematic (as you're seeing) as it might be better to just have a unique shopping cart uri per user.
/cart/[some-unique-id]
However, even in this scenario you might still want to use some form of authentication to identify that someone who's modifying the cart is still allowed to.
If you're using OAuth2, the easiest would be to just create a new grant type. OAuth2 is extensible, so you could definitely add some 'anonymous' grant type that doesn't require any info, but just provides a bearer token.
In this case your server would always have to make sure that the token is valid, but also make sure that it still correctly disallows accessing the endpoints that require 'real authentication'.

ADFS not issuing claims from custom attribute stores to some users

I'm trying to work out why some of our users aren't issued claims by our custom attribute stores.
Our main attribute store for authentication is Active Directory, but we are using two custom attribute stores to issue several custom claims to users, and also to perform some logging of claims issued. When an affected user logs in, they are authenticated successfully by AD, but have no more claims added. According to the logging in our attribute stores, the BeginExecuteQuery is never called.
I can't see anything to link the affected users, but they mainly seem to be new users, or users that have not logged into the system in a long time. Restarting ADFS sometimes clears the problem, but whether it does or not seems to be random.
I'm trying to understand why an attribute store would be ignored by ADFS on logon for certain users, when it works fine for others. If there is a quick guaranteed temporary fix to get users' claims issued correctly, that would be useful too!
For security reasons, I don't have access to the ADFS Debug tracing.
This was eventually solved with a longs string of calls to Microsoft's AD FS support team. The problem was traced to a piece of our claims rule language which was using the lastLogon and lastLogonTimestamp AD attributes without understanding how they actually behaved. This meant that for some users the condition to grant the custom claims was never met.

What's the simplest way to get user Groups from WAAD?

I've got AngularJS and Web.API WAAD authentication up and running. For client side I use great library ADAL.JS. For backend I use Microsoft.Owin.Security.OAuth. This part went quite smooth.
Now I want to implement authorization based on roles (which will be mapped to WAAD groups). Groups are not included in authentication token so I must ask Azure Graph API for them. I saw various ways to do it, using custom claims providers, adding web services to project, etc. Some examples already providing mapping between groups and roles to use in [Authorize] attribute.
But what is just the simplest example of how to get a list of group ids/names from WAAD providing User ID or username, when I'm already authenticated?
Also, is there any way to get this data in JS to use in Angular frontend, or should I create an API service which Angular should call for roles info?
In the non-JS case, the simplest way of getting groups in the token is by opting in. Download your application’s manifest, locate the “groupMembershipClaims” entry, change its value to “SecurityGroup” or “All”, upload back the manifest.
However note that this won't work for your scenario, because it uses the implicit grant - here the token is returned in an URI fragment, hence a big token would risk blowing past the URL length limits of the browser.
You can always request groups to the Graph and make it available to your frontend via custom action on your API, but from what you wrote you are already familiar with that. Let me discuss the matter here - if there's a simpler route to make this work in SPAs, I'll get back to this thread.
HTH
V.
Update: I verified and in the implicit grant case you will receive groups always via the overage claim. Please refer to https://github.com/AzureADSamples/WebApp-GroupClaims-DotNet/tree/master/WebApp-GroupClaims-DotNet - it will show you how to process the overage claim to retrieve groups. All you need to do is apply the same guidance to a web API instead, and if you need to make the info available to the client expose one or more actions doing so.

Resources