Multi-tenant in OpenIDConnect .Net Framework

Viewed 1761

I was trying to implement Multitenant Authentication (Which I'm learning) and so far I've succeeded in implementing authentication my application in Single tenant.

The code which I've used for single tenant is

 public void ConfigureAuth(IAppBuilder app)
        {

            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseOpenIdConnectAuthentication(
                 new OpenIdConnectAuthenticationOptions
                 {
                     ClientId = ConfigurationManager.AppSettings["AuthclientId"],
                     Authority = "https://login.microsoftonline.com/abc.onmicrosoft.com/",



                 });
        }

Here; first I register my application in ABC AAD and get the client ID then put it my configuration. All works fine.

But now I've to implement this with multitenant type. Even though it's multitenant, I've to allow only 2 tenant users only. Let's say abc.onmicrosoft.com and contoso.onmicrosoft.com

So far I've done like registering my application in ABC tenant and Contoso tenant then get 2 client Id's. But my issue there is no way to provide 2 client ID's in the UseOpenIdConnectAuthentication (see my updated code below)

public void ConfigureAuth(IAppBuilder app)
        {

            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseOpenIdConnectAuthentication(
                 new OpenIdConnectAuthenticationOptions
                 {

                     ClientId = ??,
                     Authority = "https://login.microsoftonline.com/common/",
                     TokenValidationParameters = new TokenValidationParameters
                     {
                         ValidateIssuer = false
                     },

                 });
        }

P.S This is new to me. I may be wrong, please correct me in order to get things in the right path

Update 1:

app.UseOpenIdConnectAuthentication(
             new OpenIdConnectAuthenticationOptions
             {

                 //ClientId = authClientID1,//App ID registered with 1st Tenant
                 Authority = "https://login.microsoftonline.com/common/",
                 RedirectUri= "https://localhost:44376/",
                 TokenValidationParameters = new TokenValidationParameters
                 {
                     ValidAudiences = new List<string>{ authClientID1, authClientID2 },
                     ValidateIssuer =true,
                     ValidIssuers= new[] { "https://sts.windows.net/<tenantID1>/", "https://sts.windows.net/<tenantID2>/" }
                 },

             });

After commenting the ClientID I'm receiving the error like AADSTS900144: The request body must contain the following parameter: 'client_id'

I'm not sure how to give my two ClientID and tenant ID in order to authenticate users only from my two tenants!

2 Answers

Your client id should be your app client id. You don't create another app in the other tenant. Setting the authority to common is enough. Issuer validation can be disabled if you want to allow any tenant.

Then when someone from another tenant logs in to your app, they'll be asked to consent to the permissions you require. Once they do, a service principal representing your app is created in their tenant automatically. It has the same client id.

To deliver a multi tenant application, you only create one application in AAD. Therefore you also only have one client_id. Make sure your app has "Multi-Tenanted" enabled.

You find much information here: https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-convert-app-to-be-multi-tenant

There is also a full sample available: https://github.com/Azure-Samples/active-directory-dotnet-webapp-multitenant-openidconnect

   public void ConfigureAuth(IAppBuilder app)
    {         
        string ClientId = ConfigurationManager.AppSettings["ida:ClientID"];
        //fixed address for multitenant apps in the public cloud
        string Authority = "https://login.microsoftonline.com/common/";

        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions { });

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                ClientId = ClientId,
                Authority = Authority,
                TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
                {
                    // instead of using the default validation (validating against a single issuer value, as we do in line of business apps), 
                    // we inject our own multitenant validation logic
                    ValidateIssuer = false,
                },
                Notifications = new OpenIdConnectAuthenticationNotifications()
                {
                    RedirectToIdentityProvider = (context) =>
                    {
                        // This ensures that the address used for sign in and sign out is picked up dynamically from the request
                        // this allows you to deploy your app (to Azure Web Sites, for example)without having to change settings
                        // Remember that the base URL of the address used here must be provisioned in Azure AD beforehand.
                        string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase;                         
                        context.ProtocolMessage.RedirectUri = appBaseUrl;
                        context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
                        return Task.FromResult(0);
                    },
                    // we use this notification for injecting our custom logic
                    SecurityTokenValidated = (context) =>
                    {
                        // retriever caller data from the incoming principal
                        string issuer = context.AuthenticationTicket.Identity.FindFirst("iss").Value;
                        string UPN = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.Name).Value;
                        string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;

                        if (
                            // the caller comes from an admin-consented, recorded issuer
                            (db.Tenants.FirstOrDefault(a => ((a.IssValue == issuer) && (a.AdminConsented))) == null)
                            // the caller is recorded in the db of users who went through the individual onboardoing
                            && (db.Users.FirstOrDefault(b =>((b.UPN == UPN) && (b.TenantID == tenantID))) == null)
                            )
                            // the caller was neither from a trusted issuer or a registered user - throw to block the authentication flow
                            throw new SecurityTokenValidationException();                            
                        return Task.FromResult(0);
                    },
                    AuthenticationFailed = (context) =>
                    {
                        context.OwinContext.Response.Redirect("/Home/Error?message=" + context.Exception.Message);
                        context.HandleResponse(); // Suppress the exception
                        return Task.FromResult(0);
                    }
                }
            });

    }
Related