Is it possible to pass groups to B2C from federated AD to Azure AD B2C - azure-active-directory

I have an application running on B2C as part of customer deployment we federate with the customers AD so they can log in with their own corporate identity.
We then create matching groups in the B2C to control behaviors in the app.
We have a request that a customer wants to create the groups in their AD and then pass it across so that they can manage their users and access in one place.
I can see how I can add the claims I want when setting up a user flow but I cant see any option for groups. Is it possible to do or do i have to query the external AD (matching the way I query for groups in the B2C directory)
Thanks

While technically possible, I still have question for the benefit of doing this.
I will try to briefly describe how this would technically work. If not clear - I will have to summ-it on Github. This is only possible using custom policy. So you have to follow: https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-technical-profile
Edit the Menifest of the application registration in federated AAD. There you have to indicate that you require group membership claim:
"groupMembershipClaims": "All",
Extend the claims schema in your custom policy extensions by adding a new claim:
<ClaimsSchema>
<ClaimType Id="idpGroupMemberships">
<DisplayName>Group Memberships in the IdP</DisplayName>
<DataType>stringCollection</DataType>
<UserHelpText>This is read only for the user</UserHelpText>
</ClaimType>
</ClaimsSchema>
Later in the technical profile, copy the incoming groups claim into your outputclaims:
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="idpGroupMemberships" PartnerClaimType="groups" />
</OutputClaims>
Finally, you have to include that new claim in your relying party policy:
<RelyingParty>
<DefaultUserJourney ReferenceId="SuSiLocalFbStaykovNet" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="idpGroupMemberships" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
Using this approach you will get the Groups as GUIDs (these will be the objec IDs of the groups in federated AAD). If you want to get the Groups as names and not GUIDs, it is only partially supported and more complicated. Check out this doc here: https://learn.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-fed-group-claims#configure-the-azure-ad-application-registration-for-group-attributes

Not exactly an answer but some insight why this might be useful.
While technically possible, I still have question for the benefit of doing this.
I have a smaller organization and a public facing B2C offer and a management UI that handles the customer requests. Some sensitive data is on an internal SQL server and we have for historical reasons and some elder software an on-premise AD. I have now running AAD B2B with hybrid connection and SQL via hybrid connection. Customers running on B2C. Because it's technically just one app, it's also just one login --> ADB2C with a social IDP pointing to AADB2B, that internally is backed by the on-premise AAD.
That all works very well and is exactly what we need. I came here with the same question as the OP to forward the security group information to the token to handle some access restrictions in the management UI.
Hence, for me the scenario makes absolutely sense. Just my 2 cent.

Related

Restricting claims in Azure App Registration?

Within Azure Active Directory, I have created an App Registration.
For most users, this works great. They can login to my application via federating in from Azure AD, and their claims are coming through.
For some users, however, they have over 150 group memberships in Azure AD. For them, they receive an HTTP 400 bad request. I've increased the maxRequestBytes and maxFieldLength of my server according to this post: https://support.microsoft.com/en-us/help/820129/http-sys-registry-settings-for-windows, but it didn't solve the issue for all users. Some received access after this, but there's a handful that still cannot login.
My application only has ~10 groups in Azure AD that it cares about. I customized my App Registration's manifest to have "groupMembershipClaims": "SecurityGroup".
Is there a way I can restrict the claims coming out of Azure AD to only the 10 groups that I care about?
Per my understanding, this issue is due to too many groups claim in some your Azure AD user access token and you want to limit the group claims.
As far as I know, there is no way to do that. But there will be two workarounds here :
1. this official doc which indicated claims about groups and hasgroups, maybe provides some tips to handle your scenario. As the doc indicated, it suggested you to call an API to fetch all user groups if a user has too many group claims.
In your scenario, you will need to check about 10 groups membership with a user. To avoid the issue that too many group claims in your user's token, you can just remove group claims in token and check groups membership with a user with this API.
2. Using Azure AD app roles claim instead of groups claim. In Azure AD application, we can create Add app roles and assign the role to users or groups. Once be assigned the role , role claim will be added in users' token.
As you only care about 10 groups, you can create corresponding app roles and assign the roles to your groups that you cared about(i,e role1 assign to group1).With this,users in groups will have corresponding role claim(i,e , users in group1 will have a role claim:role1) so that we can use this claim to replace groups claim .
What's more
except for increasing maxRequestBytes and maxFieldLength of your server, you can try to modify maxAllowedContentLength , details see this post .

Manage user restrictions to different apps within a single B2C Tenant

