Skip to content

A primer on external login providers (social logins) with OWIN/Katana authentication middleware

January 9, 2014

Like MVC 4, in MVC 5 and Visual Studio 2013 we have the ability to use external login providers (aka social logins) in our ASP.NET applications. The big change related to this from the prior version is that we no longer are using DotNetOpenAuth and instead are now using OWIN authentication middleware to handle the the various protocols to these external providers. Unfortunately the templates in Visual Studio 2013 related to these external providers are quite complex and can be overwhelming (I know, because it took many *days* of debugging and using reflector to really understand how it all worked). Anyway, that’s the point of this post – an attempt to explain in the least amount of code how this external authentication middleware works in Katana. I already made a couple of other posts related to this (cookie middleware and active vs. passive middleware), so those are assumed knowledge.

Katana ships with a few pieces of middleware to allow an ASP.NET application authenticate with external identity providers (like Google, Facebook, Live, Twitter, etc.). The middleware encapsulates the various protocols used to achieve this authentication and that’s a convenient abstraction for an application developer, since there are lots of different protocols that might be used. But it helps to learn the mechanics of the middleware to understand how you’ll be configuring and using it in your code.

When your application wants to authenticate a user with an external provider you indicate this to the AuthenticationManager on the OwinContext. Your code calls Challenge passing the name of the authentication middleware you want to invoke (so “Google”, “Facebook”, etc.). Also, as part of the mechanics, the middleware won’t kick in unless the current HTTP response is a 401 status code. When the external provider middleware you’ve triggered sees the 401 response from your application, it initiates the protocol to the external provider. This usually involves various HTTP redirects to the external provider so the user can login. Here’s what that code might look like:

public ActionResult ExternalLogin(string provider)
{
    var ctx = Request.GetOwinContext();
    ctx.Authentication.Challenge(
        new AuthenticationProperties{
            RedirectUri = Url.Action("Callback", new { provider })
        },
        provider);
    return new HttpUnauthorizedResult();
}

The above code gets the OwinContext from the request. We call Challenge passing an AuthenticationProperties which allows us to indicate a return URL (more on that later), and then the name of the provider we want to use. And, as mentioned above, we need to return a 401 status code.

Presumably your application would give the user a hyperlink with the provider as a query string parameter into this method so the user can indicate which provider they want to use. To dynamically discover what providers your application has configured, look into the GetAuthenticationTypes API on the AuthenticationManager, perhaps something like this:

var ctx = Request.GetOwinContext();
var providers =
    from p in ctx.Authentication.GetAuthenticationTypes(d => !String.IsNullOrWhiteSpace(d.Caption))
    select new
    {
        name = p.Caption,
        url = Url.Action("ExternalLogin", new { provider = p.AuthenticationType })
    };

So after your application triggers the authentication middleware and the protocol is all done, then the middleware does two important things: 1) it issues an authentication cookie (typically called the “ExternalCookie”) representing the outcome of the authentication from the external provider (and this cookie contains all the claims from the external provider for the user), and 2) it redirects the browser back into your application to a URL you have provided (from the AuthenticationProperties above). In this redirect is your chance to examine the claims inside of the external cookie to know who the user is, and then it’s up to you what to do next. Your application could store all of this information in a database, and/or it could just log the user in with the normal cookie middleware based upon the claims from the external provider. It just depends on your authentication requirements. Here’s an example of what that callback might look like:

public ActionResult Callback(string provider)
{
    var ctx = Request.GetOwinContext();
    var result = ctx.Authentication.AuthenticateAsync("ExternalCookie").Result;
    ctx.Authentication.SignOut("ExternalCookie");

    var claims = result.Identity.Claims.ToList();
    claims.Add(new Claim(ClaimTypes.AuthenticationMethod, provider));

    var ci = new ClaimsIdentity(claims, "Cookie");
    ctx.Authentication.SignIn(ci);

    return Redirect("~/");
}

The above code calls back into the AuthenticationManager to obtain the identity of the user from the external cookie. Once we have that, we immediately revoke the cookie by calling SignOut. Next we get the claims for the external authentication (this is where you might store them in a database or load additional claims from a database to add to the logged in user’s claims). In my example here, I chose to augment the claims with an additional claim so I know which provider was used. We then use those claims to log the user into the app using the normal cookie middleware.

The only other thing to look at is how to configure the middleware. This is configured in your Katana startup code, as such:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // this is the normal cookie middleware
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "Cookie",
            AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
        });

        // these two lines of code are needed if you are using any of the external authentication middleware
        app.Properties["Microsoft.Owin.Security.Constants.DefaultSignInAsAuthenticationType"] = "ExternalCookie";
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "ExternalCookie",
            AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
        });

        // these lines of code configure the various providers we want to use
        app.UseFacebookAuthentication(new FacebookAuthenticationOptions {
            AppId = "id", AppSecret = "secret"
        });
        app.UseGoogleAuthentication();
    }
}

The first section (lines 5-10) is the normal cookie middleware used for authenticating users (see my previous post on this).

The second section (lines 13-18) of code relates to the mechanics of the external provider middleware. Recall the external provider middleware needs to issue a cookie to represent the outcome of the external authentication — these two lines set that up. Line 13 assigns into the app.Properties the name of the cookie middleware to use. All the external authentication middleware look for this hard-coded name. Line 14 then configures that cookie middleware. In the templates in Visual Studio 2013 these two lines are hidden behind the UseExternalSignInCookie call in Startup.Auth.cs. An alternative would be to set on each external provider the SignInAsAuthenticationType property, which is the name of the cookie middleware to use.

Finally, the third section (lines 21-24) in the above code configures which external providers we want to use.

HTH

7 Comments leave one →
  1. January 9, 2014 11:38 am

    Reblogged this on leastprivilege.com.

  2. January 15, 2014 10:50 am

    Hi, I’m new to this stuff (I’ve been out of the web world for a few years)(my, how its changed) and still trying to get my head around it all. I cloned your git project ‘thinktecture / Thinktecture.IdentityModel’ and tried to run one of the samples, but in samples\SystemWeb\ClaimsAuthorizeSample\app_start\WebApiConfig, line 16 is not compiling — it looks like you’re trying to add a ClaimsAuthorizeAttribute object to an HttpFilterCollection, and it can’t do it. Am I doing something wrong? I’d love to get this running, it seems exactly what I need. Many thanks for all your work.

  3. January 17, 2014 2:46 am

    Hi, I have a question specifically regarding Facebook Authentication. Facebook supports several authentication workflows depending on where you place your token and secret. For web based clients the process outlined in most of the examples currently on the web make sense where a user authenticates on fb and is issued a code (rather than an access token), he then hands this over to the server-side where the magic happens and he gets a bearer token to use for authentication.
    However for mobile clients that require deep integration with facebook on the device and connect using facebook APIs, the client is directly issued an access token from facebook sdk. How then does one go about exchanging that facebook access token for a bearer token for my own WebApi service while taking as much advantage of the already existing external provider infrastructure?

    • January 17, 2014 10:09 am

      You would have to implement the custom grant type extension hooks in the OAuth2 AS MW.

  4. February 5, 2014 4:37 am

    Do you have any idea how to go about logging out from facebook when signing out from the MVC site? Seems like this is a bit of a hole in the implementation – the user remains logged in to facebook after calling AuthenticationManager.Signout() and the next attempt to authenticate from the same browser will therefore automatically login.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: