Pass extra information to IProfileService - identityserver4

When implementing IS4, we want to have a couple of extra fields on the /Account/Login form (we're building off of the Quickstart UI). The data provided by these fields (location info - 1) building and 2) station within the building) needs to be accessible when IProfileService is called as they are pieces of information used to determine the claims to be provided in GetProfileDataAsync(). We tried storing the data in HttpContext.Items, but that data is lost since there is a redirect that occurs before IProfileService is called.
Do you have any recommendations for how to pass this data back to IProfileService?

One of the extension methods on the HttpContext, SignInAsync, allows you to pass in any extra login related claims. If you add ‘building’ and ‘building_station’ as claims when you call SignInAsync from the AccountController, you should be able to access it through the HttpContext.
To do this you need to add the HttpContextAccessor to the IProfileService implementation through dependency injection, and once you get the HttpContext from it you should be be able to locate the appropriate claims in HttpContext.User.

Related

Custom attributes in Saml2AuthnRequest and Saml2AuthnResponse using itfoxtec-identity-saml2 library

We are using itfoxtec-identity-saml2 library to integrate saml in our .Net web API application.
We are passing a couple of custom attributes in relayState (ex. returnUrl, redirectionParams etc.) to make use of them once we get the relayState back from SP.
One of the service providers is not able to return relayState with multiple parameters because of the "&" separator.
Is it possible to include these custom attributes in Saml2AuthnRequest object and get the same attributes back in Saml2AuthnResponse or is relayState the only way to send and receive the custom dynamic parameters?
Is it possible to include these custom attributes in Saml2AuthnRequest object and get the same attributes back in Saml2AuthnResponse
A SAML2 authentication request can contain various custom extensions to carry extra data, flags, etc. However, anything that you would include in there would be custom, and the service provider implementation must be able to support and recognize it. It's completely non-standard and would not scale.
is relayState the only way to send and receive the custom dynamic parameters?
Yes, it is. You might want to encode the final value in such a way that could be included in the relay-state parameter as a single value.

Microsoft Identity Web - How to get the User Signed In event?

I'm using the Microsoft.Identity.Web NuGet package in order to sign users into Net Core 3.1 WebApp using Azure AD, then once the user has signed in, I then use their token with scopes to call the MS Graph API to fetch some additional data from their profile, such as their forename, surname, username etc. Basically some additional bits of info about the user that is not automatically included in the token returned from Azure AD.
This part work is working fine.
What I want to achieve is configuring some form of a system event or trigger to tell me when the user has successfully signed in, I would then use this trigger to run the Graph API query and fetch the user's additional profile attributes. The reason I want to do this is so each time the user requests a new page and runs a method or action, I can include their additional attributes into the logging.
Because the Microsoft.Identity.Web package hides away the Account Controller somewhere within the NuGet package (assuming a dll or something) I can't seem to access it to look at what I could latch onto in the way of an event trigger that I can use for the above.
Unless I call the MS Graph once the user has logged in then I would not have access to some of the user profile attributes that I want to include in the Serilog Logging structure.
Once I have the user attributes needed from MS Graph then I assume the best solution would be to store them in memory as getters setters for the lifetime of the logged in session, that way I can then access them from any page model / controller within the app through DI or a model.
I had thought about just simply calling the MS Graph from a OnGet() method when the home index page is loaded after a successful login, but the challenge is a user might not necessarily login by visiting the home page first, they might have saved a bookmark to another page they want to go to straight away which means the OnGet() method in the Home page might never be run. I need a more bullet proof solution given I should ensure that these extra user profile attributes are fetched every time without fail, regardless of which page is first visited that prompts the user login process.
Note: I've observed the fact that if I go straight to a page that has authorization enabled, once logged in then OIDC just returns me to that same page.
The final step in this riddle would be to remove the saved user profile attributes from memory once the user logs out, but this should be easy enough given the logout session always returns me to https://localhost:5001/MicrosoftIdentity/Account/SignedOut
If anyone has any ideas on what I could work with using this library to achieve the above would be great, thanks
I found something within Microsoft Identity Web, for the custom code:
AddSignIn has another override, which takes delegates instead of a
configuration section. The override with a configuration section
actually calls the override with delegates. In advanced scenarios you
might want to add configuration by code, or if you want to subscribe
to OpenIdConnect events. For instance if you want to provide a custom
processing when the token is validated.
https://github.com/AzureAD/microsoft-identity-web/wiki/web-apps#using-delegate-events
Here are Microsoft code samples for the ASP.net core, for many cases:
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/

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'.

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