July 3rd, 2013

Understanding OWIN Forms authentication in MVC 5

Overview

The new security feature design for MVC 5 is based on OWIN authentication middleware. The benefit for it is that security feature can be shared by other components that can be hosted on OWIN. Since the Katana team did a great effort to support the OWIN integrated pipeline in ASP.NET, it can also secure apps hosted on IIS, including ASP.NET MVC, Web API, Web Form.

Forms authentication uses an application ticket that represents user’s identity and keeps it inside user agent’s cookie. When user first accesses a resource requiring authorization, it will redirect user to login page. After the user provides credentials, your application code will validate the user name and password and build user claims including user’s name, roles, etc. After passing claims to the Forms authentication middleware, it will convert it to an application ticket and serialize, encrypt and encode it into a ticket token. Then, send it out as a cookie. When the next time user sends request with the cookie, the middleware will validate it and convert the ticket token back to claims principal and save it in HttpContext.User, which will shared across ASP.NET pipeline.

ASP.NET also has a forms authentication support through the FormsAuthenticationModule, which, however, can only support applications hosted on ASP.NET and doesn’t have claim support . Here is a rough feature comparison list:

Features

Asp.Net Forms Authentication

OWIN Forms Authentication

Cookie Authentication

Yes

Yes

Cookieless Authentication

Yes

No

Expiration

Yes

Yes

Sliding Expiration

Yes

Yes

Token Protection

Yes

Yes

Claims Support

No

Yes

Web Farm Support

Yes

Yes

Unauthorized Redirection

Yes

Yes

In this blog, you will learn:

· Creating an MVC project with OWIN Forms authentication enabled.

· Understanding OWIN Forms authentication options.

· Understanding Application Sign In Cookie flow.

· Understanding External Sign In Cookie flow.

· Working with new Identity API

Creating MVC project with OWIN Forms authentication enabled

To get started, you need to create new MVC .

· Make sure you have installed:

· In Visual Studio 2013, select New Project from File menu

· In New Project dialog, select Installed Template / Visual C# / Web / ASP.NET Web Application

· In New ASP.NET Project dialog, select MVC project template

clip_image002

Optional: On the right panel of the dialog, you can select Configure Authentication, to choose No Authentication, Individual User Accounts, Organization Authentication and Windows Authentication. In this tutorial, we use Individual User Accounts, which is the default setting.

clip_image004

· Click Create Project button

In the new project, open the App_Start/Startup.Auth.cs file. It has the following code:

Code Snippet
  1. publicpartialclassStartup
  2. {
  3.     // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
  4.     publicvoid ConfigureAuth(IAppBuilder app)
  5.     {
  6.         // Enable the application to use a cookie to store information for the signed in user
  7.         // and to use a cookie to temporarily store information about a user logging in with a third party login provider
  8.         app.UseSignInCookies();
  9.  
  10.         // Uncomment the following lines to enable logging in with third party login providers
  11.         //app.UseMicrosoftAccountAuthentication(
  12.         //    clientId: “”,
  13.         //    clientSecret: “”);
  14.  
  15.         //app.UseTwitterAuthentication(
  16.         //   consumerKey: “”,
  17.         //   consumerSecret: “”);
  18.  
  19.         //app.UseFacebookAuthentication(
  20.         //   appId: “”,
  21.         //   appSecret: “”);
  22.  
  23.         //app.UseGoogleAuthentication();
  24.     }
  25. }

 

Note that UseSignInCookies must be called before any external login providers.

 

Understanding OWIN Forms authentication options

The UseSignInCookies extension method actually registers two cookie authentications. (You can see the source for the methods below at at: http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.Forms/FormsAuthenticationExtensions.cs )

Code Snippet
  1. publicstaticvoid UseSignInCookies(thisIAppBuilder app)
  2. {
  3.     app.UseApplicationSignInCookie();
  4.     app.UseExternalSignInCookie();
  5. }

Both the UseApplicationSignInCookie and the UseExternalSignInCookie extension methods call UseFormsAuthentication, but with different settings.

