Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
254 views
in Technique[技术] by (71.8m points)

owin - ASP.Net Identity Login Redirect Enforce Protocol (Https)

Hopefully I'm just missing something really simple/obvious - why, and more importantly, how do you maintain (or force) the protocol during the redirect to Login?

To illustrate:

request trace

  • the original protocol is https
  • one would think this should be the "default" for something like login, but as shown, the redirect (seems) doesn't maintain it.

Stuff I tried:

  • There is a RequireHttps attribute that one could use, but:

    1. seems "weird" that it would take 2 redirects to get "there"
    2. in situations where you have a load balancer and/or have SSL "offloaded" elsewhere (not in server), then this would then be a redirect loop (SSL is between client and front-end net/ssl lb, and http to your box(es)/application). This is actually my production case...
  • I have already set IIS URL re-write as well (aka canonical rule to https for entire site), and that seems "ignored" (too) (rule does not check for "https" otherwise it suffers same redirect loop).

  • tried and failed to set absolute URL in LoginPath (in CookieAuthenticationOptions)..because you can't do that...

Thanks for advice or pointers...


Update

As to the "why"?

  1. in situations where you have a load balancer and/or have SSL "offloaded" elsewhere (not in server), then this would then be a redirect loop (SSL is between client and front-end net/ssl lb, and http to your box(es)/application). This is actually my production case..

Further tinkering got me to the above, as shown in this (localhost - my local dev box, not server) request sequence (the above issue manifests in a production load balanced environment where SSL processing is "up the stack" - e.g. ARR):

localhost https

  • the protocol is in fact maintained
  • the issue seems exactly related to the situation where the application and the "infrastructure" don't "match". It seems similar to the situation where in code, you would do a Request.IsSecureConnection in a "load balanced"/"web farm" environment (say ARR where the cert is in your ARR, not in your host/s). That check will always return false in such a situation..

So the question really is on guidance on how to get around this?


Update 2

Many thanks to Richard for changing my "direction" in trying to resolve this. I originally was looking for a way to:

  • set/tell OWIN/Identity to use a secure URL (explicitly) and "override" the way it evaluates LoginPath. The Secure (only) option in handling cookies somehow led me that way (if I can explicitly say cookies in HTTPS only, then it sort of gave me an impression of being able to do so for LoginPath..one way or the other)

  • a "hacky" way in my mind was to just deal with it client side (Javascript).

In the end, Richard's answer took me to URL Rewriting (though still not on the LB side because that's beyond my control). I'm currently working off of (based on my environment):

<rule name="Redirect to HTTPS" stopProcessing="true">
    <match url=".*" />

    <conditions>
      <add input="{HTTP_CLUSTER_HTTPS}" pattern="^on$" negate="true" />
      <add input="{HTTP_CLUSTER_HTTPS}" pattern=".+" negate="true" />

    </conditions>
    <action type="Redirect" url="https://{HTTP_HOST}{SCRIPT_NAME}/{REQUEST_URI}" redirectType="SeeOther" />
</rule>

and see some light at the end of the tunnel.


Update 3

Awesome thanks again to Richard for the sleuthing! Latest answer got me sleuthing too and it turns out there's quite a few posts here on SO related to CookieApplyRedirectContext...so now this what I have in place (which is specific to my case), and is what I was originally going after:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
   AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
   LoginPath = new PathString("/Account/Login"),

   //This is why. If I could explicitly set this, then I (thought) I should
   //be able to explicitly enforce https (too..as a setting)
   //for the LoginPath...
   CookieSecure = CookieSecureOption.Always,

   Provider = new CookieAuthenticationProvider 
   {
      OnValidateIdentity = .....
      ,
      OnApplyRedirect = context =>
      {
         Uri absoluteUri;
          if (Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out absoluteUri))
          {
             var path = PathString.FromUriComponent(absoluteUri);
             if (path == context.OwinContext.Request.PathBase + context.Options.LoginPath)
             {
                context.RedirectUri = context.RedirectUri.Replace("http:", "https:");
             }
           }
          context.Response.Redirect(context.RedirectUri);
        }
     }
});
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

This problem is occurring because your application is issuing a redirect to an absolute URL. You can fix this in one of two ways, in the load balancer or in the application itself.

Load Balancer

Configure your load balancer to rewrite redirect responses from http to https. If you were using ARR, the following rule (taken from here) should work:

<rule name="forum-redirect" preCondition="IsRedirection" enabled="true">
  <match serverVariable="RESPONSE_LOCATION" pattern="^http://[^/]+/(.*)" />
  <conditions>
    <add input="{ORIGINAL_HOST}" pattern=".+" />
  </conditions>
  <action type="Rewrite" value="http://{ORIGINAL_HOST}/{R:1}" />
</rule>

Other load balancers will require similar configuration.

Application

We can replace the URL that OWIN redirects to in the authorization process with a relative URL, which means the protocol will stay as whatever the browser was previously using.

It took a bit of digging in the Owin source to find how to do this, but the following change to your Application startup should solve your problems. First, extract the CookieAuthenticationProvider initialisation from your startup config.

Change:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login"),
    Provider = new CookieAuthenticationProvider 
    {
        // Move these options in the step below...
    }
});

To:

var cookieProvider = new CookieAuthenticationProvider
{ 
    // ... Options from your existing application
};
// Modify redirect behaviour to convert login URL to relative
var applyRedirect = cookieProvider.OnApplyRedirect;
cookieProvider.OnApplyRedirect = context =>
{
    if (context.RedirectUri.StartsWith("http://" + context.Request.Host))
    {
        context.RedirectUri = context.RedirectUri.Substring(
            context.RedirectUri.IndexOf('/', "http://".Length));
    }
    applyRedirect(context);
};

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login"),
    Provider = cookieProvider
});

While we can't get at where the redirection rule is set easily, OWIN uses a delegate to perform the actual redirect. What I've done here is stored that delegate, modified the URL it is about to be given, and then called it again.

With this option, ensure that any other redirects and links within your site are relative.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...