MicrosoftDocs / azure-docs

Open source documentation of Microsoft Azure
https://docs.microsoft.com/azure
Creative Commons Attribution 4.0 International
10.2k stars 21.36k forks source link

OpenID Connect Issuer doesn't match issuer in the metadata #38427

Closed MaikuMori closed 4 years ago

MaikuMori commented 5 years ago

The discovery URL given for common endpoints is:

https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

But the metadata document itself returns:

issuer: "https://login.microsoftonline.com/{tenantid}/v2.0"

The base part of these two should match according to the spec. This breaks OIDC Connect libraries which are rigid about the spec.

For example: https://github.com/coreos/go-oidc

See also https://github.com/coreos/go-oidc/issues/159 https://github.com/coreos/go-oidc/issues/212

If this is not the correct place where to file and issue let me know where I can do that.


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

neeleshray-msft commented 5 years ago

@MaikuMori Thank you for your query. We will investigate and update the thread.

MaikuMori commented 5 years ago

The workaround that I found and documented here: https://github.com/coreos/go-oidc/issues/212#issuecomment-528805306 only works for the personal Microsoft account sign-in.

I still believe that the metadata server is not working according to OIDC spec and should never return issuer with value: https://login.microsoftonline.com/{tenantid}/v2.0. It even looks like a bug.

Obviously changing the output is a breaking change. What if there was a UUID alias like the one for consumers tenant, but instead for common? Then we could use that as metadata endpoint and that endpoints could respond with correct issuer just like the https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0 does.

shashishailaj commented 5 years ago

@MaikuMori Thank you for sharing your findings . We will engage with the content authors to get more clarity on this.

@rwike77 Could you please take a look at this issue and provide your thoughts on this ?

codyfyi commented 4 years ago

Thank you for looking into this!

I've also seen this issue prevent several OIDC compliant services from integrating with Microsoft. For example Amazon Cognito refuses to integrate because it correctly notices the iss in the JWT token does not match the .../{tenantid}/v2.0. issuer in Azure's .well-known endpoint and other auth services fail for the same reason.

This quirk makes Azure non-compliant with the OpenID Connect specification.

rwike77 commented 4 years ago

@hpsin , can you comment on this? Thanks.

hpsin commented 4 years ago

Yes, this is unfortunately an inherent issue with the /common alias. By using /common, the app is indicating that they expect a user in any tenant (issuer) to be able to sign into the app. We reflect the ultimately chosen tenant in the issuer claim. This is especially important when there are tenant specific signing keys, where the app must know which jwks URI to visit in order to get the key used to sign the token.

@codeitcody - can you provide some other services that this has blocked? It would be helpful to understand who's using /common like this and encountering these issues.

