Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
4 of 6 tasks
bgavrilMS opened this issue Oct 24, 2023 · 17 comments
Closed
4 of 6 tasks

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

bgavrilMS opened this issue Oct 24, 2023 · 17 comments
Assignees
Labels
enhancement New feature or request feature request
Milestone

Comments

@bgavrilMS
Copy link
Member

bgavrilMS commented Oct 24, 2023

This is a large feature consisting of several work items:

@bgavrilMS bgavrilMS added enhancement New feature or request feature request labels Oct 24, 2023
@jennyf19 jennyf19 added this to the 2.15.4 milestone Oct 30, 2023
@bgavrilMS
Copy link
Member Author

@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
Copy link
Collaborator

@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
Copy link
Member Author

@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
Copy link
Collaborator

jennyf19 commented Jan 30, 2024

@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
Copy link

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
Copy link
Collaborator

jmprieur commented Feb 8, 2024

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
Copy link

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
Copy link

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
Copy link

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
Copy link

@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
Copy link

@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
Copy link

@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
Copy link

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
Copy link
Member Author

@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
Copy link

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
Copy link
Member Author

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

@bgavrilMS
Copy link
Member Author

@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:

  • with external Identity Provider, like Kubernetes.
  • with internal Identity Provider, Managed Identity (technically Managed Identity issues AAD tokens, but managed identity is constrained to a single tenant etc.)

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.

@bgavrilMS bgavrilMS closed this as not planned Won't fix, can't repro, duplicate, stale Sep 30, 2024
@bgavrilMS bgavrilMS reopened this Sep 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request feature request
Projects
None yet
Development

No branches or pull requests

9 participants