Code Snippet
  1. publicstaticIAppBuilder UseApplicationSignInCookie(thisIAppBuilder app)
  2. {
  3.     return UseFormsAuthentication(app, new FormsAuthenticationOptions
  4.     {
  5.         AuthenticationType = FormsAuthenticationDefaults.ApplicationAuthenticationType,
  6.         AuthenticationMode = AuthenticationMode.Active,
  7.         CookieName = FormsAuthenticationDefaults.CookiePrefix + FormsAuthenticationDefaults.ApplicationAuthenticationType,
  8.         LoginPath = FormsAuthenticationDefaults.LoginPath,
  9.         LogoutPath = FormsAuthenticationDefaults.LogoutPath,
  10.     });
  11. }
  12.  
  13. publicstaticIAppBuilder UseExternalSignInCookie(thisIAppBuilder app)
  14. {
  15.     app.SetDefaultSignInAsAuthenticationType(FormsAuthenticationDefaults.ExternalAuthenticationType);
  16.  
  17.     return UseFormsAuthentication(app, new FormsAuthenticationOptions
  18.     {
  19.         AuthenticationType = FormsAuthenticationDefaults.ExternalAuthenticationType,
  20.         AuthenticationMode = AuthenticationMode.Passive,
  21.         CookieName = FormsAuthenticationDefaults.CookiePrefix + FormsAuthenticationDefaults.ExternalAuthenticationType,
  22.         ExpireTimeSpan = TimeSpan.FromMinutes(5),
  23.     });
  24. }

The Application sign in cookie is used to authenticate users for the current application, while external sign in cookie is used to authenticate users from external providers, like Facebook, Google, Twitter and Microsoft account. If you want to change the default authentication options, you can use UseFormsAuthentication extension method to change them.

Here are list of options that you can change in UseFormsAuthentication:

Code Snippet
  1. app.UseFormsAuthentication(new FormsAuthenticationOptions()
  2. {
  3.     AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
  4.     AuthenticationType = “MyApplication”,
  5.     CookieDomain = “.myapp.com”,
  6.     CookieHttpOnly = true,
  7.     CookieName = “.AspNet.MyApplication”,
  8.     CookiePath = “/Account”,
  9.     CookieSecure = CookieSecureOption.Always,
  10.     ExpireTimeSpan = TimeSpan.FromDays(1),
  11.     LoginPath = “/Account/Login”,
  12.     ReturnUrlParameter = “return_url”,
  13.     SlidingExpiration = true,
  14.     Provider = new FormsAuthenticationProvider()
  15.     {
  16.         OnResponseSignin = async ctx =>
  17.         {
  18.             Console.WriteLine(“OnResponseSignin”);
  19.             PrintClaimsIdentity(ctx.Identity);
  20.         },
  21.         OnValidateIdentity = async ctx =>
  22.         {
  23.             Console.WriteLine(“OnValidateIdentity”);
  24.             PrintClaimsIdentity(ctx.Identity);
  25.         }
  26.     }
  27. });

Options

Description

ApplicaitonSignInCookie Default Values

ExternalSignInCookie Default Values

AuthenticationMode

If Active the authentication middleware alters the requested user coming in and returns 401 Unauthorized responses going out.

If Passive the authentication middleware will only provide identity and alter responses when explicitly indicated by the AuthenticationType.

Active

Passive

AuthenticationType

The AuthenticationType in the options corresponds to the IIdentity.AuthenticationType property. A different value may be assigned in order to use the same authentication middleware type more than once in a pipeline.

“Application”

“External”

CookieDomain

Defines the domain that cookie is under

<null>

<null>

CookieHttpOnly

Defines if the cookie is http only. It’s true by default.

True

True

CookieName

Defines the name of the cookie

“.AspNet.Application”

“.AspNet.External”

CookiePath

Defines the path that cookie is under. By default, it’s /.

“/”

“/”

CookieSecure

Defines if the cookie will only be sent back to HTTPS URL. By default, it is SameAsRequest, which means If the URI that provides the cookie is HTTPS, then the cookie will only be returned to the server on subsequent HTTPS requests. Otherwise if the URI that provides the cookie is HTTP, then the cookie will be returned to the server on all HTTP and HTTPS requests.

SameAsRequest

SameAsRequest

ExpireTimeSpan

Defines the expiration of the cookie.

14 days

5 minutes

LoginPath

Defines the Login path when unauthorized request will be redirected to.

“/Account/Login”

<null>

ReturnUrlParameter

Defines the return URL parameter name, which tells your application the URL of previous unauthorized request to redirect to after login. Your application code is responsible for retrieving it and redirecting the user agent to the return URL

“ReturnUrl”

<null>

SlidingExpiration

Defines if the authentication supports sliding expiration, which will automatically extends the expiration time if user session is still active. By default, it’s true.

True

False

Provider

The forms authentication provider that can intercept events during sign in and validate identity.

· OnResponseSignin: happens just before set-cookie is sent out

· OnValidateIdentity: happens just after incoming cookie is parsed into ClaimsIdentity

