I have set up two tenants, 123.onmicrosoft.com
and abc.onmicrosoft.com
.
I have developed a web application using OpenIdConnectAuthenticationMiddleware
to do the OpenID Connect authentication flow.
The out-of-the-box middleware requires the client ID to be passed during the configuration of the OWIN middleware, this is our challenge. Our web application is deployed once using two bindings: 123.mydomain.com
and abc.mydomain.com
.
We want to set the client ID at runtime. Our current approach was to develop a subclass and inject a function to gather the client ID at runtime, something like this:
public class LendingMiddleware : OpenIdConnectAuthenticationMiddleware
{
private Action<IOwinContext, OpenIdConnectAuthenticationOptions> _optionBuilder = null;
public LendingMiddleware(OwinMiddleware next, IAppBuilder app,
OpenIdConnectAuthenticationOptions options,
Action<IOwinContext, OpenIdConnectAuthenticationOptions> optionBuilder = null) : base(next, app, options)
{
_optionBuilder = optionBuilder;
}
public LendingMiddleware(OwinMiddleware next, IAppBuilder app, OpenIdConnectAuthenticationOptions options) : base(next, app, options)
{
}
public override async Task Invoke(IOwinContext context)
{
_optionBuilder?.Invoke(context, Options);
await base.Invoke(context);
}
}
On start up class we add two middleware: UseTenantVerification read the host header to load the application configuration associated to this tenant. LendingAuthentication use LendingMiddleware to do the openid flow, lending Middleware inherid from OpenIdConnectAuthenticationMiddleware
public void ConfigureAuth(IAppBuilder app)
{
//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.UseTenantVerification();
app.UseLendingAuthentication(CreateOptions(authority), (ctx, options) =>
{
TenantConfig tc = ctx.Environment["TenantInfo"] as TenantConfig;
options.ClientId = tc.ClientId;
options.TokenValidationParameters.ValidAudience = tc.ClientId;
//ctx.ProtocolMessage.Password = tenantConfig.Secret;
});
}
TenantConfig
looks like this:
public TenantConfig GetTenantConfig(string httpHost)
{
IDictionary<string, TenantConfig> Tenants = new Dictionary<string, TenantConfig>();
Tenants.Add(new KeyValuePair<string, TenantConfig>("123.local", new TenantConfig
{
ClientId = "90677fe0-412c-4625-8d45-f37c14ffb456",
Secret = "[secret!]",
HostName = "123.local",
ApiResources = new Dictionary<string, ApiResourceConfig>
{
{
"TodoWebApi", new ApiResourceConfig
{
ResourceIdentifier = "https://123.onmicrosoft.com/TodoWebApiMT",
ResourceAddress = "http://TodoWebApiMT.local"
}
}
}
}));
Tenants.Add(new KeyValuePair<string, TenantConfig>("abc.local", new TenantConfig
{
ClientId = "000309f7-ac34-4fb6-a833-ef7c664e0958",
Secret = "[secret!]",
HostName = "abc.local",
ApiResources = new Dictionary<string, ApiResourceConfig>
{
{
"TodoWebApi", new ApiResourceConfig
{
ResourceIdentifier = "https://abc.onmicrosoft.com/TodoWebApiMT",
ResourceAddress = "http://TodoWebApiMT.local"
}
}
}
}));
var k = Tenants.Keys.First(item => item.Equals(httpHost));
return Tenants[k];
}
We keep the client ID, app key and the API resources that this tenant would want to access. We have only one API app for now but eventually it will be a few API apps that would be added to the tenant directory to grant access.
Is there a better way to do this?