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