uaa icon indicating copy to clipboard operation
uaa copied to clipboard

Supporting Microsoft OIDC

Open AP-Hunt opened this issue 6 years ago • 9 comments

The issue

What version of UAA are you running?

4.30.0

How are you deploying the UAA?

I am deploying the UAA

  • using cf-deployment

What did you do?

We configured an OIDC identity provider for Microsoft using this discovery document, in order to allow logins from any Microsoft product (i.e. not just a specific Azure AD)

What did you expect to see? What goal are you trying to achieve with the UAA?

We're in the process of enabling Microsoft single sign-on for tenants, whose accounts may be in any directory. We expected that, given the global discovery document, the login flow would just work.

What did you see instead?

We found that Microsoft issues tokens whose iss claim is different from the issuer given in the discovery document. This caused UAA to believe the token was invalid.

After some investigation, we found that the iss claim includes the GUID of the organisation to which the user belongs in Microsoft land (as is evident from the discovery doc: https://login.microsoftonline.com/{tenantid}/v2.0). For some users (like Outlook or Hotmail users), the organisation GUID is well known. However, for corporate users, it will be their Azure/Office 365 tenant id.

For us, it is impossible to set an issuer URL that UAA would accept for all users.

The suggestion


@46bit and I have put together a small change in our paas-uaa fork which introduces a new configuration flag for OIDC providers: issuerValidationMode. The flag has three possible values: strict (the default), domain_only, and none.

The aim our of change is to provide a less strict form of issuer claim validation, in order to support scenarios like that posed by Microsoft SSO. In domain_only mode, the only the host portion of the token’s iss claim must match that of the configured URL. This is done as a string comparison, so subdomains are not considered to match a parent domain. E.g. sub.domain.org is not a match for domain.org, or vice versa 
 We’ve deployed this change in to our CloudFoundry deployment, and verified that we can do a successful Microsoft SSO login.

Why an issue and not a PR


We’re raising this as an issue and not a PR because we’re not sure if this necessarily the right approach to take, and we’d like some feedback on the problem and our proposed solution.

AP-Hunt avatar Jul 09 '19 16:07 AP-Hunt

We have created an issue in Pivotal Tracker to manage this:

https://www.pivotaltracker.com/story/show/167172681

The labels on this github issue will be updated when the story is started.

cf-gitbot avatar Jul 09 '19 16:07 cf-gitbot

@AP-Hunt - I'm surprised to hear that Microsoft would issue tokens with different issuer strings if you're initiating the flow by sending the user to the /authorize endpoint specified in the common configuration.

i.e. https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration lists https://login.microsoftonline.com/common/oauth2/v2.0/authorize as the endpoint to initiate the OIDC flow.

Their docs state that:

Validating an id_token is similar to the first step of validating an access token - your client should validate that the correct issuer has sent back the token and that it hasn't been tampered with.

(https://docs.microsoft.com/en-us/azure/active-directory/develop/id-tokens#validating-an-id_token)

which links to https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens#validating-tokens which states:

To validate an id_token or an access_token, your app should validate both the token's signature and the claims. To validate access tokens, your app should also validate the issuer, the audience, and the signing tokens. These need to be validated against the values in the OpenID discovery document. For example, the tenant-independent version of the document is located at https://login.microsoftonline.com/common/.well-known/openid-configuration

Interestingly enough, that's yet again a slightly different URL to their open-id configuration file. Perhaps give that a go and see if it renders different results?

If Microsoft are returning ID tokens that don't validate against the spec, or their own documentation, then I think the first port of call should be a bug filed with Microsoft. Either their product is not working correctly, or their documentation is not correct.

ie the OpenID Connect specification is crystal clear on this:

The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery) MUST exactly match the value of the iss (issuer) Claim.

(https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation)

As I think I mentioned in my other comment, a few months back when we were integrating against a single AD tenant, we found that the issuer string would heavily depend on precisely which /authorize endpoint we started with for a user, it was a bit finicky finding the right one.

aeijdenberg avatar Jul 30 '19 21:07 aeijdenberg

Notwithstanding the above, I just realised precisely what you mean - it's not just the docs that says {tenantid} in the URL, they actually bake that into the Issuer URL returned as: https://login.microsoftonline.com/{tenantid}/v2.0.

Sigh - that makes things more difficult. I wonder if the reason for doing is that unlike Google's Identity Platform, where all users (when identified by email) draw from a global namespace, whereas in Microsoft's Identity Platform, can users with the same email address for example existing in multiple tenancies?

i.e. perhaps one needs the combination of tenancy and username/email in order to uniquely identify a user? Else I could spin up a new Azure AD tenancy, create a user with name "[email protected]" and then use your sign-on (which ignores the tenancy) to impersonate you on your UAA?

aeijdenberg avatar Jul 30 '19 21:07 aeijdenberg

Thanks for getting back to us, @aeijdenberg. I agree this isn't a very enjoyable problem to have.

I wonder if the reason for doing is that unlike Google's Identity Platform, where all users (when identified by email) draw from a global namespace, whereas in Microsoft's Identity Platform, can users with the same email address for example existing in multiple tenancies?

We haven't checked whether duplicate usernames can existing in different Azure ADs, but it wouldn't surprise me.

A big surprise for us is that usernames are not verified email addresses, even though they are formatted as email addresses. You have to validate a domain name to use it in a username, but the username itself is just a text field.

The only email verification is completely separate, to allow for password resets.

i.e. perhaps one needs the combination of tenancy and username/email in order to uniquely identify a user? Else I could spin up a new Azure AD tenancy, create a user with name "[email protected]" and then use your sign-on (which ignores the tenancy) to impersonate you on your UAA?

Absolutely, yes. For Google it is wise but seemingly not security-critical to use their sub field as the username instead of just the email address. But for Microsoft it seems security-critical to use their oid field as the username, which uniquely identifies an exact user.

46bit avatar Jul 30 '19 21:07 46bit

@46bit - don't mention email_verified to me - when users are created in UAA, email_verified is set to true by default, even if the so-called email address isn't actually one at all (https://docs.cloudfoundry.org/api/uaa/version/73.7.0/index.html#create-4).

And combine that with UAA happily having multiple users with the same username/email address, but existing under different origins (where I recently learned one needs both to uniquely identify a user), it can be real fun with the many applications out there that assume, incorrectly, that email_verified might have some semblance of security significance.

aeijdenberg avatar Jul 30 '19 22:07 aeijdenberg

And combine that with UAA happily having multiple users with the same username/email address, but existing under different origins (where I recently learned one needs both to uniquely identify a user), it can be real fun with the many applications out there that assume, incorrectly, that email_verified might have some semblance of security significance.

I'd intuited that usernames/emails could de duplicated with different origins. I found it the least surprising behaviour. But we have found that UAA can be surprising.

46bit avatar Jul 30 '19 22:07 46bit

Notwithstanding the above, I just realised precisely what you mean - it's not just the docs that says {tenantid} in the URL, they actually bake that into the Issuer URL returned as: https://login.microsoftonline.com/{tenantid}/v2.0.

Yep, we were surprised by that too. As you've now seen, UAA behaves properly when you can know the tenant id to which your users should belong. However, if like us you're opening it up to a wider audience, then Microsoft's implementation causes problems.

It's probably worth saying that our change is now in production, and working OK with both Google and Microsoft providers configured.

AP-Hunt avatar Jul 31 '19 08:07 AP-Hunt

@AP-Hunt - one of the tricky things to get right with auth code like this is that while it's easy to verify that a security property change like this is "working" by allowing your intended users to login - it can be far more difficult to reason whether the same change might in fact be working a bit too well and allowing unintended consequences like other unauthorized users being able now also login and impersonate your real users.

Again, I'm not sure if Microsoft allows for same usernames in different tenancies - and if they do, then ignoring the tenancy from which issued the token may be quite perilous.

Feels like you would nearly want to map a tenancy in Azure to an origin in UAA. And if you're going to do that, then you may as well point each origin directly to the tenant specific OIDC config.

aeijdenberg avatar Jul 31 '19 21:07 aeijdenberg

@aeijdenberg While I appreciate your concern I wouldn't underestimate how much investigation we did to get to this issue being filed and our modification running. We use Microsoft's oid claim, described by https://docs.microsoft.com/en-us/azure/active-directory/develop/id-tokens as:

oid: The immutable identifier for an object in the Microsoft identity system, in this case, a user account. This ID uniquely identifies the user across applications - two different applications signing in the same user will receive the same value in the oid claim. The Microsoft Graph will return this ID as the id property for a given user account. Because the oid allows multiple apps to correlate users, the profile scope is required to receive this claim. Note that if a single user exists in multiple tenants, the user will contain a different object ID in each tenant - they're considered different accounts, even though the user logs into each account with the same credentials.

46bit avatar Jul 31 '19 21:07 46bit