WS-Federation with OWIN in ASP.NET WebForms

2019-05-31 05:56发布

问题:

I want to use WSFederation with OWIN in WebForms application. Even though I deny unauthorized access in web.config using the <authorization> tag on all my pages, application does not automatically redirect to IDP.

Application automatically redirects to Login page in case of CookieAuthentication MW, but does not do so in case of WS-FederationAuth MW.

Same thing works in MVC. In MVC app, on decorating my Action with [Authorize] attribute, application automatically redirects to IDP even when using WS-FederationAuth MW.

Is converting 401 to 302 doable in WebForms ?

My Sample Code:

  public void ConfigureAuth(IAppBuilder app)
    {

 app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
            AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType

        });

        app.UseWsFederationAuthentication(
                new WsFederationAuthenticationOptions
                {
                    Wtrealm = "http://localhost:53785/",
                    MetadataAddress = metatdataaddress,
                }
            );
        }

回答1:

In a WebForms application, you don't have any OWin attributes like [Authorize] to use that can tell the Owin Authentication Middleware to Challenge (redirect) your authentication provider. This is easy to solve though.

First, add a middleware that checks if your Identity is Authenticated, if not force a challenge redirect:

 app.Use((context, next) =>
{
    if (context.Authentication.User != null &&
        context.Authentication.User.Identity != null &&
        context.Authentication.User.Identity.IsAuthenticated)
    {
        return next();
    }
    else
    {
        // redirects to your provider
        context.Authentication.Challenge(authenticationTypes);
        return Task.FromResult(0);
    }
});
// note that it has to run in the right stage
app.UseStageMarker(PipelineStage.Authenticate)

Then, if you are using a Cookies authentication, it should be set as default. This means that "If you find a cookie, trust this as long as it's valid instead of redirecting to the WsFed provider"

app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

Now, when coming back from your WsFed provider, this cookie needs to be set. In the case of WebForms this normally has to be done manually, using a notification on your middleware. My experience is that .SignIn() is not enough to flag we're authenticated, so we actually re-establish the Authentication Ticket:

new WsFederationAuthenticationOptions
{
  Notifications = new WsFederationAuthenticationNotifications
  {
      SecurityTokenValidated = (notification) =>
      {
         var identity = notification.AuthenticationTicket.Identity;
         var defaultName = identity.FindFirst("<claim-that-identifies-user>");

         // Forcefully set a cookie so that the WsFed provider does not have to be consulted on every request as long as this cookie is valid

var ticket = new AuthenticationTicket(response.AuthenticationTicket.Identity, response.AuthenticationTicket.Properties);
            var currentUtc = new SystemClock().UtcNow;
            ticket.Properties.IssuedUtc = currentUtc;
            ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(1));
            response.AuthenticationTicket = ticket;
            response.OwinContext.Authentication.SignIn(new AuthenticationProperties(), response.AuthenticationTicket.Identity);


         // context.Authentication.User.Identity.IsAuthenticated will now be true

         return Task.FromResult(notification);
      }
   },