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

Microsoft IdP compatibility #62

Closed
bresam opened this issue Jun 5, 2024 · 8 comments
Closed

Microsoft IdP compatibility #62

bresam opened this issue Jun 5, 2024 · 8 comments

Comments

@bresam
Copy link

bresam commented Jun 5, 2024

You were right about the rfc. I am sorry, had a different interpretation of the specs.

I've found some information about the difference to the Microsoft stuff and i have two initial questions:

  • Are you interested in supporting Microsoft IdP in general?
  • Would you be willing to accept a clean PR of me?

If so, i would prefer to clarify how a possible solution could look like in first place.

Unfortunately Microsoft does here really something different (wrong!?) than expected.

Here is the Microsoft related problem:

Microsoft uses different issuers for ID Tokens and Access Tokens, which can lead to issues when validating the issuer on tokens. This is a common problem that developers face when working with Microsoft’s identity platform.

ID Tokens
ID Tokens are used for authentication and are issued by the Azure Active Directory (AAD) tenant. The issuer for ID Tokens is typically in the format https://login.microsoftonline.com/{tenant_id}/v2.0. For example, if the tenant ID is 12345678-1234-1234-1234-123456789012, the issuer would be https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0.

Access Tokens
Access Tokens, on the other hand, are used for authorization and are issued by the Azure AD App Registration. The issuer for Access Tokens is typically in the format https://login.microsoftonline.com/{tenant_id}/v2.0/.well-known/openid-configuration. Again, using the same example tenant ID, the issuer would be https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration.

Validation Issues
The main issue that arises from using different issuers for ID Tokens and Access Tokens is that when validating the issuer on tokens, the validation logic needs to be aware of the different issuers used by Microsoft. This can lead to complex and error-prone code, especially when working with multiple tenants or applications.

Conclusion
In conclusion, Microsoft’s use of different issuers for ID Tokens and Access Tokens can lead to issues when validating the issuer on tokens. Developers need to be aware of these differences and implement the necessary logic to handle the different issuers used by Microsoft.

Would be great to discuss a solution.

@bobvandevijver
Copy link
Member

bobvandevijver commented Jun 5, 2024

I have tried Microsoft Entra ID in the background as well. My observation is that it returns a V2 ID token and a V1 access token, even when using the V2 well-known/token endpoint (the V1 well-known url is not even shown in the Azure portal). You can verify this yourself by inspecting the ver claims in the two JWTs. You can also open the V1 well-known (just remove /v2.0), it should have the issuer that is used for your access token (sts.windows.net), while the V2 has the login.microsoftonline.com one.

So the issue is that Microsoft mixes the different versions and return a V1 token while the request uses the V2 endpoint (which has been made explicitly for OpenID: https://learn.microsoft.com/en-us/entra/identity-platform/access-tokens#v10-and-v20-tokens).

A possible solution is to configure your client to use v2 only (which shouldn't be an issue because clients should be unique anyways). You can do this by setting accessTokenAcceptedVersion to 2 in the manifest. It can take some time to propagate and as of now it hasn't propagated for my test client yet (but it has been less than an hour).

See https://stackoverflow.com/questions/59790209/access-token-issuer-from-azure-ad-is-sts-windows-net-instead-of-login-microsofto & https://learn.microsoft.com/en-us/answers/questions/1664801/azure-ad-token-issuer, which discusses the exact same thing.

Actually, MS is even planning on changing the default: https://learn.microsoft.com/en-us/entra/fundamentals/whats-new#general-availability---changing-default-accepted-token-version-for-new-applications

@bresam
Copy link
Author

bresam commented Jun 5, 2024

Thank you for that information.
As i've seen today, i get not only different v1 / v2 parts in the iss but also different domains.
But i will check that tomorrow in detail.

@bresam
Copy link
Author

bresam commented Jun 6, 2024

Good morning @bobvandevijver

I have checked the stuff.

When using v2 (as described in Azure app registration) it generates a JWT as Access Token with different iss compared to the iss of the ID Token.

But when using v1 it doesn't use a JWT for Access Token and of course the validation works like a charm out of the box.

So on the app registration i hadn't to do any change, just switching the well-known url to v1.
Would it be possible to add some Microsoft related information to the readme?

For me the "issue" is solved anyway.

Thank you for support.


On Microsofts app registration they say this is the OpenID Connect metadata document:
https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration

But to work properly it's needed to use:
https://login.microsoftonline.com/{tenant-id}/.well-known/openid-configuration

@bobvandevijver
Copy link
Member

It is to be expected that V1 and V2 tokens use different issuers, as the well-knows for V1 and V2 state different issuers. But why Microsoft keeps generating a V1 access token is a mystery: setting the accessTokenAcceptedVersion configuration hasn't changed anything for me.

But, I have found a workaround to force V2: add a custom scope, and add that to the generateAuthorizationRedirect method.

image

   return $oidcClient->generateAuthorizationRedirect(
      scopes: ['openid', 'api://a17-xxxxx-e7/auth']
    );

That generated a V2 token, which uses the correct issuer.

You are obviously free to use the V1 well-known, but that might bite in the future. I will add a documentation note on this weird MS behaviour.

@bobvandevijver
Copy link
Member

The IdP specific documentation for MS Entra ID has been added: https://github.com/Drenso/symfony-oidc/blob/master/docs/ms-entra-id.md.

@alexeytashmatov
Copy link

Adding scope 'api://a17-xxxxx-e7/auth' doesn't work if I use delegated access scenario
https://learn.microsoft.com/en-us/graph/auth/auth-concepts#access-scenarios

@welcoMattic
Copy link

welcoMattic commented Jan 7, 2025

I've just had a call with a Microsoft Tech expert. I have bad news about their implementation of OIDC.

First, the "hack" that consist of adding a custom scope is the recommended way to do.
Second, even with this "hack", you will obtain a limited Access Token. This Access Token does not allow you to call the userinfo endpoint listed in the .well-known/openid-configuration JSON, as it looks like https://graph.microsoft.com/oidc/userinfo. As you can see, it relies on Graph API, so it requires an Access Token dedicated to the Graph API, which our freshly obtained Access Token is not.
To obtain such an Access Token that allow Graph API access, they tell me to add some scopes (ex. User.Read), but doing this ends to generate v1.0 Access Token that we can not verify.

The conclusion of the call was and endless circle, they do not admit their implementation is wrong, and they ensure "following the recommendations of OIDC".

I've check the OIDC specification, and it says:

To obtain the requested Claims about the End-User, the Client makes a request to the UserInfo Endpoint using an Access Token obtained through OpenID Connect Authentication
https://openid.net/specs/openid-connect-core-1_0.html#UserInfo

AFAIK, the OIDC Auth process must deliver an Access Token that the client must be able to verify using keys provided by the authentication server, right?

If anyone with strong knowledge about OIDC spec can confirm or not, it could help to convince Microsoft to fix their implementation.

cc @bobvandevijver @tobias-93

EDIT: I've just realized that calling userinfo is not mandatory on OIDC auth flow. Is there any way to make it optional by config?

@bobvandevijver
Copy link
Member

I believe I tested EntraID a couple month back and confirmed it to be working with this bundle, which means that it also worked with the userinfo endpoint. I do not remember whether that was the graph. subdomain, so maybe they changed that in the meantime. As far as I know you should indeed be able to call the userinfo using the token returned during the first step, but convincing Microsoft they are doing something wrong is quite impossible.

Making the call to the userinfo can be made optional: just extend the OidcClient and make the method return an empty OidcUserData object. You will need to set the userIdentifierFromIdToken option for that to work. Or create a PR does adds an option that does the same 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants