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

feat: add support for azure ad v2 multitenant apps #148

Closed
wants to merge 1 commit into from
Closed

feat: add support for azure ad v2 multitenant apps #148

wants to merge 1 commit into from

Conversation

ulrikstrid
Copy link

@ulrikstrid ulrikstrid commented Feb 11, 2019

This PR adds support for Azure AD v2 multitenant apps. Flags are documented in README.

@codecov
Copy link

codecov bot commented Feb 11, 2019

Codecov Report

Merging #148 into master will not change coverage.
The diff coverage is 100%.

@@          Coverage Diff          @@
##           master   #148   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          18     18           
  Lines         798    803    +5     
=====================================
+ Hits          798    803    +5

@panva
Copy link
Owner

panva commented Feb 11, 2019

Hello @ulrikstrid

I'm really reluctant to be adding vendor specific workarounds to a library that aims to be interoperable between certified solutions, which Azure AD is.

That being said let's give it a chance, if you wouldn't mind, fixing the test syntax not to use trailing commas (eslint should pick up on this for you) and writing down an understandable reasoning behind this change. What use case is trying to solve and why is it needed, with code examples please. I will then ping the responsible person from Microsoft for consult.

Thank you for putting your effort into this and bearing with the above.

@ulrikstrid
Copy link
Author

I tried to fix the linting issues but might take another go at it as it seems I didn't get it to work.

Our use case is that we want to create a multi tenant application where other companies can login with their own Azure AD to our application. This is, as stated in #84, currently not possible because Microsoft does some non-standard things in their discovery document. They return issuer URL that should have {tentantid} substituted for what comes back from when trying to auth.

The returned issuer is always https://login.microsoftonline.com/{tenantid}/v2.0". The tenantId is put in the tid key of the payload that is returned and the url is changed to use that to validate it's the same. As I understand it IdentityServer just disables the issuer check but that seems to be even more unsafe than what we did in this PR.

This PR allows to use a array of valid tenantId's, if that list is omited we just match the tid with the tenantId in the iss to make sure they at least are the same.

Our usage code is this:

  const adIssuer = await Issuer.discover(
    `https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration`
  );

  const adClient = new adIssuer.Client({
    redirect_uri: REDIRECT_URI,
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    token_endpoint_auth_method: "client_secret_basic",
    isAzureADv2Multitenant: true,
    azureADv2ValidTenantIds: ["74d3d36e-789f-4168-99dd-4a6052d32573"] // GUID changed from our real GUID
  });

Anything else you want me to clarify?

@ulrikstrid
Copy link
Author

The test is failing because node 6.x.x doesn't have async/await. I'll see if I can change the test to just use catch instead.

@panva
Copy link
Owner

panva commented Feb 12, 2019

@ulrikstrid before i reach out to Mike, can't you do discovery on a route which is specific to your tenant id?