Within a single Azure AD B2C Tenant I have a directory of Users. Also, I have 2 Applications registered. Call them App1 and App2.
Assume that a User has registered his/her Account coming from (and being redirected back to) App1, now that the user is in the common User directory, he/she could also sign in to App2 and I can't do anything about it.
It doesn't make much sense to me, and I need to restrict it. How can I control which Apps the Users can access? A User could have access to any amount of registered Applications. I can't seem to find that in the Azure Portal...
Of course I can apply any solution also on App side, e.g. by checking some claim or something, but still, I need to know how to manage such restrictions. (this could be a fully custom way)
You would have to implement in B2C exactly what you said you'd implement in the app
by checking some claim or something
Apart of your B2C user journey you would introduce a precondition to check whether a claim exists or equals a certain value. This would require custom policies. There isn't an easy way to "error" out, but you could create a self asserted page that just shows an error message with a dummy input claim.
You're much better off building this logic in your application.
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>app_permission</Value>
<Value>portalapp</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AppRestrictionExchange" TechnicalProfileReferenceId="TechnicalProfileHere" />
</ClaimsExchanges>
</OrchestrationStep>

How to support multiple login scenarios in multi-tenanted Azure Active Directory (AAD)

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

Dynamic OpenIdConnectOptions for multi-tenancy in Asp.net Core 2.1-*

I am working with aspnetcore v2.1 (latest dev branches) in order to create a multi-tenant app where each tenant authenticates against their own Azure B2C AD tenant. This aproach was chosen so that email/password selections and social login associations are unique per-tenant.
Instead of a static ClientId applied in Startup.ConfigureServices, I want to apply the correct ClientId and Authority based on the current tenant identity (which I determine based on the hostname). Based on previous inspection of the 2.0-* code, I had been using an IOptionsSnapshot to allow me to apply the correct options as shown below.
In Startup.ConfigureServices:
services.AddSingleton<IOptionsSnapshot<OpenIdConnectOptions>, OpenIdConnectOptionsSnapshot>();
services.AddAuthentication().AddCookie().AddOpenIdConnect();
In Startup.Configure:
app.UseAuthentication();
With an implementation of :
public class OpenIdConnectOptionsSnapshot : IOptionsSnapshot<OpenIdConnectOptions>
However, now I find that my OpenIdConnectOptionsSnapshot is no longer being instantiated or referenced.
What is the correct way to apply a dynamic per-tenant ClientId, Authority, etc under AspNetCore Security 2.1.0-*?
(I am open to "you're doing it completely wrong" and suggestions of different ways to achieve multi-tenancy for tenants that have no pre-existing AzureAD footprint)
Try using IOptionsMonitor instead, we changed how IOptionsSnapshot worked fairly late in 2.0 and switched auth over to use the monitor instead.
OptionsSnapshot is now scoped

Authentication Process Get Azure AD group the user is a member of and do logic

Is there a way to get the Group the User is member of so we can process the authentication, or even throw exception so the token will not be created.
The reason we need groups is that we can not create OU in Azure AD whereas we could before in LDAP. We retrieved the distinguished name and therefore had very rich information about said user.
Lastly, we do see that you could create an OU on-premises but read that Graph API would not recognize it or could not retrieve it.
We are attempting to do logic within the SecurityTokenValidated stage of Authentication process and we break the process whenever we try to use:
string UPN = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.Name).Value
Is this because we are using MSAL?
The best approach for you to take here is to make use of the group claims capability of Azure AD. (And for get OUs. OUs are not represented in Azure AD at all.)
Dushyant Gill's blog post on this is relatively old, but still very much relevant: http://www.dushyantgill.com/blog/2014/12/10/authorization-cloud-applications-using-ad-groups/. In short, the process is:
Enable group claims for your application by setting the groupMembershipClaims property in your application. After setting this, when a user signs in to your application, the list of groups they are a member of will be included in the token (if the number of groups is smaller than the limit).
Update your application's authorization code to make use of the group membership claims (if present).
Update your application to query the Azure AD Graph API if the groups membership claim is not present (i.e. if the "overage" claim is present). This happens only when the user is a member of more than 150-250 groups. (Use the _claim_name and _claim_sources claims as indications that the Graph API needs to be called directly.)
As described in the documentation for Azure AD Graph API permissions, in order for your application to call the getMemberGroups method, the app must have the "Read all groups" permission (Groups.Read.All). This permission requires admin consent, but once consent has been granted, the request can be made using the signed-in user's access token.

Resources