AzureAD / microsoft-identity-web

Helps creating protected web apps and web APIs with Microsoft identity platform and Azure AD B2C
MIT License
682 stars 214 forks source link

[Documentation] How to call a Azure AD downstream api from an Azure B2C application #2239

Open LockTar opened 1 year ago

LockTar commented 1 year ago

Documentation related to component

Microsoft.Identity.Web

Please check all that apply

Description of the issue

I have 2 tenants. One Azure AD (Tenant A) and an Azure AD B2C (Tenant B). In Tenant A I have a multi tenant app registration (App 1). In Tenant B I have an application where I can login with a Blazor Server frontend with Azure B2C user flows (App 2).

In App 1 I have a scope: https://graph.dev.sample.com/access_as_user

In App 2 I want to call the API (App 1) as the logged in user.

How can we do that? I think I have 95% complete but missing the last part. I never get a consent screen... How can I force it? Do I need to use "common" somewhere?

michiproep commented 1 year ago

Hi @LockTar, this does not work. The logged-in user can only get access tokens for resources registered in the same IdentityProvider. This is not an Azure(B2C) restriction, this is how OIDC/Oauth works.

You can call App1 as ConfidentialClient and pass along some user-info within headers or payload.

LockTar commented 1 year ago

@michiproep this is not correct. I already have it working... In de sample applications you also see multi tenant setups. Why do you think it can't be working? This should be a normal multi tenant application setup.

michiproep commented 1 year ago

@LockTar: Loggin in into a AzureB2C app with UserFlow (as described in the initial description) means the Blazor App is NOT hooked to the multitenant application. If you login to a multitenant-app you probably don't use b2clogin.com at all, right?

May-be, I got something wrong here but I don't see a way of getting an accesstoken for a "B2C-only-user" to a multitenant azure app.

May-be, you could share a repo? I'd really like be proofen wrong here.

LockTar commented 1 year ago

@michiproep sorry for the late response. Busy, busy, busy...

Let's say you have many different type of customers for your data. You have employees logging into your enterprise Azure AD (with different roles), have customers that login with Azure B2C and maybe you have external partners that login with external IDP. When you create an API abstraction layer for you data (just like the Microsoft Graph) you can provide data for all types of customers once and not have to create numerous (almost) duplicate API's.

I'm not saying that you always must do this because it also comes with a higher security/testing impact of course! You as a company/developer should think about the impact and act on that.

But let's say you want to create an invoice API for all your customers.

So:

You can set it up as follow:

  1. Create an app registration in the enterprise Azure AD for your "Management portal" frontend were employees can login
  2. Create an app registration in Azure AD B2C for your "Customer portal" frontend were customers can login
  3. Create a multi tenant app registration in the enterprise Azure AD for your Invoice API

Then create App Roles on the API app registration so you can create roles for your different types of employees. So managers, operations, customer support desk etc. Extend to what you like.

Create a portal for your employees and connect it to app reg 1. Create a second portal (or use same portal but use a second auth provider) and connect it to app reg 2.

Important to get it working for customers: Connect the multi tenant app reg in the enterprise Azure AD to the Azure B2C tenant. You can do that by creating a service principal in B2C that has a connection to the AppId of the multi tenant app (app registration 3). Make sure that you have a scope on appreg 3 something like "access_as_user" and that you allow the client to consume that scope.

No when a customer or an employee logins into his portal (or the same portal) it can request a delegated accesstoken for the API. The API then can check what the IDP is and who is really calling the API. Is that employee John with the role "Customer support" or is that customer Jane that is wants to see there own invoice?

Don't forget to test your API endpoint! Make the security testing a really important part of your job. You don't want that Jane can grab all invoices of every customer. This is not really in this setup the case but of course in every situation.

Hopefully this helps!

michiproep commented 1 year ago

Hi @LockTar , thanks for your effort here. I see what you're doing now. May I ask a few more questions because I don't understand some details.

  1. How does the part "Important to get it working for customers" work in detail? -Is that a 4th app registration? (actually an API AppRegistration in B2c?) -Or does it just make your multitenant app show up in the App3->ApiPermissions->Add->UI in AzurePortal

-And how does "...connection to the AppId of multitenant app..." look like / how to setup this? Can you paste an example script/request?

  1. Is it also correct that you created a multi-tenant app because you want access from excactly 2 tenants - employee and b2c?

Thanks in advance!

LockTar commented 1 year ago
  1. How does the part "Important to get it working for customers" work in detail? -Is that a 4th app registration? (actually an API AppRegistration in B2c?) -Or does it just make your multitenant app show up in the App3->ApiPermissions->Add->UI in AzurePortal

No it's not a fourth app registration but a Service Principal. You can find the SPN's in the Enterprise Application blade in the azure portal. So an application is more the description of the application but the actual permissions are set on the SPN. So when you have a multi tenant app reg you can connect multiple SPN's to that app reg. So you can connect 1, 2, 3, 4, etc SPN's. Basically how the Microsoft Graph API is working in your enterprise tenant. Microsoft has in his system an app reg called Microsoft Graph and your Azure AD, my Azure Ad and all other Azure AD's have a SPN connected to that app reg.

So with the powershell (in the link in previous comment) you create a service principal connected to that multi tenant app reg.

-And how does "...connection to the AppId of multitenant app..." look like / how to setup this? Can you paste an example script/request?