<null>

<null>

Understanding Application Sign in Cookie flow

Active mode is similar to what the old ASP.NET forms authentication module did, while passive is a way to let framework code control the authentication explicitly.

ApplicatinSignInCookie is an active forms authentication middleware, so when a valid cookie is returned, it will:

· Automatically redirect an unauthorized response to the login page.

· Set the logged in user principal to HttpContext.User, so the rest of ASP.NET pipeline will know what user is authenticated.

 

The following is a basic flow of application forms authentication.

User Agent

Forms Authentication Middleware(Application)

Web App

1. Get /Account/Manage

—————————->

   
   

2. Response Status: 401

AccountController is protected by Authroize attribute, so unauthorized request will return a 401 error.

[Authorize]

public class AccountController : Controller

{

}

<—————————-

 

3. Alter response status to 302 and redirect to /Application/Login?ReturnUrl=/Account/Manage

The application sign in cookie is in active authentication mode and it will automatically redirect to login page when there is a 401 response.

<—————————-

 

4. GET /Application/Login?ReturnUrl=/Account/Manage

—————————->

   
   

5. Response Status: 200 and with login page in body

<—————————-

6. POST /Application/Login?ReturnUrl=/Account/Manage

User input user name and password and post back to server

—————————->

   
   

7. Status: 301

Location: /Account/Manage

Server code does:

a. Validating user credentials

b. Calling IdentityAuthenticationManager.SignIn to sign in with application sign in cookie

c. Redirecting to returnUrl

<—————————-

 

8. Status: 302

Location: /Account/Manage

Set-Cookie: .AspNet.Application=<Ticket Token>

The middleware will convert user claims with extra data into ticket token and set it in cookie.

<—————————-

 

9. GET /Account/Manage

Cookie: .AspNet.Application=<Ticket Token>

—————————->

   
 

10. Validate <Ticket Token> and convert it to claims identity and set it to HttpContext.User

—————————->

 
   

11. Status: 200 with manage account page in body

Authorize attribute sees that the identity is authenticated from HttpContext.User. So allow the request to reach the action.

<—————————-

Understanding External Sign in Cookie flow

ExternalSignInCookie is a passive forms authentication, which is unobtrusive to your application if you don’t explicitly ask it to do something. Your application code can explicitly ask it to provide the user identity or alter the response to set cookie or remove cookie. To demo external sign in cookie, you need to configure an external provider like Facebook. This flow chart starts from the point Facebook authentication middleware receives the user info from Facebook graph API. For the detailed flow for external provider sign in process, please check out Robert’s tutorial: External Authentication Services

User Agent

Forms Authentication Middleware(Application)

Forms Authentication Middleware(External)

Facebook Authentication Middleware

Web App

<After facebook returns authorization code back to your app>

1. GET /signin-facebook?code=<authorization code>&state=<state>

—————————->

       
     

2. Status: 302

Location: /Account/ExternalLoginCallback?loginProvider=Facebook

Middleware code does:

a. Get access token by authorization code from facebook

b. Get user graph data from facebook

c. Convert user graph data into claims identity

d. Sign in claims identity as external type

<—————————-

 
   

3. Status: 302

Location: /Account/ExternalLoginCallback?loginProvider=Facebook

Set-Cookie: .AspNet.External=<ticket token>

External forms middleware does:

a. Convert claims identity to ApplicationTicket

b. Serialize ApplicationTicket to byte array

c. Encrypt and encode byte array to ticket token

d. Set cookie to response

<—————————-

   

4. Get /Account/ExternalLoginCallback?loginProvider=Facebook

Cookie: .AspNet.External=<ticket token>

 

—————————->

       
       

5. IdentityAuthenticationManager.GetExternalIdentity()

The extension method will call into OWIN middleware to explicitly authenticate with external type

<—————————-

   

6. Authenticate cookie and return user claims identity

External forms middleware does:

a. Decode and decrypt ticket token into byte array

b. Deserialize byte array to ApplicationTicket

c. Get claims identity from ApplicationTicket

d. Return identity back to caller

—————————->

   
       

7. Status: 200

Body: external login page

 

After getting the external identity, check if the user is already registered.

– If no, return external login confirmation page.

– If yes, directly log user in (Not included in this flow)

<—————————-

8. POST /Account/ExternalLoginConfirmation

Cookie: .AspNet.External=<ticket token>

Body: UserName=test&LoginProvider=Facebook

—————————->

       
       

9. IdentityAuthenticationManager.GetExternalIdentity()