Unfortunately there's no way at this time to make /common OIDC compliant - only our tenanted URLs (of which /consumers is one, as it's just an alias to the singleton MSA tenant) can be OIDC compliant. We can consider breaking tenant-specific signing keys (it's proprietary) and force those apps to build the jwks URI from scratch (s/common/$claims.tid/), or allow apps to opt-in to a /common issuer so that in the future they get expected values. I don't love the idea of making standards compliance opt-in though. We'll discuss internally on what's the least-bad way to address this, if possible.

rwike77 commented 4 years ago

I followed up with engineering, unfortunately it's not possible to make /common OIDC compliant right now. Closing this doc issue, feel free to reopen if necessary. #please-close

GlitterHorn commented 4 years ago

Is there any chance of this being fixed?

I happened upon this bug when I wanted to set up Microsoft SSO with Gitea. I can only allow auth to the consumers tenant thanks to this bug.

hpsin commented 4 years ago

Unfortunately not, we have to rev the entire platform (v3) to change the issuer.

GlitterHorn commented 4 years ago

Is that in the roadmap?

MaikuMori commented 4 years ago

What about having both V2 and V3 up? That way old code can still function and newer code can use the standards compliant version.

hpsin commented 4 years ago

Not on the roadmap currently. We absolutely would have both supported concurrently (as well as v1) but making a radical change to the existing architecture is out of scope right now given our current workload.

GlitterHorn commented 4 years ago

Ok. I suppose it's not the end of the world - my Gitea instance is just for people to file issues against my own projects, so I don't need AAD support.

Thanks for the details, though!

tsuna commented 3 years ago

How are we supposed to have use OIDC in multi-tenant apps with customers that use Azure as their identity provider?

issuer did not match the issuer returned by provider, expected "https://login.microsoftonline.com/common/v2.0" got "https://login.microsoftonline.com/{tenantid}/v2.0"

Do people hard-code an exception for the fact that Microsoft can't seem to implement OIDC properly?

hpsin commented 3 years ago

Yes - they validate tokens based on the issuer claim, rather than the authority. Or validate the token based on the keys (static across tenants) and signature only. The common endpoint is not OIDC compliant - only tenanted endpoints can be.

engenb commented 3 years ago

Hey @hpsin, I was hoping to get some clarification regarding one of your earlier responses:

Yes, this is unfortunately an inherent issue with the /common alias. By using /common, the app is indicating that they expect a user in any tenant (issuer) to be able to sign into the app.

Are you referring to the common authorization endpoint?

I use my tenant-specific authorize endpoint https://login.microsoftonline.com/{tenat id}/oauth2/v2.0/authorize to get an access code and then exchange that for a token from my tenant-specific token endpoint https://login.microsoftonline.com/{tenant id}/oauth2/v2.0/token and yet the issuer, as specified by the "iss" claim, is still https://sts.windows.net/{tenant id}/

Additionally, my token has a claim "ver": "1.0" which I suspect is telling me I got a V1 token from the V2 endpoint based on this doc.

I also saw some discussion here that suggested editing my Azure AD registered application's manifest to set "accessTokenAcceptedVersion": 2 which I have done, but this does not seem to have any effect. I'm not msal.js (the subject of the linked issue) but the conversation here is relevant.

I've also found some info mentioning that the V2 endpoint may return a V1 token based on the scopes requested. I'm requesting scopes based on my registered app resource uri i.e. api://preprod.api.mycompany.com/.default - is this V1 only?

Is this behaving as you'd expect? This causes problems for every api we have that is performing token validation as implemented by default in asp .net core jwt bearer middleware. Without some configuration to override the valid issuers, validation fails. We can work around this but ideally, we'd be working within the spec and getting the issuer value from the metadata doc and not having to manage separate configuration for this sts.windows.net issuer.

Additionally, we're linking federated external identities to B2C users. For external identities authenticating with Azure AD, the ideal solution for us would be to register a federated identity using the issuer and unique id from the identity provider, Azure AD in this case. However, the issuer does not match the issuer defined in the token when these external users log in so we will need some workaround here as well.

I've been doing a lot of experimenting and research and I'd really appreciate any info you can provide that might clarify the conditions that result in the V2 endpoint returning a V1 token.

Thanks

hpsin commented 3 years ago

Are you referring to the common authorization endpoint? Yep - login.microsoftonline.com/common/oauth2/v2.0/*

The issuer for v1 tokens is indeed sts.window.net.

Scopes and APIs are defined in a app registration - so e.g. your api://preprod.api.mycompany.com/ corresponds to some application registration. That app is the only app that should ever validate a token for the api://preprod.api.mycompany.com/ scopes.

The API entirely and completely owns the formatting of their tokens. It doesn't matter what a client application does, or how a token is requested - the format of that token will always be the same - it'll be what the API says it should be. This ensures that APIs can reliably validate their tokens, rather than worrying about how the token was requested.

api://preprod.api.mycompany.com/ needs to set the accessTokenAcceptedVersion to 2. Doing that on a client will do nothing.

For every single authentication, we do:

  1. What API is the app trying to access? (api://preprod.api.mycompany.com/)
  2. What format of token does that API require? (1, in your case)
  3. Emit a token in that format. (v1)

This code runs on both the v1 and v2 endpoint, and makes no considerations about the client app when determining the shape of the token.

engenb commented 3 years ago

api://preprod.api.mycompany.com/ needs to set the accessTokenAcceptedVersion to 2. Doing that on a client will do nothing.

This made me realize where I went wrong. I had configured accessTokenAcceptedVersion on the client application, not the api application which I've gotta admit makes perfect sense and I should have known better :man_facepalming:

This solved my problem! Thanks a million and thanks for the quick response @hpsin!

MNF commented 3 years ago

@ClairelyClaire mentioned that AzureAD .well-known config is partially invalid, making OAuth2 registration with the "common" or "organizations" endpoints impossible go-gitea/gitea#12073 I also noticed the issue on organisations ( not common) endpoints, getting errors like oidc: issuer did not match the issuer returned by provider, expected "https://your-tenant-name.b2clogin.com/tfp/c5b28ff6-f360-405b-85d0-8a87b5783d3b/B2C_1A_signin/v2.0/" got "https://your-tenant-name.b2clogin.com/c5b28ff6-f360-405b-85d0-8a87b5783d3b/v2.0/“ The actual one has no policy name in the url.

Is any plans to make organisation endpoints oidc compliant?

hpsin commented 3 years ago

In a possible v3, as mentioned above. We cannot break our existing apps.

Use of B2C here is out of scope - I believe B2C requires you to use the special B2C issuer and endpoints, no?

barshow commented 3 years ago

@hpsin what about another keyword like common-compliant, or something that allows the issuer to be what it is suppose to be. I would also argue a major version bump is allowed to break apps, but that is another discussion.

hpsin commented 3 years ago

Good call - we usually think about this in terms of clients getting access tokens, and resources validating those access tokens, with no control over how the access token was requested (e.g. they're often requested from v1, even if you're running a v2 resource).

However, we could indeed fix this with an ID token issuer optional claim, or a new issuer for metadata.

Would you prefer to leave code as-is, and set an optional claim in the app config, or use a new issuer (common-compliant, e.g.) ?

barshow commented 3 years ago

@hpsin this is a good question, I would think a new issuer would have the most flexibility in the case that an app maybe uses multiple oidc libraries, web vs phone etc. Thank you for even considering this!

hpsin commented 3 years ago

That makes sense - it's a run-time decision tightly coupled to the code, not the app registration. I'll get some other folks to give some input. Thank you for the idea and helping tease out the distinctions here.

GoodiesHQ commented 2 years ago

I came across this issue adding OAuth2 to my application. What I'd like to do is allow any email of any organization to use my application by signing into Azure. All I really need is the authorized email account, then my application creates its own bearer token that is used throughout as only identity verification is required. I am okay if personal accounts are not used. I am using authlib in Python and using the /common endpoint appears to completely fail for any account, tenented or not. It's completely unusable with authlib and it appears there's no non-hacky way of solving this at all short of forgoing issuer validation.

I can't use the tenented enpoint because I want ANY organization to be able to authenticate with ease.

Am I totally out of luck? Can I really only support my own organization authenticating? What do I have to do? Any pointers are appreciated.

Edit: I can use the /consumers endpoint and properly support personal accounts, but is there really no way to support any teneted account? It seems a little crazy.

soplan commented 2 years ago

Is there already a fix for this so we can use commonv2 or something else

I need it to support work and personal accounts for Amazon cognito

aeneasr commented 2 years ago

This issue is affecting all services which require an OIDC compliant upstream service, including the largest Auth OSS ecosystem in the world: https://github.com/ory

We'd be happy to assist when v3 comes out, Ory Hydra is an excellent OIDC compliant authz server ;)

kralicky commented 1 year ago

I'm running into this issue trying to test Azure AD as an openid provider for our software. Using the endpoint https://login.microsoftonline.com/<my_tenant_id>/v2.0 obtained from the endpoints list on the web dashboard, all id tokens have an incorrect issuer claim of "iss": "https://sts.windows.net/<my_tenant_id>/". I also tried the endpoint https://login.microsoftonline.com/organizations/v2.0 (also from the endpoints list, after switching the account types option), but this one has the {tenantid} placeholder in its openid-configuration issuer url, so that won't work either.

Setting "accessTokenAcceptedVersion": 2 in my app's manifest didn't seem to change anything. Unsure how to proceed here.

jonathandavis5 commented 1 year ago

@kralicky

I'm running into this issue trying to test Azure AD as an openid provider for our software. Using the endpoint https://login.microsoftonline.com/<my_tenant_id>/v2.0 obtained from the endpoints list on the web dashboard, all id tokens have an incorrect issuer claim of "iss": "https://sts.windows.net/<my_tenant_id>/". I also tried the endpoint https://login.microsoftonline.com/organizations/v2.0 (also from the endpoints list, after switching the account types option), but this one has the {tenantid} placeholder in its openid-configuration issuer url, so that won't work either.

Setting "accessTokenAcceptedVersion": 2 in my app's manifest didn't seem to change anything. Unsure how to proceed here.

Hi, did you ever get to the bottom of this? My ID tokens have the correct tenanted issuer but the access tokens do not. Thanks

kralicky commented 1 year ago

@jonathandavis5 Yep... unfortunately not much you can do here. Azure is simply not OIDC compliant. :-1:

amplicity commented 1 year ago

It's been 4 years, is this seriously not resolved?

DerekTCS commented 1 year ago

Firebase authentication is also refusing to work without a strict OpenID Connect implementation.

For now the only way around this issue is using custom policies, as documented here: https://docs.gitlab.com/ee/administration/auth/oidc.html#configure-microsoft-azure-active-directory-b2c

Can't wait for this issue to be fixed.

Vits-99 commented 2 months ago

It's been 5 years now, is this seriously still not solved?