Using OWIN Authentication Middleware WITH VS2012 And ASP.NET MVC-4

Short version

Aim is to use OWIN Authentication middleware using external authentication and Asp.Net Identity for a Asp.Net MVC 4 application in Visual Studio 2012.

Longer version

There has been a lot of talk about OWIN. The advantages it brings to developers so that they don’t have to write their code specific to a particular host. Whatever code they want to write which must examine the request and response can now be written as a OWIN middleware. There also has been changes around Authentication. Earlier, FormsAuthentication along with Asp.Net Membership/SimpleMembership was used to authenticate the user and save his/her profile data. Now the new security feature design for MVC5 application is based on OWIN Authentication middleware and ASP.Net Identity. But this is nicely packaged in a MVC 5 template for Visual Studio 2013. What if I want to use it for a MVC 4 application using Visual Studio 2012. This blog post is result of one such experiment.

References

If you want to learn more then please refer following links.

Introduction to ASP.NET Identity

An Overview of Project Katana

Understanding OWIN Forms authentication in MVC 5

A primer on OWIN cookie authentication middleware

Let’s start the experiment!

Creating a new MVC 4 application

Open Visual Studio 2012 and create a basic ASP.NET MVC 4 Web Application.

image

image

Setting up Home controller and a default action

Inside the Controllers folder, create a HomeController and an Index action. Also set up its view. Run app to see if you are able to see the default view.

Setting up OWIN

Next thing is to set up OWIN in our ASP.NET MVC 4 application. Install ‘Microsoft.Owin.Host.SystemWeb’ package. When prompted also install the packages this package depends on.

Install-Package Microsoft.Owin.Host.SystemWeb

Note: The above package enables running OWIN pipeline on IIS.

Also install ‘Microsoft.Owin.Diagnostics

Install-Package Microsoft.Owin.Diagnostics -Version 2.1.0

Verify OWIN is running

Run application, But guess what? there is an error which says following.

The following errors occurred while attempting to load the app.
– No assembly found containing an OwinStartupAttribute.
– No assembly found containing a Startup or [AssemblyName].Startup class.
To disable OWIN startup discovery, add the appSetting owin:AutomaticAppStartup with a value of “false” in your web.config.
To specify the OWIN startup Assembly, Class, or Method, add the appSetting owin:AppStartup with the fully qualified startup class or configuration method name in your web.config.

The reason for above error is that we have added the NuGet package for OWIN in our application but have not configured it yet. As error says it is looking for a startup file which will have the configuration for OWIN.

Lets fix this error. On the root of your application create class named ‘Startup’. Replace class definition with following.

namespace AspNetOwinForBlog
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseWelcomePage();
        }
    }
}

Run application again, if you see following page then it means the OWIN application is successfully running.

image

Once we have verified Owin is running, Comment out following lines from the Startup class.

//app.UseWelcomePage();

Import OWIN auth middleware

To Enable cookie based external authentication in our application we will import related OWIN middlewares.

Install-Package Microsoft.Owin.Security

Install-Package Microsoft.Owin.Security.Cookies

Install-Package Microsoft.Owin.Security.Facebook

Install-Package Microsoft.Owin.Security.OAuth

Also install Microsoft’s OWIN implementation for ASP.NET Identity

Install-Package Microsoft.AspNet.Identity.Owin

Configure external auth provider, Facebook for this example

Create an app at developers.facebook.com and obtain AppId and AppSecret. Also configure a ‘Website’ platform and configure Site Url as your applications url. For me it was http://localhost:63807/

image

Configure auth middleware

Replace content of your startup class with following, Please see inline code comments for more information.

using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Facebook;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace AspNetOwinForBlog
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            //app.UseWelcomePage();

            app.UseCookieAuthentication(
                new CookieAuthenticationOptions()
                {
                    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                    //Imp Point: Below setting indicates that when user is not authenticated , instead of 401 response
                    //issue a 302 response and transfer client to the address defined below
                    // if you dont want the OWIN middleware to change 401 to 302 then comment the following line as i have done
                    //,LoginPath = new PathString("/Home/Index")

                    //Uncomment following to indicate that cookies should be only transferred if the connection is secure
                    //,CookieSecure = CookieSecureOption.Always
                });
            // Use a cookie to temporarily store information about a user logging in with a third party login provider
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            app.UseFacebookAuthentication(new FacebookAuthenticationOptions()
            {
                AppId = "appid",
                AppSecret = "appSecret",
            });
        }
    }
}