The extension method will call into OWIN middleware to explicitly authenticate with external type

<—————————-

   

10. Authenticate cookie and return user claims identity

—————————->

   
       

11. Status: 302

Location: /

Web app code does:

a. Create local user via membership provider

b. Associate local user with external identity’s ID claim (facebook id)

c. Sign the external identity in as Application type

d. Redirect to returnUrl or home page

<—————————-

 

12. Status: 302

Location: /

Set-Cookie: .AspNet.Application=<Ticket Token>

 

Turn claims identity to ticket token and set cookie in response

<—————————-

     

Working with new Identity API

IdentityAuthenticationManager wraps everything that you need to work with Application and External sign in cookies.

Code Snippet
  1. publicclassIdentityAuthenticationManager
  2. {
  3.     public IdentityAuthenticationManager();
  4.     public IdentityAuthenticationManager(IdentityStoreManager storeManager);
  5.  
  6.     publicstring ClaimsIssuer { get; set; }
  7.     publicstring RoleClaimType { get; set; }
  8.     public IdentityStoreManager StoreManager { get; set; }
  9.     publicstring UserIdClaimType { get; set; }
  10.     publicstring UserNameClaimType { get; set; }
  11.  
  12.     publicvirtualvoid Challenge(HttpContextBase context, string authenticationType, string redirectUrl);
  13.     publicvirtual Task<bool> CheckPasswordAndSignIn(HttpContextBase context, string userName, string password, bool isPersistent);
  14.     publicvirtual Task<bool> CreateAndSignInExternalUser(HttpContextBase context, string loginProvider, IUser user);
  15.     publicvirtual IEnumerable<Microsoft.Owin.Security.AuthenticationDescription> GetExternalAuthenticationTypes(HttpContextBase context);
  16.     publicvirtual Task<ClaimsIdentity> GetExternalIdentity(HttpContextBase context);
  17.     publicvirtual Task<IList<Claim>> GetUserIdentityClaims(string userId, IEnumerable<Claim> claims);
  18.     publicvirtual Task<bool> LinkExternalIdentity(ClaimsIdentity id, string userId, string loginProvider);
  19.     publicvirtual Task SignIn(HttpContextBase context, string userId, bool isPersistent);
  20.     publicvirtual Task SignIn(HttpContextBase context, string userId, IEnumerable<Claim> claims, bool isPersistent);
  21.     publicvirtual Task<bool> SignInExternalIdentity(HttpContextBase context, ClaimsIdentity id, string loginProvider);
  22.     publicvirtualvoid SignOut(HttpContextBase context);
  23.     publicvirtualbool VerifyExternalIdentity(ClaimsIdentity id, string loginProvider);
  24. }

Method

Description

CheckPasswordAndSignIn

Verify user name and password against storage like SQL server and sign in with Application cookie

CreateAndSignInExternalUser

Create user based on external identity from External cookie in storage like SQL server, and sign in user as Application cookie

GetExternalIdentity

Get external identity from External cookie

GetUserIdentityClaims

Replace user id and name claims and add roles and user custom claims from storage.

LinkExternalIdentity

Link external identity with local user in storage

SignIn

Sign out External cookie and sign in Application cookie

SignInExternalIdentity

Get user associating with external identity in storage and sign this user in as Application cookie

SignOut

Sign out from Application cookie

VerifyExternalIdentity

Verify if the external identity has the same issuer as loginProvider

Challenge

Explicitly ask authentication middleware to send challenge to the response. For example, Application forms middleware will challenge to redirect to login page with 302 status code.

GetExternalAuthenticationTypes

Get supported external authentication types which you register in the OWIN middleware pipeline, like Facebook, Google, etc.

The following shows the login code for the ASP.NET MVC template:

Code Snippet
  1. [HttpPost]
  2. [AllowAnonymous]
  3. [ValidateAntiForgeryToken]
  4. publicasyncTask<ActionResult> Login(LoginViewModel model, string returnUrl)
  5. {
  6.     if (ModelState.IsValid)
  7.     {
  8.         // Validate the user password
  9.         if (await AuthenticationManager.CheckPasswordAndSignIn(HttpContext, model.UserName, model.Password, model.RememberMe))
  10.         {
  11.             return RedirectToLocal(returnUrl);
  12.         }
  13.     }
  14.  
  15.     // If we got this far, something failed, redisplay form
  16.     ModelState.AddModelError(“”, “The user name or password provided is incorrect.”);
  17.     return View(model);
  18. }

0 comments

Discussion are closed.