5

Current scenario: Web App and Web API are authenticated using AAD B2C and working fine. Each customer has a unique tenant. OpenIdConnectAuthenticationOptions (web app) and OAuthBearerAuthenticationOptions (api) are set at the application Startup.

As described here: Token based authentication for both Web App and Web API using Azure AD B2C

Unfortunately both Web app and API have to be deployed for each and every customer to keep them separated.

Requirement: Use same Web app and API for multiple customers rather than deploying them for every customer. Each customer will have a different URL for the web application.

enter image description here

Question 1 (Web App): How to redirect (for authentication) users to the correct tenant based on the request URL?

i.e. Set OpenIdConnectAuthenticationOptions (tenant, applicationId, signInPolicy etc.) from the database (or memory) on the fly based on the Request.Url rather than at application Startup.

Question 2 (API): How to validate the received token using the correct tenant?

i.e. Set OAuthBearerAuthenticationOptions configurations on the fly based on the received token clientId rather than at the application Startup

Kaf
  • 33,101
  • 7
  • 58
  • 78

2 Answers2

2

There are a couple of things within this that will not work based on what I think are assumptions.

  • Firstly, B2C has no concept of multiple "tenants". B2C is essentially a Directory within your tenant and cannot be separated further. You can have multiple B2C Directories within your tenant, say for each customer, but you cannot have multiple segregated customers within a single B2C Directory.

  • Secondly, if you did have your application connected to multiple B2C directories, you would need to manage then connectors, App ID's, keys etc for each one. It is then up to your application to work out which one to use based on some data (URL accessed etc.).

There are also a couple of questions I would ask around how are you on-boarding users ? Can they register themselves ? Do they have their own user stores ?

If you really want to keep user accounts separate for each organisation (and they don't have a SAML / OIDC identity provider already) then I would do the following:

In this way your application only needs to trust and maintain the single B2C directory, and if you do have customers who have their own OIDC or SAML endpoints they become a claims provider within the directory, rather than a separate B2C instance.

Glenn Prince
  • 96
  • 1
  • 4
  • You are correct, we are using B2C tenants as directories. Users are created in the customer B2C tenant using the Graph API. Each customer has a dedicated tenant (or directory). This setup is already in place. What we really need is to use a one single application and divert user request to the correct tenant based on the URL. – Kaf Feb 01 '18 at 17:35
  • I am trying to use `IAppBuilder.MapWhen()` approach to redirect users based on the URL at the moment. I haven't been able to fully test it yet. Your link is useful and it's a different approach. – Kaf Feb 01 '18 at 20:21
  • @Kaf I also had the same requirement, did you able to solve the issue? Please help me how you did it if yes :-) – Jagadesh May 13 '19 at 11:20
1

Per @ManishJoisar's request this is how I resolved my issue long ago. This is an old .net framework 4.8 code sample.

Please note that B2C v2 policies provide better implementation with federation through custom policies these days.

public void ConfigureAuth(IAppBuilder app)
    {

        int index = 2000;
        foreach (var record in SystemConfig.ApiToWebUrl)
        {
            app.MapWhen(
                    context => System.Web.HttpContext.Current.Request.Url.BaseURL() == record.Key,
                    config =>
                    {
                        var customer = SystemConfig.GetWebSettings(true)[record.Value];
                        config.UseOAuthBearerAuthentication(CreateBearerOptionsFromPolicy(index, customer.B2CSignInPolicyId,
                                                customer.B2CAadInstance, customer.B2CTenant, customer.B2CApplicationId));
                    }
                );

            index++;
        }
    }

    public OAuthBearerAuthenticationOptions CreateBearerOptionsFromPolicy(int index, string b2cPlicyName,
                    string b2cInstance, string b2cTenant, string b2cClientId)
    {
        TokenValidationParameters tvps = new TokenValidationParameters
        {
            // This is where you specify that your API only accepts tokens from its own clients
            ValidAudience = b2cClientId,
            AuthenticationType = string.Format("{0}_{1}", b2cPlicyName, index)
        };

        var aadInstance = string.Format("https://{0}{1}", b2cTenant.Split('.')[0], ".b2clogin.com/{0}/{1}/v2.0/.well-known/openid-configuration");

        var config = String.Format(aadInstance, b2cTenant, b2cPlicyName);

        return new OAuthBearerAuthenticationOptions
        {
            // This SecurityTokenProvider fetches the Azure AD B2C metadata & signing keys from the OpenIDConnect metadata endpoint
            AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider(config)),
            Provider = new CustomOAuthBearerProvider()
        };
    }
Kaf
  • 33,101
  • 7
  • 58
  • 78