Well when you created the app registration and created the SPN to the multi tenant app reg in the B2C tenant. The customer portal app reg should have the scope "access_as_user" from your API app registration. That should now be visible in the "API Permissions" blade of the app reg. Do an admin grant for that delegated scope.

  1. Is it also correct that you created a multi-tenant app because you want access from excactly 2 tenants - employee and b2c?

If I understand you correct, Yes! I want to have access to my API from 2 Azure AD's. Customer B2C and employee enterprise azure AD.

Don't forget that you always should check the token in the API!!! If it's a valid issuer, authority, subject etc!

michiproep commented 1 year ago

@LockTar Thanks. Figured it out. I have similar requirements but I actually don't like this approach since it's not really a multi-tenant api. It's an Api for exactly two tenants. That's why I'll register one App (api) in enterprise tenant and the other in b2c. In the api-code I'll then have two bearer auth schemas... Even then I would need two login buttons on the public facing app. I probably register everything in b2c and federate my enterprise idp with b2c. What do you think of that?

ckupferschmid commented 4 months ago

Hi We do have pretty much the same topic. In our case, we have our Enterprise Tenant (TenantA) and 3 B2C Tenants (Tenant1, Tenant2 and Tenant3) Now we want to expose a central multitenant app in TenantA that can be consumed by all the mentioned tenants.

Of cause, the api will need to verify the access token and make sure only 'known' tenants can access the api, but to have an easier single api registration (and import that to central exposed registration to tenant1-3) makes it easier to configure, handle and monitor for us.

So we registered the api in our main TenantA as multitenant application and then 'imported' the registration as service principal into all the B2C tenants (tenant1-3) using

New-AzureADServicePrincipal -AppId $AppID

then we authorized the locally registered Client Apps from every tenant (TenantA as well as Tenant1-3) to request scopes exposed from the multitenant app and granted admin consent in all the Tenants.

Now when we try to acquire an access token, we get:

AADB2C90205: This application does not have sufficient permissions against this web resource to perform the operation.

Any help/ideas would be appreciated.

LockTar commented 4 months ago

@ckupferschmid First of all, New-AzureADServicePrincipal is a very old cmdlet that shouldn't be used anymore. Basically it's doing the same as the new ones but the whole module is deprecated.

It really depands on what you are trying to do. You want to call from 3 B2C tenants the API on the main tenant right? You want to do that as deamon or as user? Difference between application or delegated permissions. When you added the main multi tenant app to B2C with the cmdlet, you could set the permissions on the local b2c app registration. Have you done that? After that, you could simple create an accesstoken from b2c to the main tenant. You don't need to do anything on the main tenant.

Be careful! By creating a multi tenant app reg in the main tenant, your opening up your tenant. So every tenant let's say mine evil tenant could connect to your multi tenant app. I could simple create an access token for your app. You can't prevent that. On the app side of your multi tenant app, you need to filter on the incoming requests. In example the client id (the client/app id of the local b2c tenant).

ckupferschmid commented 4 months ago

@LockTar Thank you for your reply.

Yes, I could also use the New-MgServicePrincipal command, Tried it and it also created the same ServicePrincipal and the same Error when trying to acquire the access token.

From the 3 B2C tenants, users must be able to login and acquire access tokens for the api from the main tenant.

I have granted the scope exposed by the multitenant app to the registered client app that is locally registered in the B2C tentants, so the B2C client app should be able to request an access token. Also, I have granted 'admin consent' in the B2C tentants, so every user can use the B2C tenants can use the api.

So I did the following

Main tenant:

B2C tenants:

But I do get the above mentioned error.

I think ether it's not supported or we miss something.

Regarding opening the main tenant for others. My understanding is, that the third-party tenant is issuing the access token, not the main tenant and the api MUST filter and validate tokens from allowed tenants in order to be safe. The main tenant 'just' manages the definitions of the application that then gets 'imported' as a service principal -in our case - into the B2C tenants. Admins of the B2C tenants can then manage the access to the exposed scopes/roles for the users in their tenant. Third rogue third party tenants can potentially create the service principal in their tenant, but as long as our api is filtering allowed tenants, It cannot access our api, because our api would not validate the access token from that tenant. https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser#relationship-between-application-objects-and-service-principals

LockTar commented 4 months ago

I think your trying something that isn't supported. App Roles aren't supported in Azure B2C. The App Roles in the main tenant can only be used with normal (non Azure B2C) tenants. Not sure what that role assignment cmdlet is. But I don't think it will work because roles are not supported. I have everything working fine in production but I'm moving a way from Azure B2C because of the lack of options. It's also to difficult to add new features.

I have the main tenant with scopes and roles. I have roles only for apps (deamon apps) like "Customers.Read.All" and roles for users like "Servicedesk user". I have one scope "access_as_user". That scope is used for Azure B2C users. That scope is then also selected in the API permissions blade at het B2C app side. The App Roles like "Servicedesk user" are used for internal employees.

I also set in the manifest accessTokenAcceptedVersion to the value 2 so you always use the v2 endpoints. On all applications. I don't want to support legacy v1 endpoints.

Your exactly right, you say the same but different words. Maybe I was not clear. The main tenant must filter out the bad evil tenants. As long that is clear. But most people forget that. They create an App Role like "Customers.Read.All" and forget to filter out the correct clients. So opening up the data to the outside.