Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setup OWIN dynamically (by domain)

We are trying to setup a white label on our project that uses OWIN(includes FB, google, Live logins). Is there a way to setup their API credential dynamically, say if they changes the domain the settings will change.

I think owin loads earlier than MVC? Is there a way that we can load it on Global.asax(Request)?

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
    }
}

UPDATE:

In other words a single Application will host many domains and sub domains(white labeling).

like image 558
Lee Avatar asked Jun 26 '14 05:06

Lee


3 Answers

I have been googling on the same things today. Then I found a nice OWIN sample at http://aspnet.codeplex.com/SourceControl/latest#Samples/Katana/BranchingPipelines/BranchingPipelines.sln that explained the branching capabilities of OWIN. If I understand this sample correctly, you should be able to configure different OWIN stacks depending on request parameters such as host header, cookie, path or whatever using the app.Map() or app.MapWhen() methods.

Let's say you have 2 different DNS domains representing 2 customers with different login configs, you can initialize OWIN to use different configs depending on the value of the host header:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {

        app.MapWhen(ctx => ctx.Request.Headers.Get("Host").Equals("customer1.cloudservice.net"), app2 =>
        {
            app2.UseWsFederationAuthentication(...);
        });
        app.MapWhen(ctx => ctx.Request.Headers.Get("Host").Equals("customer2.cloudservice.net"), app2 =>
        {
            app2.UseGoogleAuthentication(...);
        });
    }
}
like image 56
David Fahlander Avatar answered Sep 30 '22 23:09

David Fahlander


I just went through an exercise in trying to do exactly this. Unfortunately, There is no way to directly inject middleware into a Katana-based host after startup. The reason for this is because in order for middleware to actually be used, it has to be composed together into the application delegate. Katana's implementation does this by calling IAppBuilder.Build(typeof(AppFunc)), where AppFunc is a using alias of the specified type for the application delegate: Func<IDictionary<string,object>, Task> when initialization is complete. Here's the key line at the bottom of .Initialize:

AppFunc = (AppFunc)builder.Build(typeof(AppFunc));

The only opportunity you have to configure middleware is before this, during the configuration step in the startup class you write or by web.config.

Just to be very clear about what won't work, I was experimenting with something like this:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        HomeController.Initialized += () => ConfigureGoogle(app);
    }

    private void ConfigureGoogle(IAppBuilder app)
    {
        app.UseGoogleAuthentication(/* stuff */);
    }
}

public class HomeController : Controller
{
    public event EventHandler Initialized;

    [Route("/setup/submit"), AcceptVerbs(HttpVerbs.Post)]
    public ActionResult SetupSubmit()
    {
        /* ... */

        Initialized();
    }
}

This doesn't throw exceptions, and there are no obvious signs of errors - but it doesn't work because the application delegate has already been composed by this point. Katana doesn't give you any APIs for re-building the application delegate (and I'm not sure that would be a good idea anyway - there would be countless bugs that could be created by such a mechanism; for example, how should the server handle an in-flight request when the application delegate is recomposed after initialization?).

What's your alternative? @DavidFahlander's approach is going to be the right one, but you still need to be careful if you want to achieve dynamism. Look at what .MapWhen does:

// put middleware in pipeline before creating branch
var options = new MapWhenOptions { Predicate = predicate };
IAppBuilder result = app.Use<MapWhenMiddleware>(options);

// create branch and assign to options
IAppBuilder branch = app.New();
configuration(branch);
options.Branch = (AppFunc)branch.Build(typeof(AppFunc));

First, notice that this calls app.Use with a MapWhenMiddleware type. This means you are facing the same limitations as before - it all has to be done up front. The branched middleware also will be baked in before initialization is complete: see the last line: branch.Build.

Your only hope of dynamism here is to use the predicates in a way that achieves your goal. This doesn't get you 100% of the way there, but it gets pretty darn close:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {

        app.MapWhen(ctx => ClientHasWsFederationConfigured() && ctx.Request.Headers.Get("Host").Equals("customer1.cloudservice.net"), app2 =>
        {
            app2.UseWsFederationAuthentication(...);
        });
        app.MapWhen(ctx => ClientHasGoogleAuthConfigured() && ctx.Request.Headers.Get("Host").Equals("customer2.cloudservice.net"), app2 =>
        {
            app2.UseGoogleAuthentication(...);
        });
    }
}

The limitations here will be these:

  • You have to configure all your supported authentication types up-front. you can't add a new one while the app is running.
  • ClientHasXXXConfigured will be run on every request. This may or may not be acceptable on a performance basis, depending on what you do.

Given the information you put in the question, I think these tradeoffs are probably ok as long as you're careful about what ClientHasXXXConfigured (or it's equivalent) does.

like image 27
Ben Collins Avatar answered Oct 01 '22 00:10

Ben Collins


I am working on a Microsoft.Owin.Security design change that will allow for multi-tenancy in AuthenticationOptions (e.g. GoogleAuthenticationOptions) to support the ability for a developer to inject their owin implementations.

Here is my proposal to the Katana project team: https://katanaproject.codeplex.com/discussions/561673

I also have a working implementation that sits on top of the existing Microsoft.Owin.Security infrastructure and does not benefit from my design change suggestion. It requires rolling your own version of the Auth Middleware (copy paste existing) but it's a viable workaround until I can get my design change implemented in Microsoft.

https://github.com/kingdango/Owin.OAuth.Multitenant (This is rough, I just got it up this morning)

Ultimately I don't think it makes sense to develop multiple Owin pipelines for each tenant just to support multi-tenancy. The right solution is to have middleware that is extensible and that's what I'm proposing.

like image 40
kingdango Avatar answered Sep 30 '22 23:09

kingdango