AzureAD / microsoft-identity-web

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

[Feature Request] Improve MSI support in Id.Web #2551

Closed bgavrilMS closed 1 month ago

bgavrilMS commented 1 year ago

This is a large feature consisting of several work items:

bgavrilMS commented 12 months ago

@jmprieur, @jennyf19 - I added here some notes about samples for MSI and FIC. I think we are all in agreement that MSI standalone sample is needed. Not sure how you feel about FIC, but I think we should have one (another chapter in client_credentials)

jennyf19 commented 9 months ago

@JoshLozensky for the E2E test, would recommend creating a new test class under TokenAcquirerTests, these have similar E2E tests. You'll want to make sure the test only runs in ADO, because we'll be using the system assigned managed identity, so check this IgnoreOnAzureDevopsFactAttribute for an idea on what to do. If you look at the DaemonConsoleCallingDownstreamApi, you'll get an idea of how we can have a very simple test using TokenAcquirerFactory, but instead of using IDownstreamApi as the service, you will want to use IAuthorizationHeaderProvider. Then api.CreateAuthorizationHeaderForAppAsync() and use keyvault as the scopes, as when running in ADO, in the MwWilson1ESHostedPool, there is a user assigned managed identity: idwebvalidationbuild - which has access to keyvault, so we will get a token to keyvault. The scopes to use are "https://vault.azure.net/.default" and then just check that the result is not null.

bgavrilMS commented 9 months ago

@JoshLozensky - is it fair to say that checkbox 1 is done? Will you be working on a sample and / or on updating FIC next?

jennyf19 commented 9 months ago

@bgavrilMS - can you give links to the specific samples you want updated: Update Workload Identity samples to use Id.Web instead of MSAL Add a sample for MSI next to the client_credentials sample Add a sample for certificatless next to the client_credentials sample (?) With @JoshLozensky and @jmprieur we checked the boxes on the done work above. @JoshLozensky will make issues in the samples to track the work

LarsKemmann commented 8 months ago

When trying to read through the 2.17.0 release and related PRs I'm getting confused... is it now possible (with 2.17.0 and/or with extra configuration work on the underlying MSAL.NET components) to set up my web app (running on Azure App Service with a user-assigned managed identity) to be able to sign in users with the authorization code flow, without requiring either a client secret or a certificate (i.e., private key)? Basically, a workload federation-type of deal on the app registration where I can specify which set of managed identities is allowed to act as the application (I would imagine this requires configuring the app registration's workload federation to accept the managed identity system's token issuer as the IdP)? Or is this issue just about enabling Id.Web to use certificates that are stored in Azure Key Vault (and authenticate to AKV with a managed identity) rather than locally with the web app? That would be nice I'm sure, but I would really like to get rid of secrets (including certificate private keys) altogether from our environment and the authorization code flow for user sign-in is the one place where I just can't do that yet.

jmprieur commented 8 months ago

Hello @LarsKemmann

Workload identity federation is already supported by Microsoft.Identity.Web. you would need to use a client credential. See https://learn.microsoft.com/dotnet/api/microsoft.identity.abstractions.credentialdescription.managedidentityclientid?view=msal-model-dotnet-latest. You will also need to change your app registration (in the certificate and secret tab) to add the federation identity credential with your managed identity.

image

and choose 'Other issuer'

For the support of managed identity, we just updated the doc: https://github.com/AzureAD/microsoft-identity-web/wiki/calling-apis-with-managed-identity cc: @jennyf19, @JoshLozensky

LarsKemmann commented 8 months ago

You will also need to change your app registration (in the certificate and secret tab) to add the federation identity credential with your managed identity.