I seem to recall https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration does have a tenant specific result when you replace common with the tenant id. (i could validate that if you just didn't hide your real tenant id)

@ulrikstrid
Copy link
Author

When I change common to my tenant id I get back a issuer with my tenant id in it, but then I need to have separate routes for each of our customers and I would really like to avoid that as that is a lot more work than adding a new customer to the database.

@panva
Copy link
Owner

panva commented Feb 12, 2019

I seem to recall login.microsoftonline.com/common/v2.0/.well-known/openid-configuration does have a tenant specific result when you replace common with the tenant id.

that is indeed the case

When I change common to my tenant id I get back a issuer with my tenant id in it, but then I need to have separate routes for each of our customers and I would really like to avoid that as that is a lot more work than adding a new customer to the database.

Since the PR suggests you know the tenantIds upfront i think you can get without it. At authorization request time, do you know the tid already? Is that something you have configured in your customer db?

@ulrikstrid
Copy link
Author

I don't know the tid that the current user will have until they come back.

@panva
Copy link
Owner

panva commented Feb 12, 2019

So are you blindly accepting users from tenant ids not belonging to your current customer?

@ulrikstrid
Copy link
Author

Anyone can get to the login, but with the changes in this PR I can match them to the list of tenant ids I provide in the configuration azureADv2ValidTenantIds. So anyone can try to login, but if they come back with the wrong tenant id they will not come in.

@panva
Copy link
Owner

panva commented Feb 12, 2019

Does a customer of yours provide his tenant id?

@ulrikstrid
Copy link
Author

We will know tenant id's ahead of time as we need to register them as customers in the application. But we don't know them for each connection.
That's why we can provide the list of valid tenant ids

@simongottschlag
Copy link

The main goal of this is to have one "Azure AD Application" that is configured to be multi tenant. This means we can connect an application using node-openid-client to it and use Azure AD for authentication of all users (from any tenant) with this.

For some usecases, this is something that you would like to do (to use Azure AD as an OP just like you use GitHub and Google).

In our case, we don't want to allow access to all Azure AD tenants, but specific ones. On our side, we will store the customers with an Azure AD tenant ID - which we will pick up for all customers and generate the array to use with azureADv2ValidTenantIds. We will also do matching that a user that is a member of a customer also is coming in from the correct tenant id - but that's nothing to do with OIDC.

@ssypi
Copy link

ssypi commented Feb 13, 2019

I think its pointless to compare tid and iss and it only makes this azure-specific and unnecessary complex. If someone wants to do that, they can do that afterwards in their own code. What I propose is to have something like "ignoreIssuerValidation" or "validateIssuer: false" flags instead for ignoring it. Additionally, you could have something like "validIssuers" array for having multiple issuers instead of ignoring it completely.

This way it is not tied to Azure AD and can be used in other cases while still allowing multiple Azure tenants to be supplied to the validIssuers array. They just have to be in full iss format instead of just the tenant-id.

This kind of approach is what Microsoft also suggests in their documentation for multi-tenanted applications.

https://docs.microsoft.com/fi-fi/azure/active-directory/develop/howto-convert-app-to-be-multi-tenant#update-your-code-to-handle-multiple-issuer-values

For example, if a multi-tenant application only allows sign-in from specific tenants who have signed up for their service, then it must check either the issuer value or the tid claim value in the token to make sure that tenant is in their list of subscribers. If a multi-tenant application only deals with individuals and doesn’t make any access decisions based on tenants, then it can ignore the issuer value altogether.
In the multi-tenant samples, issuer validation is disabled to enable any Azure AD tenant to sign in.

@panva
Copy link
Owner

panva commented Feb 13, 2019

The solution i'm leaning towards is to refactor the ID Token validation so that it allows for some prototype overloading on specific parts. That's one thing.

The other is i'll probably expose (as part of this library or a new one, not sure yet) another issuer/client constructor just for AAD.

@panva
Copy link
Owner

panva commented Mar 5, 2019

Just checking in, i haven't forgotten about this, I'm just now planning to revive the work on 3.x (due in April with node 12 release) and i'll include this there.

@acoll
Copy link

acoll commented Mar 28, 2019

I had to add support for MS login across multiple tenants today and just ended up forking this project and adding my own little dirty patch to define custom iss validation. Let me know if I can help on testing or implementing proper support.

@panva
Copy link
Owner

panva commented Apr 15, 2019

Coming back to this as I progress on v3.x

There are a few points of feedback i need confirmed from you.

  1. Based on discussions with MS's Standards Architect I will only allow the special AAD issuer validation handling if the Issuer was discovered through Issuer.discover(...) where the resolved AS metadata URL is equal to (see below line [1]). Issuer instances created through the regular constructor will not get this applied. I was told that "so long as the client uses the discovered metadata the issuer validation can be omitted" hence this restriction.
  2. Clients created through this Issuer will have an altered iss ID Token validation handling such as (see below line [2])
  3. I will not implement this custom azureADv2ValidTenantIds handling since you can implement this on your application's level after getting the tokenset from authorizationCallback

[1]: https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
[2]:

const azureADv2Issuer = this.issuer.issuer.replace('{tenantid}', payload.tid);
assert.equal(payload.iss, azureADv2Issuer, 'unexpected iss value');

Thoughts?

@panva panva closed this in 24486dd Apr 29, 2019
@github-actions github-actions bot locked and limited conversation to collaborators Apr 4, 2020
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants