public class Startup
{
public void Configuration(IAppBuilder app)
{
//...
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "clientid",
Authority = 'authority",
RedirectUri = "http://mysite.com/"
});
}
}
Hopefully by blogging and linking to Ben's article it will be easier for others to find it.
Example Usage
public class Startup
{
public void Configuration(IAppBuilder app)
{
//..
app.UsePerTenant((tenantContext, appBranch) =>
{
appBranch.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
appBranch.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = $"OAuthCookie.{tenantContext.FriendlyName}"
});
appBranch.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = tenantContext.AuthClientId,
Authority = tenantContext.AuthAuthority,
RedirectUri = $"http://localhost:2295/{tenantContext.FriendlyName}/"
});
});
}
}
ASP.NET Implementation
TenantPipelineMiddleware.cs
/// <summary>
/// Workaround for the known OpenID multitenancy issue https://github.com/aspnet/Security/issues/1179
/// based on http://benfoster.io/blog/aspnet-core-multi-tenant-middleware-pipelines and https://weblogs.asp.net/imranbaloch/conditional-middleware-in-aspnet-core designs
/// </summary>
public class TenantPipelineMiddleware : OwinMiddleware
{
readonly IAppBuilder rootApp;
readonly Action<TenantContext, IAppBuilder> newBranchAppConfig;
readonly ConcurrentDictionary<TenantContext, Lazy<Func<IDictionary<string, object>, Task>>> branches;
public TenantPipelineMiddleware(OwinMiddleware next, IAppBuilder rootApp, Action<TenantContext, IAppBuilder> newBranchAppConfig)
: base(next)
{
this.rootApp = rootApp;
this.newBranchAppConfig = newBranchAppConfig;
this.branches = new ConcurrentDictionary<TenantContext, Lazy<Func<IDictionary<string, object>, Task>>>();
}
public override async Task Invoke(IOwinContext context)
{
TenantContext tenantContext = context.GetTenantContext();
if (tenantContext == null || tenantContext.IsEmpty())
{
await this.Next.Invoke(context);
return;
}
Lazy<Func<IDictionary<string, object>, Task>> branch =
branches.GetOrAdd(tenantContext, new Lazy<Func<IDictionary<string, object>, Task>>(() =>
{
IAppBuilder newAppBuilderBranch = rootApp.New();
newBranchAppConfig(tenantContext, newAppBuilderBranch);
newAppBuilderBranch.Run((oc) => this.Next.Invoke(oc));
return newAppBuilderBranch.Build();
}));
await branch.Value(context.Environment);
}
}
OwinContextExtensions.cs
public static class OwinContextExtensions
{
static string tenantContextKey = "tenantcontext";
public static TenantContext GetTenantContext(this IOwinContext context)
{
if (context.Environment.ContainsKey(tenantContextKey))
{
return (TenantContext)context.Environment[tenantContextKey];
}
return null;
}
}
AppBuilderExtensions.cs
public static class AppBuilderExtensions
{
public static IAppBuilder UsePerTenant(this IAppBuilder app, Action<TenantContext, IAppBuilder> newBranchAppConfig)
{
return app.Use<TenantPipelineMiddleware>(app, newBranchAppConfig);
}
}
*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment.

No comments:
Post a Comment