Changes to HomeController

Replace content of the HomeController with following.

using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Web;
using System.Web.Mvc;

namespace AspNetOwinForBlog.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult ExternalLogin(string provider)
        {
            // Request a redirect to the external login provider
            return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Home"));
        }

        [AllowAnonymous]
        public ActionResult ExternalLoginCallback()
        {
            var externalLoginInfo = AuthenticationManager.GetExternalLoginInfo();
            var claims = externalLoginInfo.ExternalIdentity.Claims.ToList();

            //The second parameter must match the authentication type configured in the startup file.
            var id = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
            AuthenticationManager.SignIn(id);
            return RedirectToAction("index");

        }

        private IAuthenticationManager AuthenticationManager
        {
            get
            {
                return HttpContext.GetOwinContext().Authentication;
            }
        }

        private class ChallengeResult : HttpUnauthorizedResult
        {
            public ChallengeResult(string provider, string redirectUri)
            {
                LoginProvider = provider;
                RedirectUri = redirectUri;
            }

            public string LoginProvider { get; set; }
            public string RedirectUri { get; set; }

            public override void ExecuteResult(ControllerContext context)
            {
                var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
                context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
            }
        }
    }
}

Changes to Index View

Replace contents of the Index view with following.

@using System.Web
@using Microsoft.AspNet.Identity
@using Microsoft.Owin.Security

@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>

@if (Request.IsAuthenticated)
{
    <h3>Welcome @User.Identity.GetUserName() </h3>
}
else
{
    <h4>You are not logged in, Please use a service to log in.</h4>
    <hr />

        //get list of configured external authentication middleware
        var loginProviders = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes();
        if (loginProviders.Count() == 0)
        {
            <div>
                <p>There are no external authentication services configured</p>
            </div>
        }
        else
        {
            using (Html.BeginForm(&quot;ExternalLogin&quot;, &quot;Home&quot;))
            {
                @Html.AntiForgeryToken()
                <div id=&quot;socialLoginList&quot;>
                    <p>
                    @foreach (AuthenticationDescription p in loginProviders)
                    {
                        <button type=&quot;submit&quot; class=&quot;btn btn-default&quot; id=&quot;@p.AuthenticationType&quot; 
                            name=&quot;provider&quot; value=&quot;@p.AuthenticationType&quot; 
                            title=&quot;Log in using your @p.Caption account&quot;>@p.AuthenticationType</button>
                    }
                    </p>
                </div>
            }
        }
}

Web.config change

Please verify following configuration is not present in your web.config file

<authentication mode="Forms">
    <forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>

That is it folks

Run application and if not logged in you shall see a screen as follows

image

and after you have successfully authenticated with Facebook you shall see following.

image

As one of the reader noted

As @ironicnet noted, you should remove “FormsAuthentication” module.

<system.webServer>
  <modules>
    <remove name="FormsAuthentication" />
  </modules>
<system.webServer>

-Thanks!

Advertisements

6 comments

  1. Just to add as if as me you were breaking your head with the redirecting to “login.aspx”…
    besides of making sure that the Authentication tag is absent, make it so that it also removes the module:
    http://forums.asp.net/t/1966436.aspx?OWIN+Authentication+and+timeout+with+redirection

    1. Good point you mentioned. Thanks.

  2. Hello Prerak,

    your article is pretty helpful. I liked it.
    But what about logout functionality? If you have done it, please share.

    1. you can try AuthenticationManager.SignOut(); for logout. HTH. thanks.

  3. Touseef Kariyania · · Reply

    Hello Prerak, It worked. thanks.

    But this way, as the other article mentioned,
    var ctx = Request.GetOwinContext();
    var authenticationManager = ctx.Authentication;
    authenticationManager.SignOut();

    I did the PoC which is working only on basic application.
    when I move to master page application, it is not working. It always redirect to login.aspx page with error code 401.
    localhost:64686/login.aspx?ReturnUrl=%2fHome%2fExternalLogin

    I have tried different solutions like remove authentication node, add

    but neither of this things works.
    I dont know what i’ve missed.
    I am getting this error and I could not solve it from last 5 days and now I need help!

    I am using VS2012 FYI.

    I could share the code with you as well, if you are able to solve, that’d be grateful.

  4. Hi brother, this helped me. thanks so much. I got it.

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 )

Google+ photo

You are commenting using your Google+ 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 )

Connecting to %s

%d bloggers like this: