Comment unsuccessful. Please correct the errors below.

MVC Application using Owin to achieve Federated Authentication

Here is a minimal example of how to set up an ASP.NET MVC Application to use Owin to achieve Federated Authentication (WS-Federation) and trust the Microsoft Azure Access Control Service (ACS) to enable login with Facebook, Google, Live Id, Yahoo! and custom Active Directories using Active Directory Federation Service ADFS.

Who am I (What’s my name)?

First of all – as my readers know – I start my technical blog posts with a related bit of music. This time Identity is on the agenda for the post and what would fit better than to use the old Snoop Doggy Dogg hit Who am I (what’s my name)?

The Goal

I have many customers who build SaaS Applications as their business idea. They naturally have users that need to sing into the applications they build. It’s tempting to use username/password authorization in your application for this scenario. You shouldn’t.

The issue with username/password sing-in is that suddenly  you are storing secrets that belong to your users. If you can avoid this you should!

One way to not have to store secrets for your users is to allow users to sing in using external Identity Providers (IdP). Today this practice is very common and some very large companies have adopted it. The largest example I know of is Spotify. In order to sing into Spotify the user must have a Facebook account. Spotify won’t store the user secret of the password in their database, but instead fully and wholly delegate this to Facebook.

One minor complication for you as a developer in this scenario is that there is not only one IdP out there. There are many. Many big ones and very many others too. The big ones are for instance Facebook, Google, Microsoft Account and Yahoo!. But you might also want to set up Federated Authentication with any number of customer ADFS instances. This is a common scenario with my customers. They want users to be able to pic a social IdP if they want to, but they also want to be able to Federate Identity with importan customers’ ADFS servers. The number of integration points for authentication is limitless. This can cause your application to have to contain custom code for each IdP you integrate with. The ASP.NET team has done a tremendous job making this as easy as possible for you in the template project that you get when you do File –> New –> ASP.NET Application. Still it can be made even easier and more powerful!

Enter the ACS (Access Control Service). ACS externalizes all of this IdP management from your application and enables your application to trust only one thing – the ACS. In turn the ACS can be set up such that it allows multiple IdPs to authorize to multiple applications that you build (called RP or Relying Party applications). This blog post is not about how you set up ACS in detail. Rather it’s about how you configure an application usin Owin to take advantage of the ACS. Here is a picture that shows the setup in it’s entirety.

ACS usage

Top right is a little extra thin that I haven’t mentioned above. If you do want to allow your users to sign into your application using a username/password that you control, it’s completely possible. I’m unsure of why you would want to keep your users’ secrets but again it can be done. In order to do this you would have to set up and maintain your own IdP my managing something that is known as an STS or Security Token Service. I would advice you to take a look at the excellent open source project Identity Server by Dominick Baier. If you want to read more about how to set up ACS you can do so here: How to Authenticate Web Users with Azure Active Directory Access Control.

What I wanted to do with my authentication demo project was to find out how little I have to set up in order to do achieve this in a modern ASP.NET MVC application using Owin.

I used the Microsoft Azure Access Control Service but you don’t have to do that. you can also use this same approach to authenticate with any WS-Federation compatible Identity Provider Security Token Service.

How does Owin involve Security?

At it’s heart Owin is a pipeline. Stuff that gets called before your application code is reached. Security is such a concern which lends itself very well to implementations as an Owin middleware component. The standard ASP.NET MVC template that uses Indvidual Accounts as it’s security strategy is equipped with a long list of these Owin components, one for each authentication method on top of a few core ones. If you are not, for instance, allowing Twitter users to sign into your application using Twitter you can go ahead and remove the component Microsoft.Owin.Security.Twitter. In my scenario I want to use only one security measure in my application and let the external service ACS be the security gate keeper for my app. My app only uses Microsoft.Owin.Security.WsFederation. This means that my list of packages is as short as possible and that means that there is few security concerns inside my application. Which is a good thing.

Packages

I added NuGet packages to my application ONLY if they were needed to arrive to a minimal set of packages. It was rather tricky actually because it’s difficult to know which package exactly does what. After some trial and error I arrived at this basic set. Again this is an ASP.NET MVC application and I have enabled Owin middleware to handle WS-Federation:

<packages>
  <package id="bootstrap" version="3.2.0" targetFramework="net45" />
  <package id="jQuery" version="2.1.1" targetFramework="net45" />
  <package id="Microsoft.AspNet.Mvc" version="5.2.2" targetFramework="net45" />
  <package id="Microsoft.AspNet.Razor" version="3.2.2" targetFramework="net45" />
  <package id="Microsoft.AspNet.WebPages" version="3.2.2" targetFramework="net45" />
  <package id="Microsoft.IdentityModel.Protocol.Extensions" version="1.0.0" targetFramework="net45" />
  <package id="Microsoft.Owin" version="3.0.0" targetFramework="net45" />
  <package id="Microsoft.Owin.Host.SystemWeb" version="3.0.0" targetFramework="net45" />
  <package id="Microsoft.Owin.Security" version="3.0.0" targetFramework="net45" />
  <package id="Microsoft.Owin.Security.Cookies" version="3.0.0" targetFramework="net45" />
  <package id="Microsoft.Owin.Security.WsFederation" version="3.0.0" targetFramework="net45" />
  <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
  <package id="Modernizr" version="2.8.3" targetFramework="net45" />
  <package id="Owin" version="1.0" targetFramework="net45" />
  <package id="System.IdentityModel.Tokens.Jwt" version="4.0.0" targetFramework="net45" />
