Drew
Drew

Reputation: 1347

How to use cookie authentication with WsFederation authentication in OWIN app

I am making an OWIN app to self host a website. I am having trouble with the authentication part. The basic flow I am looking for is:

  1. Check if the request has the correct cookie. If it has the correct cookie, then get the SecuritySessionToken and build the ClaimsIdentity, and then return the AuthenticationTicket.
  2. If the cookie isn't there, then let the STS know.
  3. Get the SAML token from the STS and build the claims & authentication ticket. (Even though I'm using WsFederation authentication, ADFS is not involved here.)

Step 1 is working fine for me. Steps 2 and 3 are where I'm having trouble.

This is what I have for my configuration method in the startup class:

public void Configuration(IAppBuilder app)
{
    CookieAuthenticationOptions options = new CookieAuthenticationOptions
    {
        AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
        CookieName = "MyCookieName",
        CookiePath = "/cookiePath",
        AuthenticationMode = AuthenticationMode.Active
    };
    // Not relevant how this module is made. It helps with
    SessionAuthenticationModule module = this.CreateModule();
    app.Use(typeof(MyCookieAuthenticationMiddleware), app, options, module);

    WsFederationConfiguration fedConfig = new WsFederationConfiguration();
    fedConfig.Issuer = "https://mySTS.com/NotRealUrl/";
    SecurityTokenHandlerCollection handlerCollection = new SecurityTokenHandlerCollection(new List<SecurityTokenHandler>() { new SamlSecurityTokenHandler() });
    WsFederationAuthenticationOptions wsFederationOptions = new WsFederationAuthenticationOptions
    {
        Configuration = fedConfig,
        Wtrealm = "http://localhost/MyApp/NotRealUrl",
        Wreply = "https://mySTS.com/NotRealUrl/Login",
        SecurityTokenHandlers = handlerCollection,
        AuthenticationMode = AuthenticationMode.Active,
        AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
    };
    app.Use(typeof(MyWsFederationAuthenticationMiddleware), app, wsFederationOptions);
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
    app.Use<MyMiddleware>();
}

The part up to app.Use(typeof(MyCookieAuthenticationMiddleware), app, options, module); works fine. That is, when the cookie is there, I use my custom cookie authentication middleware (MyCookieAuthenticationMiddleware) to generate the AuthenticationTicket.

The part that doesn't work is the WsFederation authentication middleware part. I created my own middleware for this (MyWsFederationAuthenticationMiddleware) so that I could take a look at what's going on since doing app.UseWsFederationAuthentication(wsFederationOptions) directly wasn't working for me. I am looking at the source code for WsFederationAuthenticationHandler.cs to guide me, but I'm still confused about some overall concepts.

Questions

  1. I think I know what the general flow should be (what I numbered at the top), but I'm not sure how this translates exactly to the middleware pipeline. If I don't have a cookie, then I find this out in AuthenticateCoreAsync() in MyCookieAuthenticationHandler. But at what point do I redirect this to the STS to get the SAML token? Do I just wait for the pipeline to hit AuthenticateCoreAsync() in MyWsFederationAuthenticationHandler, and if the user is not authenticated, then get the token?
  2. How do I actually tell the pipeline that it needs to get the token from the STS? Looking again at WsFederationAuthenticationHandler.cs, it seems like their implementation of ApplyResponseChallengeAsync() might be doing what I want? More specifically, if it's a 401 status code then make a WsFederationMessage and redirect to the STS using that message?
  3. Assuming I can actually get the STS to return the token I want, where in the pipeline does it return to?

Thanks and sorry for the sort of long post.

Update 1

I forgot to mention, when I run my code using app.UseWsFederationAuthentication(wsFederationOptions) (instead of my own MyWsFederationAuthenticationMiddleware), I get back a 400 "bad request - request too long" error. The URL is very long and looks like it contains various query parameters, including wtrealm, wctx, wa, and wreply, each of which contains url encoded strings. Looks like wctx is the really long one. I imagine it's some base64 encoded object. Unfortunately I don't really know what's going on.

Upvotes: 0

Views: 3547

Answers (1)

Dennis K
Dennis K

Reputation: 477

I think, the problem is, that both Middlewares have the AuthenticationMode Active

You should change the CookieAuthenticationOptions.AuthenticationType to the CookieAuthenticationDefaults.AuthenticationType then set WsFederationAuthenticationOptions.AuthenticationMode to passive.

I recommend an custom controller. If the user visits this controller you must trigger the Authentication on the OwinContext.Authentication manually for the WsFederationAuthenticationDefaults.AuthenticationType and return an 401. That should trigger the ApplyResponseChallengeAsync in the WsFederationAuthenticationHandler

In the SecurityTokenValidated Method on the WsFederationAuthenticationOptions.Notifications you can issue a new AuthTicket with an identity of type CookieAuthenticationDefaults.AuthenticationType.

Now the identity from the identity provider is converted to a an local identity with cookieauth.

Upvotes: 1

Related Questions