Inviting a consumer to Azure AD B2C with custom in-app attributes - azure-active-directory

Inviting a consumer user to Azure AD B2C has been covered by other Stack Overflow questions & answers, and AFAIK requires the use of custom policies that entail a signed JWT being created and used during invite redemption.
What I'm trying to figure out: In our application, we have differently permissioned user groups and different organisations managed by internal RBAC logic. We identify & authorize users based on their oid claim in the access token that's returned to msal-react/msal-browser and used to authenticate against our backend's API.
In Microsoft Graph, a user can be invited and the API will respond with the created user's oid. However, this is the wrong type of user and not appropriate for B2C scenarios.
With the custom policy route not creating the user object in AAD B2C at the time of invite, and therefore without knowing the user's oid at the time of inviting them to the application, what might be the best way to configure their in-app profile and have them identifiable to the application itself upon first login?
My thought at the moment is to have the application store the emails of users that are invited who have not yet redeemed/signed-in. We can configure the emails claim to be returned upon login, which is checked against the invited emails store when an oid claim is returned that isn't present in the database. This can then trigger a function to update the user's internal id with the oid in their first login's claim.
If this is inadvisable or if there's a better way, I'd be very grateful to hear it.

It would work, or just pre create the user up front via MS Graph API. Then you have an email and objectId available.
You could also put an extension attribute on the account indicating whether the user has redeemed their invite. That would allow you to monitor who has redeemed, and also be a way to provide a different experience depending on if the user has redeemed or not redeemed the link.

Related

Azure B2C External provider with custom claims and policies

I have an Azure B2C tenant with some identity providers (Facebook, LinkedIn, etc) and a custom claim where the user is able to choose a gender.
During the sing up with one of those, I'm able to get the gender's claim in the token, but this doesn't happen when the user signs in. I was wondering where is the right place to map/include this custom claim since I've been using the base custom policies: SocialAndLocalAccount
Any clue about where to get it? I have my custom claim inside the SelfAsserted-Social's Technical Profile but no luck during the sign in.
The SelfAsserted-Social technical profile will only be executed on the first time the user connects with his social account.
As you can see from the comments in the user journey
Show self-asserted page only if the directory does not have the user account already (i.e. we do not have an objectId).
You need to persist that claim if you want to reuse it as per comment here
If a claim is to be persisted in the directory after having been collected from the user, it needs to be added as a PersistedClaim in the ValidationTechnicalProfile referenced below
You then need to read it from AD in your AAD-UserRead... technical profiles
And output it in your Relaying Party's output claims.
Follow this guide https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-configure-user-input#add-a-claim-to-the-user-interface for the Social profiles where city in the examples is your gender claim.

IdentityServer4 Add Tenant Claim after Authentication

I have a multi-tenant scenario in which one email can be associated with multiple tenants.
I've thus configured a custom AccountChooserResponseGenerator that inherits from the built-in AuthorizeInteractionResponseGenerator class.
After the user authenticates, the UI correctly diverts the user to an Account Chooser view, which lists the tenants the email address is associated with. The idea being that the user must now select the Tenant he/she wants to log in to, and then get redirected to that Tenant's URI.
My problem though is that I can't figure out how, after selecting a Tenant, I can add that Tenant Id as a Claim to the token that gets passed to the app from IdentityServer (multiple tenants could share the same URL so I need something in the token to know which Tenant has context).
So in the AccountChooserController.TenantSelected(long tenantId) method, I'm expecting to be able to add this tenantId to the User Claims, but it does not seem like this is supported.
Please advise if this is possible, and how?
Put the tenant information into the cookie when calling SignInAsync - you can then retrieve it from your profile service.

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

Are there security-risks in managing multi-tenancy based on Claims using IdentityServer4?

For some time i've been busy building an identity-server, some complicated structures came up as i'm trying to implemented multi-tenancy.
I would like to know if there are any security-risks regarding the approach im planning to take. Maybe others are struggling as well and could use an example-structure. The following case is present:
One identityserver
Multiple tenants with seperate domains
Multiple user-accounts possible for a user per tenant (Student & Teacher)
Registered with multiple tenants under differentusernames/email-addresses
SSO required for all tenants at once
The structure i had in mind:
A user is logged in at the Identityserver. The available clients (applications/tenants) for which de id-server can provide authentication are presented like a hub. One is selected (Company3 for example) and the option to link an account for this specific tenant is available.
The user provides an email (Student#company3.com) and a verification is sent to the user. The identityserver-database holds data regarding a one-to-many relationship between a user and the registered client, and a one-to-many between these registrations and the verified email-addresses.
When logging into the identityserver, the token would hold the data for the tenants and the verified accounts. This token is returned and the current client verifies the token using the key from the discovery-endpoint.
An example of the custom claims would be:
{
"company3":[ "student#company3.com", "teacher#company3.com"]
"company1":[ "student#company1.com", "teacher#company1.com"]
}
Company3 would have secure confirmation that the current user has access to 2 accounts. Company1 would have the same information out the token which makes SSO between the different tenants possible.
As i see it, there wouldnt be any security risks. But, im not very experienced with Identity-servers and authentication yet. I would love some feedback regarding possible security risks.
Cheers

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