</packages>

As you can see there are some Owin fundamentals and then Cookie handling and WsFederation. Also the System.IdentityModel and Microsoft.IdentityModel are used by the Owin.Security modules. If you are trying to repeat this in your own code go ahead and install the Microsoft.Owin.Security.WsFederation and Microsoft.Owin.Security.Cookies packages first which will get you almost all the way there.

Set up WS-Federation Authentication using Owin

Now that we have the packages installed calling them to set up Authentication is the next step. In order to get into Owin this is a standard step:

[assembly: OwinStartup(typeof(Startup))]
namespace martensson.com.Security
{
    public partial class Startup
    {
        // ReSharper disable once UnusedMember.Global
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }
    }
}

The OwinStartupAttribute will ensure the Configuration method is called in the Startup class as the application starts up.

The ConfigureAuth step is really very easy:

private void ConfigureAuth(IAppBuilder app)
{
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

    app.UseCookieAuthentication(new CookieAuthenticationOptions());

    app.UseWsFederationAuthentication(
        new WsFederationAuthenticationOptions
        {
            MetadataAddress = AuthenticationConfiguration.MetadataAddress,
            Wtrealm = AuthenticationConfiguration.Wtrealm
        }
    );
}

Basically we set cookie based authentication which means that the web session is maintained using a standard cookie. Next we call UseWsFederationAuthentication to set up and point to the WS-Federation services in ACS. The two values MetadataAddress and Wtrealm are set up when you create and configure your ACS tenant.

The security code in the app

The only thing you need to configure inside your application in order to mark secure sections (that require sign-in) is the AuthorizeAttribute:

[Authorize]
public class AdminController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

This means any call to any action method inside of the AdminController may only be done by authorized users.

The ACS comes with a built in sing-in page which is used unless you configure that you want to use your own localized sign-in page. In the ACS you can download a template page and insert into your code if you wish. I created an AuthenticationController with a SignIn view and a SingOut view. Also there is a mystical Return view which is used to redirect back from the ACS. This is all pretty simple stuff.

Sign in

public ActionResult SignIn()
{
	return View(AuthenticationConfiguration.IdentityProviderStream);
}

The IdentityProviderStream link is not strictly required but it is a hard coded string in a sample login page that I like to dig out and turn into a constant. Mine in my example looks like this If you browse to it you will find that it shows you a json feed containing information about the IdPs configured in in my ACS:

https://magnusmartenssoncom.accesscontrol.windows.net/v2/metadata/IdentityProviders.js?protocol=wsfederation&realm=http%3a%2f%2flocalhost%3a8353%2f&reply_to=&context=&request_id=&version=1.0&callback=ShowSigninPage

When I have signed in at my IdP and return via the ACS to my application I use this landing place which is simply implemented to send me to the admin/index view:

public ActionResult Return()
{
	// ToDo: Actually handle the real return url and return to the correct page. We only have one page to return to in this small sample.
	return RedirectToAction("Index", "Admin");
}

Sign out

What we have below is a federated sign-out folks! This means that if I sign out from my application I will also sign out from the IdP I used. This means that if I sign in using Facebook I will be signed into Facebook the browser window I authenticate with. When I sign out from my application I also sing out of Facebook.

public void SignOut()
{
	IOwinContext owinContext = HttpContext.GetOwinContext();
	IAuthenticationManager authenticationManager = owinContext.Authentication;
	authenticationManager.SignOut(WsFederationAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
}

The application

Here is what my simple application looks like.

Not authenticated:

Not authenticated

Authenticated:

Authenticated

Deploy to azure

It is of course extremely easy to deploy my application to Azure. Simply right click the application in the Solution Explorer and choose Publish… => Microsoft Azure Websites and follow the instructions to create a new free test web site in Azure. If you don’t have an azure account you can get a free trial account here: Free Trial Azure Account where you can do lots of stuff including hosting up to ten free development/test web sites.

What you need to do when you have deployed is make sure you configure your ACS namespace with a Relying Party configuration for your Azure application. This is how my azure config looks:

ACS RP configuration

Your urls will be different than mine. Again this is not an ACS blog post so I won’t delve deeper into it here.

Look at my code and the app

Here is the solution I built as a zip file: This site as a VS solution code sample

You can hopefully also find it here in it’s deployed form: http://wsfedacsowinmvc.azurewebsites.net/

And you can download this code sample here: Federated Authentication and ACS MVC sample project.

Cheers,

Magnus

Posted by: Magnus Mårtensson
Last revised: 2014-09-17 11:37 History

No comments yet. Be the first!

Login in to comment!