@jmprieur This sounds fantastic -- but am I understanding correctly that this allows using the managed identity to provide the client assertion required by the /oauth2/v2.0/token endpoint, in the authorization code grant (so that my confidential client web app can get an access token for the user to access the protected resource without needing a client secret or certificate)? If so, can you explain what the settings are that I would need to provide as the 'Other issuer' in the "Certificates & secrets" tab? The only guidance I can see for using this capability is this short blurb in the wiki (https://aka.ms/ms-id-web/certificateless) but that doesn't give me many clues, and doesn't even mention the Microsoft.Identity.Web.Certificateless NuGet package.

LarsKemmann commented 8 months ago

I see on the 'Other issuer' federated credential scenario page that the "Subject identifier" should be the object ID of the managed identity's service principal: image image That seems to conflict with what the blurb in the v2 wiki notes:

Microsoft.Identity.Web.CertificatelessOptions was renamed from ManagedIdentityObjectId to ManagedIdentityClientId (request Chris Brooks). This is unlikely to affect you though as this was an experimental feature.

Is the UI out of date -- should I actually use the managed identity's client ID rather than its object (principal) ID?

And what value do I use for the Issuer? I do see from the code that the Audience should probably be left as the default (api://AzureADTokenExchange)?

(I'm assuming Name and Description are for my reference only...?)

And do I also need to do anything with the "Federated credentials" tab on my actual (user-assigned) managed identity, or do I leave that as the default (i.e., it's only needed for non-Azure-hosted workloads)?

LarsKemmann commented 8 months ago

Looking at one of the MI tokens returned by the IMDS when I specify api://AzureADTokenExchange as the resource, I see that the issuer is going to be in the format https://login.microsoftonline.com/{TENANT_ID}/v2.0. So that's helpful. The sub and oid claims are both apparently being set to the object (principal) ID of the managed identity (at least in the case of a user-assigned managed identity) rather than to the client ID. I'll give that a try (tomorrow) and report back. 😊

I did notice that that token doesn't really conform to all the requirements in the spec - in particular, iss is set differently than expected (probably for a good reason) and jti isn't set (but I know there are other AAD-specific claims on the token that serve essentially the same purpose). So hopefully this works... 🤞🏻

MarcelMichau commented 6 months ago

@LarsKemmann were you able to test this out & get it working?

My scenario is similar in that I have a protected Web API which needs to call a downstream protected Web API on behalf of the user using ITokenAcquisition.GetAccessTokenForUserAsync(scopes). I would also very much like to remove the need for using client secrets/certificates in favour of using managed identity, though from what I can gather, this scenario is not supported.

I am able to call the downstream API on behalf of the app using a user-assigned managed identity with ITokenAcquisition.GetAccessTokenForAppAsync(scopes) using the approach detailed in Calling APIs with Managed Identity so this scenario works perfectly well.

When calling the downstream API on behalf of the user & using a client secret, this works perfectly fine as well.

I then attempted to update my configuration to use the ManagedIdentityClientAssertion as per below:

...
    "AzureAd": {
        "Instance": "https://login.microsoftonline.com/",
        "Domain": "xxxxxxx.onmicrosoft.com",
        "TenantId": "xxxxxxx",
        "ClientId": "xxxxxxx",
        "ClientCredentials": [
            {
                "SourceType": "SignedAssertionFromManagedIdentity",
                "ManagedIdentityClientId": "xxxxxxx"
            }
        ]
...

With only this changed & removing the client secret from the configuration I receive the following error:

AADSTS70025: Client application has no configured federated identity credentials

At this point, I found this post & based on the above detail I added the user-assigned managed identity to the App Registration federated credentials with the below details:

Federated credential scenario: Other issuer Issuer: https://login.microsoftonline.com/{TENANT_ID}/v2.0 Subject identifier: user-assigned managed identity object ID

Upon doing this, I receive another error:

AADSTS700222: AAD-issued tokens may not be used for federated identity flows.

Searching for this error brings up this doc stating that "Creating a federation between two Microsoft Entra identities from the same or different tenants isn't supported."

I've been racking my brain trying to understand if what I'm trying to achieve is even possible & that I'm just misconfiguring something somewhere, or if it's indeed the case that doing an OBO flow just has to use a client secret/certificate & that's how it is & always will be - due to technical reasons/limitations that I'm not aware of.

Just for additional context, this was tested with the latest 2.18.1 version of Microsoft.Identity.Web.

@jmprieur any guidance or assistance will be appreciated.

LarsKemmann commented 6 months ago

@MarcelMichau Unfortunately, I wasn't able to try this out yet. Thank you for the write-up, I wish I could have saved you some of the effort that it must have taken to put that together.

@jmprieur This feels like something that the Entra team should be able to resolve. There's nothing in the standard that inherently prevents a managed identity and an app being federated.

scrocquesel commented 2 months ago

@MarcelMichau Unfortunately, I wasn't able to try this out yet. Thank you for the write-up, I wish I could have saved you some of the effort that it must have taken to put that together.

@jmprieur This feels like something that the Entra team should be able to resolve. There's nothing in the standard that inherently prevents a managed identity and an app being federated.

Using MI as FIC for Entra App Registrations seems to be on the close roadmap. @pamelafox presented this flow at Build 2024, 24'30''. Maybe we could get a more precise date of when it will be publicly available.

scrocquesel commented 2 months ago

When available it could be interesting to test if we can actually use a FIC with MSI assertion in a daemon app. Ifo so, should we use SourceType or AcquireTokenOptions ?

bgavrilMS commented 2 months ago

@scrocquesel - FIC with MSI assertion is already being used internally, good to hear from @pamelafox on it making it to 3p soon.

As for the dev ex, we'd prefer this to be a configuration:

{ 
  "AzureAd": { 
    "Instance": "https://login.microsoftonline.com/", 
    "ClientId": "YOUR_APPLICATION_ID", 
    "TenantId": "12345"

   // To call an API 
   "ClientCredentials": [ 
      { 
        "SourceType": "SignedAssertionFromManagedIdentity", 
        "ManagedIdentityClientId": "YOUR_USER_ASSIGNED_MANAGED_IDENTITY_CLIENT_ID",
        "TokenExchangeUrl":"api://AzureADTokenExchange"
      } 
   ] 
  }, 
  // more here.. 
}  
OmnipotentOwl commented 1 month ago

When running in Kubernetes would you use SignedAssertionFromManagedIdentity or SignedAssertionFilePath? Thinking about the flow itself it would probably be something like Kubernetes Service Account -> User Managed Identity (Federated Identity) -> Entra Id Application (Federated Identity). From that, I would tell MIW the ManagedIdentityClientId and the ClientId for the Entra Id Application so that it can walk up the token exchanges to get to the correct token.

bgavrilMS commented 1 month ago

Closing as this feature has been completed. Tracking MSI + FIC samples separately.

bgavrilMS commented 1 month ago

@OmnipotentOwl - for Kubernetes this is SignedAssertionFilePath.

Federated Identity means that there is a trust relationship between an "external" IdP and Entra ID. The trust relationship is described in the app portal. Entra Id will trust tokens with a specific issuer, audience etc from the external IdP. These tokens can be used as client assertions for all confidential client scenarios, eliminating the use of secrets and certificates as basis of trust.

There are 2 forms of Federated Identity:

Today, Kubernetes drops the tokens in a file in a well known location. Hence using SignedAssertionFilePath. There is no managed identity in the flow whatsoever.