I hope the following question makes sense:
I manage my users in ActiveDirectory.
I authenticate them via IdentityServer3.
I authorize the APIs via the AD groups that the user is in (acting as security roles).
How should I set up IdentityServer3:
Must I use my own custom UserService to access ActiveDirectory?
and does that replace the MembershipReboot / AspNetIdentity support (or am I misunderstanding what the UserService is)?
Or should I use one of the MembershipReboot / AspNetIdentity packages from IdentityServer3, and somehow customize them to map to ActiveDirectory (and if so, how)?
Seems there is no "mapping" and should not be a mapping from AD to a membership-reboot or an aspidentity or the newer identity-reboot user stores. The reason seems to be simple: mr and aspid or ir are all ways to store the user information in a persistent way (some sort of database or repository), which is already done in AD.
The userservice is enough. It causes the ASP Identity objects to be populated, and the middleware to work as expected, calling user authentication, and user or resource authorization correctly and automatically, after the client calls are "decorated" with "Authorize" attributes or after returning from the OP (the OpenID-Connect Provider) or from separate authorization or resource providers, in security calls.
Answer update: Now in IdentityServer4 the UserService has been deprecated and instead you use IResourceOwnerPasswordValidator.
See here for working code and a detailed explanation, in the answer after the accepted one (vote it up please)
IdentityServer4 register UserService and get users from database in asp.net core
Related
I have set up a B2C instance OK and managed to get a basic Blazor (server) app working with it a using the Microsoft Identity Platform (using AD groups for permissions - it was a hassle but works).
However, I'm trying to use an external Azure AD as a custom identity provider in the user flow, so that I am not just restricted to just email/id/social accounts, but can have guest accounts from other directories use the app without having to manage their sign-in's. To do that I performed a web app registration in the AD tenant that I wanted to use to authenticate those accounts against (as suggested in a couple of tutorials).
The application I registered in the external AD has a Redirect URI in the format "https://{My B2C Directory Name}.b2clogin.com/{My B2C Directory Name}.onmicrosoft.com/oauth2/authresp", which matches the name of my B2C instance, and I have added the client id and secret generated from that app registration and put the details into the custom identity provider I have created for the sign-in flow, as per the instructions here (including the mappings etc.):
https://learn.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-azure-ad-single-tenant?pivots=b2c-user-flow
I also found a slightly older tutorial here, which is pretty similar (different mappings) that I've tried to follow (and adapt the bits that are out-of-date).
https://medium.com/the-new-control-plane/connecting-azure-ad-b2c-to-azure-ad-via-the-b2c-custom-identity-provider-42fbc2832e32
However when I run the user flow I get "AADSTS900971: No reply address provided." - this happens even when I run the flow directly from the User Flows tab in B2C with a 'Reply URL' explicitly set to "http://jwt.ms" (just to capture the token contents).
I'm confused about the reply URL being missing because they exist in both registered apps. Also, it's not saying they're mismatched, just that one isn't set at all (but appears to be).
It feels like I'm missing something simple - does anyone have any idea what that might be?
Ok so I did a couple of things to resolve this:
Re-registered the application in the AD I want to authenticate with (following this tutorial again: https://learn.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-azure-ad-single-tenant?pivots=b2c-user-flow)
I was careful to ensure that the redirect URI in the format:
https://{B2C Instance Name}.b2clogin.com/{B2C Instance Name}.onmicrosoft.com/oauth2/authresp
was all lower case.
I also had to change from just a 'sign-in' user flow to the 'sign-up, sign-in' one, and then applied the custom identity provider to that flow. Apparently you need that even for users from another AD to be able to complete their invite process (otherwise you just end up with a user doesn't exist error - even if you've invited/added them to the B2C users list).
I also elected to 'Grant admin consent for Default directory' under the API Permissions tab for the application being registered in the external AD (to be used for the custom identity provider).
The flow seems to work now. The only thing that would be useful would be to have an invite only sign-up, sign-in flow so that you could invite specific people without breaking the invite process.
If anyone knows how to do that please do post something.
we are looking for brand new implementation for Identityserver4,
I wnet thru the documentation and install the Project on VS2017 and DB in sqlserver.
Database is created with the default migration script provided for bot ConfigurationData as well as Operational DAta.
I am very much confused , where the user will be how the clients will be add etc?
Also in the startup the default ASPNEtIdentity is add, but in database there is no ApplicationUser table, so where the userdata will be?
My requirement is simple - User will be aple to login to application by his credentials(or may be by 3rd party application) and will use the application or
API will directly use Identity server to with clientcredential scope,
Here please do let me know:
Should I introduce AspNetIdentity or Not, and Why?
Where is the user table and Password of the user in the database generated by the migration.
How we can add User clients and resources to the Created Database
Do I need to add Login/Logout page ?
In API APIResource is used to defined the Resources "api1" and same is used by the client code to get the access but this "api1" is not used anywhere with the definition/signature of the Method, so ow will it be correlated?
First off, IdentityServer4 on it's own does not handle users or authentication thereof - you either need to use ASP.Net Identity and the integration library that hooks it up to IdentityServer4 or build all of that stuff yourself.
So to sum up:
Yes you'll need to use that or roll your own user store and authentication logic
Either provided by ASP.Net Identity or built yourself
https://www.identityserver.com/documentation/admin-ui/ may fit your needs or you could build your own tooling and/or scripts to do so
Yes although the quickstart samples provide good basic starting points
The bearer token middleware by default will validate the audience (ApiResource.Name) but I prefer to enforce scope at a more granular controller or action level using a filter (e.g. [ScopeAuthorize("my.api")] public MyResult ApiAction() { ... }). This filter will check that the specified scope is defined in the set of claims in the ClaimsPrincipal).
I want to have an architecture made up of these devices / programs with different roles (which are all separated, none of these roles must exist in one and the same instance):
CLIENT
AUTHENTICATION-SERVER
AUTHORIZATION-SERVER (there can be a multitude of these)
RESOURCE-SERVER (also many possible)
And:
I don't want to use any websites a user has to navigate to.
From my studies of OAuth and Open ID Connect I would assume that a Client could get an ID_TOKEN (after authorization) from an AUTHENTICATION-SERVER and could then request ACCESS_TOKENS to different resources from the AUTHORIZATION-SERVERs.
I guess the method I described allows identity theft on the client side.
I cant find a way to easily get id tokens from an authorization server to a client (which I could then use to authenticate to multiple authorization servers). My clients are trusted. I want to use native apps and implement open id connect among them anyway.
I do not want to use any websites as of now and still need to be able to have a secure communication with associations of claims to users and everything. Are OpenID Connect and its possible 'Flows' appropiate for this? Are there any other implementations that would allow the flow I described (or mabye derivations of OpenID Connect)?
It's not recommended but if you really don't want to use a browser (which is somewhat anti-OAuth/OIDC and is full of limitations) then ResourceOwnerPassword grant type is the one you'll need to use. This will not return an id_token though, just an access_token.
id_tokens exist to support front-channel browser-based sign in flows, they serve no purpose if using the ROP grant type.
Access tokens are issued by the authentication/secure token service (IDS4 in this case) and grant the client (optionally with user consent) access to the resource. The resource then has its own authorization rules to enforce scope and user-related ACL/business rule access controls.
Our application (referred to as "XYZ_App" below) is a multi-tenant SaaS application. We are in the process of making it available for Microsoft AppSource as a multi-tenanted "Web app / API" (referred to as "AppSourceXYZ_App" below).
We started our OpenID Connect implementation with endpoints pointing to “common” as per stated in the documentation when multi-tenancy is desired/required.
In XYZ_App, we added information in the system to know what AAD instance each XYZ_App tenant is associated with (using the GUID Microsoft assigned to this AAD instance, we are NOT using the "rename-safe.onmicrosoft.com" representation).
When using the “common” endpoints, we had to manually validate the issuer from the JWT to make sure it was the expected one: a user could access XYZ_App requesting access to XYZ_App’s tenant associated with contoso.onmicrosoft.com, get directed to “login.microsoftonline.com/common” to authenticate and then decide to authenticate with a user from another AAD instance (referred to as "anotherAADInstance.onmicrosoft.com" below). In this scenario, even though a user could successfully authenticate on anotherAADInstance.onmicrosoft.com, XYZ_App’s redirect URI must make sure the JWT issuer is the one from contoso.onmicrosoft.com. I’ll refer to this setup as Scenario_1.
With that scenario in mind, we thought about NOT using “common” and customize the requests going to login.microsoftonline.com on the fly; attempting to “jail” requests to be forced to authenticate against a specific AAD instance. We would still need to perform our validation in the redirect URI to make sure the issuer is the appropriate one, but we thought this approach might make our lives easier. I’ll refer to this setup as Scenario_2.
Do you envision Scenario_2 is viable in the long run or is it too short-sighted ? Based on my current knowledge of OpenID Connect, one limitation I can see with Scenario_2 is that it would become problematic to support “broker accounts” into our app.
Explanation of “broker accounts”: in our industry, some external users are allowed access to the system. Let’s say I have a company called “BrokerCo” (with their own brokerco.onmicrosoft.com AAD instance) who has 2 employees: Broker1 and Broker2. BOTH anotherAADInstance and contoso hired Broker1 and Broker2 to get broker services to perform tasks in XYZ_App; requiring XYZApp to grant them access. What is the ideal way for authentication from an OpenID Connect standpoint ? If XYZ_App were to use “login.microsoftonline.com/common” for authentication (like in Scenario_1; as opposed to “jailed” access like in Scenario_2), Broker1 and Broker2 could authenticate with brokerco.onmicrosoft.com (no AAD "External users" for anotherAADInstance nor contoso), but they would then get to redirect URI with an issuer that is different than what XYZ_App’s anotherAADInstance and contoso tenants are configured for... I feel like I’m back to square 1...
Do you have suggestions or pointers to solve this issue ?
Background context:
While playing with OpenID Connect issuers, I got the following error message:
AADSTS50020: User account 'testuser#anotherAADInstance.onmicrosoft.com' from identity provider 'https://sts.windows.net/XXXXXXXX-fake-GUID-9bZZ-XXXXxxxxXXXX/' does not exist in tenant 'XYZ Publisher' and cannot access the application 'YYYYYYYY-fake0-GUID-YYYYyyyyYYYY' in that tenant. The account needs to be added as an external user in the tenant first. Sign out and sign in again with a different Azure Active Directory user account.
Thanks in advance !
Your question has multiple layers, trying to address most of them:
AppSource is about trial experiences for new users: this mean that any corporate account around the globe can potentially be an user of your SaaS application - or at least to the trial experience of your application, therefore the first thing you need to think when integrating with AppSource is how easy it has to be for a potential user to experience your app for the first time.
With that in mind, AppSource recommends that the trial of application is build on such a way that allows any user from any organization to sign-in, and therefore a multi-tenant approach for your application is the recommended setup for any application.
The single-tenant approach requires more control on your side, and for a trial experience - it means that the user cannot try your application right away because the operation you have to do on your single-tenant application is to add the user to an Azure Active Directory tenant as a guest user. This guest account will need then to wait receiving an email to accept the invitation to join to this tenant you are adding the user to then sign-in to your application.
Therefore your scenario 1 is the best scenario thinking on a trial experience in general, and also in general require less management (as you'd not need to create/ manage each individual account that needs to access your application as guest users of your Azure AD instance).
However some concerns you listed - that this scenario bringing are valid: Because you are accepting the common endpoint, you are saying basically that any user can sign-in to any tenant to your application, and this may not be desirable. In addition, the scenario you listed that a user can generate a token for any application is also valid, however, you can add additional checks to make this more secure and that any token generated by another authentication is blocked:
You can validate the 'audience' claim to guarantee that the token was issued to your application
You can eventually check the 'tid'/'iss' claims against of a list of tenant Ids in your database to see if that the user's organization is a valid organization in your application -- this would be valid for non-trial users/ organizations.
More information in this article.
About scenario '2' and broker accounts:
This scenario could be interpreted in two different ways:
Broker accounts are guest accounts of a customers' Azure AD tenant
Broker accounts are third party accounts but are not actually added as a user of anotherAADInstance or contoso AD
If your case is '1' then you're right: if your application needs to authenticate guest users that belong to another Azure AD tenant, then common endpoint could not be used directly.
If your case is '2' then what you'd need to do is to continue using the common endpoint and somewhat after the user is authenticated ask them to choose the company. I am describing this on generic terms without fully understanding this scenario. Perhaps this is not simple as you want the remote company to control this and not the user - so some additional complexities may need to be handled here.
A note is that if your scenario is scenario 1. - in theory - you can still use an hybrid approach, where you'd ask user to type the username inside the application and the company that they want to sign-in, then check if you need to validate the user against common or tenant-id endpoint, preparing the request and then sending a login_hint argument when authenticating. More information here
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.