ASP.NET Web Api and Identity 2.0 - Customizing Identity Models and Implementing Role-Based Authorization

Posted on October 26 2014 03:23 PM by jatten in ASP.Net, C#, ASP.NET MVC, CodeProject   ||   Comments (11)

hermself 240In a previous post, we took a high-level look at using Identity 2.0 in the context of a Web Api application. We essentially poked and prodded the default Visual Studio Web Api project template, learned where things live, and got a basic sense for how it all is supposed to work.

However, the VS project template is very basic, using Bearer Tokens as the primary authentication mechanism, and does not offer any out-of-the-box support for advanced authorization scenarios. In a nutshell, the VS Project template affords us basic token-based authentication, and that's about it.

Additionally, the User model is simplistic, and there are no Role models defined. This may be intentional, since in some cases you may be using a Web Api project as a simple authentication service. In other scenarios, though, you may want to customize the User model and/or add Role-Based Authentication to the mix.

Image by madamepsychosis  | Some Rights Reserved

We have previously looked at customizing User and Role models in the context of an ASP.NET MVC application, and how we need to modify the stock MVC project to accommodate these customizations. In this post, we will do the same for a Web Api project.

You can find the source code for the example Web Api project on Github:

Consider Your Use Case Before Deciding on Your Auth Strategy

There are a number of options for Authentication and Authorization strategies in a Web Api project. Use of Bearer tokens and Role-Based Authentication is relatively simple to implement, but is not the most advanced architecture for an authorization solution. Before deciding upon traditional Role-Based Authorization, you may want to examine the scope of your authentication and authorization needs, and determine if something simpler, or something more advanced, may be warranted. 

ASP.NET Web Api can take full advantage of Claims-Based Authorization, which, for more complex systems, may be a better choice. Similarly, as mentioned previously, if the primary purpose of your Web Api is to act as an Authentication Service, you may want to go with a more robust token system (for example, shared private keys as opposed to the bearer tokens used by default), and do away with authorization at this level.

Role-Based Authorization is a good fit in a project where there exists a modest need for different levels of authorization/access, and possibly the Web Api is a part of, or associated with, a larger MVC or other ASP.NET site where Roles are used to govern authorization. Consider a standard MVC project, in which a few roles are sufficient to manage authorization, and which serves web pages as well as offers API access.

Applying What We've Learned Previously

Fortunately, much of what we are about to do, we have seen previously, and we can even borrow bits and pieces of code we've already written. If you are just getting familiar with Identity 2.0, feel free to review previous posts in which we performed some similar customization of Users and Roles in the context of an ASP.NET MVC project:

Now that we have some idea what we are dealing with, let's see how we can apply it in the Web Api context.

Getting Started - Create a New ASP.NET Web Api Project

First, in Visual Studio, create a new ASP.NET Web Api project. Once the project is created, update the Nuget packages in the solution, either using Manage Packages for Solution in the context menu for Solution Explorer, or by using Update-Package in the Package Manager Console.

This will update all the nuget packages, and specifically update Web Api to version 2.2.

Adding a Role Model, and Customizing ApplicationUser

To get started, let's take another look at the Models => IdentityModes.cs file. Currently, there is not a lot there:

The Default IdentityModels.cs File in Web Api:
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
 
namespace AspNetIdentity2WebApiCustomize.Models
{
    public class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(
        		UserManager<ApplicationUser> manager, string authenticationType)
        {
            // Note the authenticationType must match the one defined in 
            // CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
            // Add custom user claims here
            return userIdentity;
        }
    }
 
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection", throwIfV1Schema: false)
        {
        }
        
        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }
    }
}

 

As we did in our ASP.NET MVC examples, we begin by modifying and adding to the existing models defined in Models => IdentityModels.cs. In fact, since we did a lot of the work previously, we will start by stealing the IdentityModels.cs code from the ASP.NET Extensible Template Project. Careful here. We can save ourselves some pain by pasting the classes into the existing namespace defined in the current code file, and leaving the using statements as they are for the moment:

Updated IdentityModels.cs Code:
// You will not likely need to customize there, but it is necessary/easier to create our own 
// project-specific implementations, so here they are:
public class ApplicationUserLogin : IdentityUserLogin<string> { }
public class ApplicationUserClaim : IdentityUserClaim<string> { }
public class ApplicationUserRole : IdentityUserRole<string> { }
 
// Must be expressed in terms of our custom Role and other types:
public class ApplicationUser 
    : IdentityUser<string, ApplicationUserLogin, 
    ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationUser()
    {
        this.Id = Guid.NewGuid().ToString();
 
        // Add any custom User properties/code here
    }
 
 
    public async Task<ClaimsIdentity>
        GenerateUserIdentityAsync(ApplicationUserManager manager)
    {
        var userIdentity = await manager
            .CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        return userIdentity;
    }
}
 
 
// Must be expressed in terms of our custom UserRole:
public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
{
    public ApplicationRole() 
    {
        this.Id = Guid.NewGuid().ToString();
    }
 
    public ApplicationRole(string name)
        : this()
    {
        this.Name = name;
    }
 
    // Add any custom Role properties/code here
}
 
 
// Must be expressed in terms of our custom types:
public class ApplicationDbContext 
    : IdentityDbContext<ApplicationUser, ApplicationRole, 
    string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }
 
    static ApplicationDbContext()
    {
        Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
    }
 
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
    // Add additional items here as needed
}
 
// Most likely won't need to customize these either, but they were needed because we implemented
// custom versions of all the other types:
public class ApplicationUserStore 
    :UserStore<ApplicationUser, ApplicationRole, string,
        ApplicationUserLogin, ApplicationUserRole, 
        ApplicationUserClaim>, IUserStore<ApplicationUser, string>, 
    IDisposable
{
    public ApplicationUserStore()
        : this(new IdentityDbContext())
    {
        base.DisposeContext = true;
    }
 
    public ApplicationUserStore(DbContext context)
        : base(context)
    {
    }
}
 
 
public class ApplicationRoleStore
: RoleStore<ApplicationRole, string, ApplicationUserRole>,
IQueryableRoleStore<ApplicationRole, string>,
IRoleStore<ApplicationRole, string>, IDisposable
{
    public ApplicationRoleStore()
        : base(new IdentityDbContext())
    {
        base.DisposeContext = true;
    }
 
    public ApplicationRoleStore(DbContext context)
        : base(context)
    {
    }
}

 

Now, we need to add a few additional using statements at the top of the code file to bring in some references we need with the new code. Add the following to the using statements at the top of the file:

Additional Using Statements Added to IdentityModels.cs:
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
using System;

 

There are two immediate problems with the code we just pasted in there. The first is probably obvious, because the VS compiler is probably telling you that there is no DBInitializer class defined. Also, if you Build the project, a few other problems will surface in the VS error list. We'll take care of that in a moment.

The other is not so obvious. The code we pasted in here is from an MVC project. For the most part this is fine. However, our ApplicationUser class defines a method GenerateUserIdentityAsync. The code we stole from our MVC project has this method, but defines it in terms of a single parameter of type ApplicationUserManager. Recall the code we pasted over, which defined ApplicationUser with two constructor parameters. The missing parameter in our newly copied method is of type string, and represents the authenticationType.

This is important, because GenerateUserIdentityAsync is called when we need to retrieve a user's ClaimsIdentity, which represents the various claims the specific user has within our system.

Confused yet? We don't need to worry about the details of ClaimsIdentity just yet. What we DO need to do is update the GenerateUserIdentityAsync method defined on ApplicationUser to accept a string parameter representing the authenticationType.

Update ApplicationUser for Web Api

To make our ApplicationUser class ready for use in a Web Api context, we can replace the code for the GenerateUserIdentityAsync  method with the following:

Update GenerateUserIdentityAsync with Authentication Type Parameter:
// ** Add authenticationtype as method parameter:
public async Task<ClaimsIdentity>
    GenerateUserIdentityAsync(ApplicationUserManager manager, string authenticationType)
{
    // Note the authenticationType must match the one defined 
    // in CookieAuthenticationOptions.AuthenticationType
    var userIdentity = 
        await manager.CreateIdentityAsync(this, authenticationType);
    // Add custom user claims here
    return userIdentity;
}

 

                            Adding a DBInitializer and Other Identity Config Items

                            We mentioned earlier, and the compiler is helpfully pointing out to you, that the code we stole from the Identity Extensible Template project is attempting to use a DBInitializer class that doesn't exist (yet) in our Web Api project. Also, you probably notice (if you have built the project since adding the additional Identity Models), that there appear to be some problems with our new ApplicationUser class.

                            We will resolve most of these issues by once again stealing select bits of code from the Identity Extensible Template project.

                            If we look at the App_Start => IdentityConfig.cs file in our Web Api project, we see that, as with the original IdentityModels.cs file, there is not much there:

                            The Default Identity.config File from a Web Api Project:
                            using System.Threading.Tasks;
                            
                            using Microsoft.AspNet.Identity;
                            
                            using Microsoft.AspNet.Identity.EntityFramework;
                            
                            using Microsoft.AspNet.Identity.Owin;
                            
                            using Microsoft.Owin;
                            
                            using AspNetIdentity2WebApiCustomize.Models;
                            
                             
                            
                            namespace AspNetIdentity2WebApiCustomize
                            
                            {
                            
                                // Configure the application user manager used in this application. UserManager 
                            
                                // is defined in ASP.NET Identity and is used by the application.
                            
                                public class ApplicationUserManager : UserManager<ApplicationUser>
                            
                                {
                            
                                    public ApplicationUserManager(IUserStore<ApplicationUser> store)
                            
                                        : base(store)
                            
                                    {
                            
                                    }
                            
                             
                            
                                    public static ApplicationUserManager Create(
                            
                                            IdentityFactoryOptions<ApplicationUserManager> options, 
                            
                                            IOwinContext context)
                            
                                    {
                            
                                        var manager = 
                            
                                                new ApplicationUserManager(
                            
                                                        new UserStore<ApplicationUser>(
                            
                                                                context.Get<ApplicationDbContext>()));
                            
                             
                            
                                        // Configure validation logic for usernames
                            
                                        manager.UserValidator = new UserValidator<ApplicationUser>(manager)
                            
                                        {
                            
                                            AllowOnlyAlphanumericUserNames = false,
                            
                                            RequireUniqueEmail = true
                            
                                        };
                            
                                        // Configure validation logic for passwords
                            
                                        manager.PasswordValidator = new PasswordValidator
                            
                                        {
                            
                                            RequiredLength = 6,
                            
                                            RequireNonLetterOrDigit = true,
                            
                                            RequireDigit = true,
                            
                                            RequireLowercase = true,
                            
                                            RequireUppercase = true,
                            
                                        };
                            
                                        var dataProtectionProvider = options.DataProtectionProvider;
                            
                                        if (dataProtectionProvider != null)
                            
                                        {
                            
                                            manager.UserTokenProvider = 
                            
                                                    new DataProtectorTokenProvider<ApplicationUser>(
                            
                                                            dataProtectionProvider.Create("ASP.NET Identity"));
                            
                                        }
                            
                                        return manager;
                            
                                    }
                            
                                }
                            
                            }

                             

                            In order to work with Roles in our Web Api project, we will need an ApplicationRoleManager, and as mentioned previously, we will be adding the ApplicationDbInitializer from the Extensible Template project.

                            First, we need the following using statements at the top of the IdentityConfig.cs file:

                            Using Statements for the IdentityConfig.cs File:
                            using AspNetIdentity2WebApiCustomize.Models;
                            
                            using Microsoft.AspNet.Identity;
                            
                            using Microsoft.AspNet.Identity.EntityFramework;
                            
                            using Microsoft.AspNet.Identity.Owin;
                            
                            using Microsoft.Owin;
                            
                            using System.Data.Entity;
                            
                            using System.Web;

                             

                            Now, add the ApplicationRoleManager and ApplicationDbInitializer classes from the Extensible Template project to our IdentityConfig.cs file:

                            Add ApplicationRoleManager and ApplicationDbInitializer to IdentityConfig.cs:
                            using AspNetIdentity2WebApiCustomize.Models;
                            
                            using Microsoft.AspNet.Identity;
                            
                            using Microsoft.AspNet.Identity.EntityFramework;
                            
                            using Microsoft.AspNet.Identity.Owin;
                            
                            using Microsoft.Owin;
                            
                            using System.Data.Entity;
                            
                            using System.Web;
                            
                             
                            
                             
                            
                            namespace AspNetIdentity2WebApiCustomize
                            
                            {
                            
                                public class ApplicationUserManager : UserManager<ApplicationUser>
                            
                                {
                            
                                    public ApplicationUserManager(IUserStore<ApplicationUser> store)
                            
                                        : base(store)
                            
                                    {
                            
                                    }
                            
                             
                            
                                    public static ApplicationUserManager Create(
                            
                                        IdentityFactoryOptions<ApplicationUserManager> options, 
                            
                                        IOwinContext context)
                            
                                    {
                            
                                        var manager = new ApplicationUserManager(
                            
                                            new UserStore<ApplicationUser>(
                            
                                                context.Get<ApplicationDbContext>()));
                            
                             
                            
                                        // Configure validation logic for usernames
                            
                                        manager.UserValidator = new UserValidator<ApplicationUser>(manager)
                            
                                        {
                            
                                            AllowOnlyAlphanumericUserNames = false,
                            
                                            RequireUniqueEmail = true
                            
                                        };
                            
                                        // Configure validation logic for passwords
                            
                                        manager.PasswordValidator = new PasswordValidator
                            
                                        {
                            
                                            RequiredLength = 6,
                            
                                            RequireNonLetterOrDigit = true,
                            
                                            RequireDigit = true,
                            
                                            RequireLowercase = true,
                            
                                            RequireUppercase = true,
                            
                                        };
                            
                                        var dataProtectionProvider = options.DataProtectionProvider;
                            
                                        if (dataProtectionProvider != null)
                            
                                        {
                            
                                            manager.UserTokenProvider = 
                            
                                                new DataProtectorTokenProvider<ApplicationUser>(
                            
                                                    dataProtectionProvider.Create("ASP.NET Identity"));
                            
                                        }
                            
                                        return manager;
                            
                                    }
                            
                                }
                            
                             
                            
                             
                            
                                public class ApplicationRoleManager : RoleManager<ApplicationRole>
                            
                                {
                            
                                    public ApplicationRoleManager(IRoleStore<ApplicationRole, string> roleStore)
                            
                                        : base(roleStore)
                            
                                    {
                            
                                    }
                            
                             
                            
                                    public static ApplicationRoleManager Create(
                            
                                        IdentityFactoryOptions<ApplicationRoleManager> options, 
                            
                                        IOwinContext context)
                            
                                    {
                            
                                        return new ApplicationRoleManager(
                            
                                            new ApplicationRoleStore(context.Get<ApplicationDbContext>()));
                            
                                    }
                            
                                }
                            
                             
                            
                             
                            
                                public class ApplicationDbInitializer 
                            
                                    : DropCreateDatabaseAlways<ApplicationDbContext>
                            
                                {
                            
                                    protected override void Seed(ApplicationDbContext context)
                            
                                    {
                            
                                        InitializeIdentityForEF(context);
                            
                                        base.Seed(context);
                            
                                    }
                            
                             
                            
                                    //Create User=Admin@Admin.com with password=Admin@123456 in the Admin role        
                            
                                    public static void InitializeIdentityForEF(ApplicationDbContext db)
                            
                                    {
                            
                                        var userManager = HttpContext.Current
                            
                                            .GetOwinContext().GetUserManager<ApplicationUserManager>();
                            
                             
                            
                                        var roleManager = HttpContext.Current
                            
                                            .GetOwinContext().Get<ApplicationRoleManager>();
                            
                             
                            
                                        const string name = "admin@example.com";
                            
                                        const string password = "Admin@123456";
                            
                                        const string roleName = "Admin";
                            
                             
                            
                                        //Create Role Admin if it does not exist
                            
                                        var role = roleManager.FindByName(roleName);
                            
                                        if (role == null)
                            
                                        {
                            
                                            role = new ApplicationRole(roleName);
                            
                                            var roleresult = roleManager.Create(role);
                            
                                        }
                            
                             
                            
                                        var user = userManager.FindByName(name);
                            
                                        if (user == null)
                            
                                        {
                            
                                            user = new ApplicationUser { UserName = name, Email = name };
                            
                                            var result = userManager.Create(user, password);
                            
                                            result = userManager.SetLockoutEnabled(user.Id, false);
                            
                                        }
                            
                             
                            
                                        // Add user admin to Role Admin if not already added
                            
                                        var rolesForUser = userManager.GetRoles(user.Id);
                            
                                        if (!rolesForUser.Contains(role.Name))
                            
                                        {
                            
                                            var result = userManager.AddToRole(user.Id, role.Name);
                            
                                        }
                            
                                    }
                            
                                }
                            
                            }

                             

                            Now, we need to make a few changes to our ApplicationUserManager. Since we have added our customizable models, including modified versions of UserStore and RoleStore, we need to adapt the ApplicationUserManager to play nice. We have expressed our models with different type arguments than the default implementation expected by the Web Api project. Specifically, we have employed a customized implementation of IUserStore. Rather than the concrete UserStore defined in Microsoft.AspNet.Identity.EntityFramework, we have implemented our own ApplicationUserStore, which is expressed in terms of specific type arguments.

                            We now need to tune up our ApplicationUserManager to work with our ApplicationUserStore.

                            Change the code for ApplicationUserManager in IdentityConfig.cs to the following:

                            Modified ApplicationUserManager:
                            public class ApplicationUserManager 
                            
                                : UserManager<ApplicationUser, string>
                            
                            {
                            
                                public ApplicationUserManager(IUserStore<ApplicationUser, string> store)
                            
                                    : base(store)
                            
                                {
                            
                                }
                            
                             
                            
                                public static ApplicationUserManager Create(
                            
                                    IdentityFactoryOptions<ApplicationUserManager> options, 
                            
                                    IOwinContext context)
                            
                                {
                            
                                    var manager = new ApplicationUserManager(
                            
                                        new UserStore<ApplicationUser, ApplicationRole, string, 
                            
                                            ApplicationUserLogin, ApplicationUserRole, 
                            
                                            ApplicationUserClaim>(context.Get<ApplicationDbContext>()));
                            
                             
                            
                                    // Configure validation logic for usernames
                            
                                    manager.UserValidator = new UserValidator<ApplicationUser>(manager)
                            
                                    {
                            
                                        AllowOnlyAlphanumericUserNames = false,
                            
                                        RequireUniqueEmail = true
                            
                                    };
                            
                             
                            
                                    // Configure validation logic for passwords
                            
                                    manager.PasswordValidator = new PasswordValidator
                            
                                    {
                            
                                        RequiredLength = 6,
                            
                                        RequireNonLetterOrDigit = true,
                            
                                        RequireDigit = true,
                            
                                        RequireLowercase = true,
                            
                                        RequireUppercase = true,
                            
                                    };
                            
                                    var dataProtectionProvider = options.DataProtectionProvider;
                            
                                    if (dataProtectionProvider != null)
                            
                                    {
                            
                                        manager.UserTokenProvider = 
                            
                                            new DataProtectorTokenProvider<ApplicationUser>(
                            
                                                dataProtectionProvider.Create("ASP.NET Identity"));
                            
                                    }
                            
                                    return manager;
                            
                                }
                            
                            }

                             

                            With that, the very minimal basics are in place for us to use our new, extensible model classes (including Roles, which were not directly available to us in the default Web Api implementation) in our Web Api project.

                            We do need to clean up one more issue though. The AccountController still appears to rely on Microsoft.AspNet.Identity.EntityFramework.IdentityUser, and we need it to use our new implementation ApplicationUser.

                            Modify AccountController to Use ApplicationUser

                            We can easily correct this last remaining issue. Open the AccountController class, and locate the GetManageInfo() method. We can see where a local variable user is declared, explicitly types as IdentityUser.

                            Below that, we can see in the foreach() loop, we explicitly declare an iterator variable linkedAccount as type IdentityUserLogin.

                            Existing Code in the Web Api AccountController GetManageInfo() Method:
                            [Route("ManageInfo")]
                            
                            public async Task<ManageInfoViewModel> GetManageInfo(
                            
                                string returnUrl, bool generateState = false)
                            
                            {
                            
                                IdentityUser user = 
                            
                                    await UserManager.FindByIdAsync(User.Identity.GetUserId());
                            
                                if (user == null)
                            
                                {
                            
                                    return null;
                            
                                }
                            
                             
                            
                                List<UserLoginInfoViewModel> logins = new List<UserLoginInfoViewModel>();
                            
                                foreach (IdentityUserLogin linkedAccount in user.Logins)
                            
                                {
                            
                                    logins.Add(new UserLoginInfoViewModel
                            
                                    {
                            
                                        LoginProvider = linkedAccount.LoginProvider,
                            
                                        ProviderKey = linkedAccount.ProviderKey
                            
                                    });
                            
                                }
                            
                             
                            
                                if (user.PasswordHash != null)
                            
                                {
                            
                                    logins.Add(new UserLoginInfoViewModel
                            
                                    {
                            
                                        LoginProvider = LocalLoginProvider,
                            
                                        ProviderKey = user.UserName,
                            
                                    });
                            
                                }
                            
                             
                            
                                return new ManageInfoViewModel
                            
                                {
                            
                                    LocalLoginProvider = LocalLoginProvider,
                            
                                    Email = user.UserName,
                            
                                    Logins = logins,
                            
                                    ExternalLoginProviders = GetExternalLogins(returnUrl, generateState)
                            
                                };
                            
                            }

                             

                            In both cases we have implemented our own versions of these types. Here, we can either change the declaration in each case to use the var keyword, which relieves us of the type constraint on the variable (but, some would argue, makes our code a bit ambiguous), or we can change the explicit type declaration in each case to use our own implementation.

                            For now, let's change the explicit type declaration to use our own implementations:

                            Modified Code for GetManageInfo() Method:
                            [Route("ManageInfo")]
                            
                            public async Task<ManageInfoViewModel> GetManageInfo(
                            
                                string returnUrl, bool generateState = false)
                            
                            {
                            
                                ApplicationUser user = 
                            
                                    await UserManager.FindByIdAsync(User.Identity.GetUserId());
                            
                                if (user == null)
                            
                                {
                            
                                    return null;
                            
                                }
                            
                             
                            
                                List<UserLoginInfoViewModel> logins = new List<UserLoginInfoViewModel>();
                            
                                foreach (ApplicationUserLogin linkedAccount in user.Logins)
                            
                                {
                            
                                    logins.Add(new UserLoginInfoViewModel
                            
                                    {
                            
                                        LoginProvider = linkedAccount.LoginProvider,
                            
                                        ProviderKey = linkedAccount.ProviderKey
                            
                                    });
                            
                                }
                            
                             
                            
                                if (user.PasswordHash != null)
                            
                                {
                            
                                    logins.Add(new UserLoginInfoViewModel
                            
                                    {
                            
                                        LoginProvider = LocalLoginProvider,
                            
                                        ProviderKey = user.UserName,
                            
                                    });
                            
                                }
                            
                             
                            
                                return new ManageInfoViewModel
                            
                                {
                            
                                    LocalLoginProvider = LocalLoginProvider,
                            
                                    Email = user.UserName,
                            
                                    Logins = logins,
                            
                                    ExternalLoginProviders = GetExternalLogins(returnUrl, generateState)
                            
                                };
                            
                            }

                             

                            Above, we have simply changed the declared type for the local user variable from IdentityUser to ApplicationUser, and the iterator variable linkedAccount from IdentityUserLogin to ApplicationUserLogin.

                            Add Initialization for ApplicationRoleManager in Startup.Auth

                            Recall from our high-level exploration of ASP.NET Web Api and Identity that initialization and configuration of Identity 2.0 occurs in the Startup class defined in App_Start => Startup.Auth.

                            As we have seen, the original VS Web Api template did not really provide for Role-Based anything, and consequently, provides no configuration or initialization for our recently added ApplicationRoleManager at startup. We need to add a line of initialization code to our Startup.Auth file:

                            Add Initialization for ApplicationRoleManager in Statup.Auth:
                            public void ConfigureAuth(IAppBuilder app)
                            
                            {
                            
                                app.CreatePerOwinContext(ApplicationDbContext.Create);
                            
                                app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
                            
                                app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
                            
                             
                            
                                app.UseCookieAuthentication(new CookieAuthenticationOptions());
                            
                                app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
                            
                             
                            
                                // Configure the application for OAuth based flow
                            
                                PublicClientId = "self";
                            
                                OAuthOptions = new OAuthAuthorizationServerOptions
                            
                                {
                            
                                    TokenEndpointPath = new PathString("/Token"),
                            
                                    Provider = new ApplicationOAuthProvider(PublicClientId),
                            
                                    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
                            
                                    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
                            
                                    AllowInsecureHttp = true
                            
                                };
                            
                             
                            
                                // Enable the application to use bearer tokens to authenticate users
                            
                                app.UseOAuthBearerTokens(OAuthOptions);
                            
                             
                            
                                // ... Code for third-part logins omitted for brevity ...
                            
                            }

                             

                            We've added a single line, which initializes an instance of ApplicationRoleManager for each incoming request.

                            About the ApplicationDbInitializer

                            With the changes we've introduced so far, we should be able to take our new and improved Web Api project for a test spin to see if the most basic functionality works.

                            Before we do, though, we need to recognize that we have fundamentally changed how the EF/Code-First database generation has been changed with the introduction of our custom ApplicationDbInitializer.

                            Recall from our explorations of customizing an MVC project with extensible models, the ApplicationDbInitializer allows us to specify some options for how and when the database behind our application is generated, and to provide some initial data to work with.

                            As we move towards Role-Based Authorization and a more restrictive security model for our Api, this becomes important.

                            The way we currently have ApplicationDbInitializer configured, it derives from DbDropCreateDatabaseAlways, which means every time we run our application, the backing store will be destroyed, and re-created from scratch. We also have it set up to create a default User, and we assign that user to the Admin role. In this manner, we start our application with a user with Admin-level access permissions.

                            The default VS Web Api project doesn't take advantage of this out of the box. If we look at the class declaration for AccountController we see that the class itself is decorated with a simple [Authorize] attribute. What this essentially does is restrict access to all of the Action methods on the class to authorized users (except those methods specifically decorated with an [AllowAnonymous] attribute).

                            In other words, for now, any user who is registered, and who successfully signs in and presents a valid Bearer Token can access any of the Action methods on AccountController.

                            We'll take a closer look at implementing Role-Based Authentication momentarily, First, we will extend our ApplicationUser and ApplicationRole classes with some custom properties.

                            Adding Custom Properties to ApplicationUser and ApplicationRole

                            As we saw when we examine customizing Users and Roles within an MVC project, we will add a few simple properties to our ApplicationUser and ApplicationRole models. Modify the code for each as follows:

                            Add Custom Properties to ApplicationUser and ApplicationRole:
                            // Must be expressed in terms of our custom Role and other types:
                            
                            public class ApplicationUser
                            
                                : IdentityUser<string, ApplicationUserLogin,
                            
                                ApplicationUserRole, ApplicationUserClaim> 
                            
                            {
                            
                                public ApplicationUser()
                            
                                {
                            
                                    this.Id = Guid.NewGuid().ToString();
                            
                                }
                            
                             
                            
                             
                            
                                public async Task<ClaimsIdentity>GenerateUserIdentityAsync(
                            
                                    ApplicationUserManager manager, string authenticationType)
                            
                                {
                            
                                    // Note the authenticationType must match the one 
                            
                                    // defined in CookieAuthenticationOptions.AuthenticationType
                            
                                    var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
                            
                             
                            
                                    // Add custom user claims here
                            
                                    return userIdentity;
                            
                                }
                            
                             
                            
                                // Add Custom Properties:
                            
                                public string Address { get; set; }
                            
                                public string City { get; set; }
                            
                                public string PostalCode { get; set; }
                            
                            }
                            
                             
                            
                             
                            
                            // Must be expressed in terms of our custom UserRole:
                            
                            public class ApplicationRole : IdentityRole<string, ApplicationUserRole> 
                            
                            {
                            
                                public ApplicationRole() 
                            
                                {
                            
                                  this.Id = Guid.NewGuid().ToString();
                            
                                }
                            
                             
                            
                                public ApplicationRole(string name) : this() 
                            
                                {
                            
                                  this.Name = name;
                            
                                }
                            
                             
                            
                                // Add Custom Property:
                            
                                public string Description { get; set; }
                            
                            }

                             

                            Here, we have added an Address and related properties to ApplicationUser, and a simple Description property to ApplicationRole.

                            Now, let's update our ApplicationDbInitializer to set some sample values for these new properties. Update the code for the InitializeIdentityForEF() method as follows:

                            Set Initial Values for Custom Properties in ApplicationDbInitializer:
                            public static void InitializeIdentityForEF(ApplicationDbContext db)
                            
                            {
                            
                                var userManager = HttpContext.Current
                            
                                    .GetOwinContext().GetUserManager<ApplicationUserManager>();
                            
                             
                            
                                var roleManager = HttpContext.Current
                            
                                    .GetOwinContext().Get<ApplicationRoleManager>();
                            
                             
                            
                                const string name = "admin@example.com";
                            
                                const string password = "Admin@123456";
                            
                             
                            
                                // Some initial values for custom properties:
                            
                                const string address = "1234 Sesame Street";
                            
                                const string city = "Portland";
                            
                                const string state = "OR";
                            
                                const string postalCode = "97209";
                            
                             
                            
                                const string roleName = "Admin";
                            
                                const string roleDescription = "All access pass";
                            
                             
                            
                                //Create Role Admin if it does not exist
                            
                                var role = roleManager.FindByName(roleName);
                            
                                if (role == null)
                            
                                {
                            
                                    role = new ApplicationRole(roleName);
                            
                             
                            
                                    // Set the new custom property:
                            
                                    role.Description = roleDescription;
                            
                                    var roleresult = roleManager.Create(role);
                            
                                }
                            
                             
                            
                                var user = userManager.FindByName(name);
                            
                                if (user == null)
                            
                                {
                            
                                    user = new ApplicationUser { UserName = name, Email = name };
                            
                             
                            
                                    // Set the new custom properties:
                            
                                    user.Address = address;
                            
                                    user.City = city;
                            
                                    user.State = state;
                            
                                    user.PostalCode = postalCode;
                            
                             
                            
                                    var result = userManager.Create(user, password);
                            
                                    result = userManager.SetLockoutEnabled(user.Id, false);
                            
                                }
                            
                             
                            
                                // Add user admin to Role Admin if not already added
                            
                                var rolesForUser = userManager.GetRoles(user.Id);
                            
                                if (!rolesForUser.Contains(role.Name))
                            
                                {
                            
                                    var result = userManager.AddToRole(user.Id, role.Name);
                            
                                }
                            
                            }

                             

                            With that, we should be ready to see if everything at least works correctly . . .

                            Create a Simple Web Api Client Application

                            To see if everything is working properly to this point, we will create a simple console application as an Api client.

                            In Visual Studio, create a new Console Application, and the use the Manage Nuget Packages for Solutions to add the Microsoft Asp.NET Web Api 2.2 Client Libraries, or use the Package Manager Console and do:

                            Add Web Api 2.2 via the Nuget Package Manager Console:
                            PM> Install-Package Microsoft.AspNet.WebApi.Client

                             

                            Now that we have the required Web Api Client Libraries in our project, open the Program.cs file.

                            Make sure the following using statements are present at the top of the file. Note we have added references to System.Net.Http and Newtonsoft.Json, ad well as System.Threading:

                            Required Using Statements for Console Api Client Application:
                            using System;
                            
                            using System.Collections.Generic;
                            
                            using System.Linq;
                            
                            using System.Text;
                            
                            using System.Threading.Tasks;
                            
                            using System.Net.Http;
                            
                            using Newtonsoft.Json;

                             

                            Next, let's add some very basic client code used to retrieve a token from the token endpoint of our Web Api. Add the following within the Program class:

                            Add Client Code to Retreive Response from Web Api Token Endpoint:
                            // You will need to substitute your own host Url here:
                            
                            static string host = "http://localhost:63074/";
                            
                             
                            
                            static void Main(string[] args)
                            
                            {
                            
                                Console.WriteLine("Attempting to Log in with default admin user");
                            
                             
                            
                                // Get hold of a Dictionary representing the JSON in the response Body:
                            
                                var responseDictionary = 
                            
                                    GetResponseAsDictionary("admin@example.com", "Admin@123456");
                            
                                foreach(var kvp in responseDictionary)
                            
                                {
                            
                                    Console.WriteLine("{0}: {1}", kvp.Key, kvp.Value);
                            
                                }
                            
                                Console.Read();
                            
                            }
                            
                             
                            
                             
                            
                            static Dictionary<string, string> GetResponseAsDictionary(
                            
                                string userName, string password)
                            
                            {
                            
                                HttpClient client = new HttpClient();
                            
                                var pairs = new List<KeyValuePair<string, string>>
                            
                                            {
                            
                                                new KeyValuePair<string, string>( "grant_type", "password" ), 
                            
                                                new KeyValuePair<string, string>( "username", userName ), 
                            
                                                new KeyValuePair<string, string> ( "Password", password )
                            
                                            };
                            
                                var content = new FormUrlEncodedContent(pairs);
                            
                             
                            
                                // Attempt to get a token from the token endpoint of the Web Api host:
                            
                                HttpResponseMessage response =
                            
                                    client.PostAsync(host + "Token", content).Result;
                            
                                var result = response.Content.ReadAsStringAsync().Result;
                            
                                // De-Serialize into a dictionary and return:
                            
                                Dictionary<string, string> tokenDictionary =
                            
                                    JsonConvert.DeserializeObject<Dictionary<string, string>>(result);
                            
                                return tokenDictionary;
                            
                            }
                            

                             

                            Note, at this point, we have added code the the Main() method, which calls out to a single, rather contrived method GetResponseAsDictionary() . All we are basically doing here is submitting an HTTP POST to the Token endpoint of our Web Api, and then de-serializing the JSON response body into a Dictionary<string, string> .

                            Once we have the Dictionary, we are iterating over each Key/Value pair, and writing the contents to the Console.

                            If everything has worked the way we expect, our Console output should be something along the lines of the following:

                            Console Output from Token Endpoint Response:
                            Attempting to Log in with default admin user
                            
                            access_token: AuzOQkgG3BYubP1rlljcPhAzW7R7gA4Vew8dHy_MScMn2-Rs3R6dNlwCU_SuFwKveq
                            
                            uf5rflB7PCfamlcT_-KJ4q3lfx7kiFNpSF9SdMLwKP_mCSOXGbrxrK3jXfH7bum3sZdl7w8k5irLa27i
                            
                            Bvp_RqtXgkSmgpcNWitCU8RBz7aOaHr8r-FCklg4wUkLNE26qlR6Sl42DAAiBZNLpUZUt-M7vaOs8TZB
                            
                            W4YehAzrqFAuTX3peMJBQB8K8_XxaTkRnEhSEMz9DnUnqzQjjVr5rnSdFSGxQmrQA8dBBwq4RaUfwbCU
                            
                            7au787CMn7EGiDO9KRcGHAsGHOJqb8P8Z7A-ssV7tfEqJayrNH-F_Z2p5kiasDODQrG53CZNUE0vuDT6
                            
                            Fp4_xOavE6wkYcHTfXWZJWFEMokE4NB9mtAl3lReYSZQyzKkcHWFNQCMAj3LoNGSdnEVVM_jzZtRSfWj
                            
                            IG2OmhyR1wZNRCHY_6NwEMOIHGLpA_L-kFFAJPgwQWi-WljeV-X2KiMQIeYlGGdskaNw
                            
                            token_type: bearer
                            
                            expires_in: 1209599
                            
                            userName: admin@example.com
                            
                            .issued: Sun, 26 Oct 2014 13:21:03 GMT
                            
                            .expires: Sun, 09 Nov 2014 13:21:03 GMT

                             

                            We've seen this before, in our overview article. The de-serialized JSON above represents the content of the response to our POST to the Token endpoint of our Web Api. The important part of the response is and access_token itself.

                            This Doesn't Look Any Different Than Before . . .

                            At this point, that de-serialized JSON response doesn't look any different than it did in our previous post, before we added all our fancy new Roles and custom properties. Shouldn't it have some new information in it now? Roles, and Addresses and stuff?

                            No.

                            A Little More On the Nature of Bearer Tokens (but only a little)

                            We had a really, really brief look Bearer Tokens in our Introduction to Identity in Web Api. As mentioned there, we will undertake a more in-depth exploration of Tokens in another post.

                            For our purposes here today, we are simply going to expand a little on what we learned previously, sufficient to understand how the access_token we retrieved as part of our JSON response above fits into the scheme of our newly modified Web Api project.

                            Bearer Tokens are, by design, "opaque" to the client. In other words, they are encoded (and sometimes encrypted) on the server, and can be decoded (and potentially decrypted) by the server. They are not designed to be decoded/decrypted by the client.

                            Recall from exploring the basic structure of an ASP.NET Web Api project, the ApplicationOauthProvider class, defined in the Providers => ApplicationOauthProvider.cs file.

                            When you POST a request to the Token endpoint of the ASP.NET Web Api application (at least, in the way we have it configured here), the server validates the credentials you present (in this case, user name + Password) by calling the GrantResourceOwnersCredentials() method defined on the ApplicationOauthProvider class:

                            The GrantResourceOwnersCredentials() Method from ApplicationOAuthProvider:
                            public override async Task GrantResourceOwnerCredentials(
                            
                                OAuthGrantResourceOwnerCredentialsContext context)
                            
                            {
                            
                                var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
                            
                                ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
                            
                             
                            
                                if (user == null)
                            
                                {
                            
                                    context.SetError("invalid_grant", "The user name or password is incorrect.");
                            
                                    return;
                            
                                }
                            
                             
                            
                                ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
                            
                                   OAuthDefaults.AuthenticationType);
                            
                                ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
                            
                                    CookieAuthenticationDefaults.AuthenticationType);
                            
                             
                            
                                AuthenticationProperties properties = CreateProperties(user.UserName);
                            
                                AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
                            
                                context.Validated(ticket);
                            
                                context.Request.Context.Authentication.SignIn(cookiesIdentity);
                            
                            }

                             

                            We can see that this code attempts to find a user with credentials matching those presented. If a valid user is found, the method calls the GenerateUserIdentityAsync() method defined on the ApplicationUser class to obtain an instance of ClaimsIdentity representing the user, and any claims the user may make within our system.

                            The ClaimsIdentity is then used to create an AuthenticationTicket.

                            In the code for GrantResourceOwnersCredentials(), above, when Validated() is called on the OAuthGrantResourceOwnerCredentialsContext, the OWIN middleware serializes the ClaimsIdentity into an encoded and signed token, to be returned in the HTTP Response header as a result of our request.

                            All of the above is a long-winded way of saying, the important user information, including user roles, and anything else we decide needs to be a part of our token, is present in the token when it is received by the client.

                            The Client just can't get to it.

                            A Note About Bearer Tokens and Security

                            As mentioned in our previous posts, using bearer tokens, and submitting credentials to obtain the token from the token endpoint should only be done over SSL/STL in a production system. Bearer tokens are exactly what their name implies - anyone presenting a valid bearer token will be granted access to the system, with whatever privileges the actual "owner" of the token has.

                            OAuth Bearer tokens represent a fairly simple authentication/authorization scheme, In building out your Web Api, consider all of the alternatives, and make the best choice for your application.

                            As mentioned previously, we will take a more thorough look at Claims Identity, and token authentication/authorization in later posts.

                            Adding Role-Based Authorization to the Web Api Application

                            Now that we understand how to authenticate ourselves using a bearer token, let's look at how we can use our new Role-Based Authorization capability within our application.

                            To start, let's look at the two primary controllers present in the Web Api application, AccountController and ValuesController (we are ignoring the HomeController, since it serves no purpose for our needs here).

                            We've already seen that AccountController is decorated with an [Authorize] attribute. This means that only authenticated users may access the action methods defined on this controller (unless, of course, the method itself is decorated with an [AllowAnonymous] attribute).

                            Let's look at the simplistic ValuesController. ValuesController is provided as a simple example of how one might add a basic CRUD-style functionality. ValuesController is similarly decorated with the [Authorize] attribute. Again, only authenticated users are able to access the Action methods defined on ValuesController.

                            As we saw in previous posts about implementing Role-Based Authorization in an MVC project, we can modify the access permissions for our Web Api, at either the Controller level, or the Action method level, by expanding on our use of [Authorize] .

                            Consider, we might want to restrict access to AccountController only to users who are in the Admin role, but allow access to ValuesController, and the functionality it provides, to any authenticated user.

                            In this case, we will want to make some modifications to our Web Api configuration.

                            Add a Vanilla Users Role as a Default in ApplicationDbInitializer

                            First, let's make sure we have two distinct Roles available in our application - the "Admin" role we already create as an initial value during configuration, and a new "Users" role. Update the InitializeDatabaseForEF() method as follows:

                            Add a Users Role and a Default User to InitializeDatabaseForEF() Method:
                            public static void InitializeIdentityForEF(ApplicationDbContext db)
                            
                            {
                            
                                var userManager = HttpContext.Current
                            
                                    .GetOwinContext().GetUserManager<ApplicationUserManager>();
                            
                             
                            
                                var roleManager = HttpContext.Current
                            
                                    .GetOwinContext().Get<ApplicationRoleManager>();
                            
                             
                            
                                // Initial Admin user:
                            
                                const string name = "admin@example.com";
                            
                                const string password = "Admin@123456";
                            
                             
                            
                                // Some initial values for custom properties:
                            
                                const string address = "1234 Sesame Street";
                            
                                const string city = "Portland";
                            
                                const string state = "OR";
                            
                                const string postalCode = "97209";
                            
                             
                            
                                const string roleName = "Admin";
                            
                                const string roleDescription = "All access pass";
                            
                             
                            
                                //Create Role Admin if it does not exist
                            
                                var role = roleManager.FindByName(roleName);
                            
                                if (role == null)
                            
                                {
                            
                                    role = new ApplicationRole(roleName);
                            
                             
                            
                                    // Set the new custom property:
                            
                                    role.Description = roleDescription;
                            
                                    var roleresult = roleManager.Create(role);
                            
                                }
                            
                             
                            
                                // Create Admin User:
                            
                                var user = userManager.FindByName(name);
                            
                                if (user == null)
                            
                                {
                            
                                    user = new ApplicationUser { UserName = name, Email = name };
                            
                             
                            
                                    // Set the new custom properties:
                            
                                    user.Address = address;
                            
                                    user.City = city;
                            
                                    user.State = state;
                            
                                    user.PostalCode = postalCode;
                            
                             
                            
                                    var result = userManager.Create(user, password);
                            
                                    result = userManager.SetLockoutEnabled(user.Id, false);
                            
                                }
                            
                             
                            
                                // Add user admin to Role Admin if not already added
                            
                                var rolesForUser = userManager.GetRoles(user.Id);
                            
                                if (!rolesForUser.Contains(role.Name))
                            
                                {
                            
                                    userManager.AddToRole(user.Id, role.Name);
                            
                                }
                            
                             
                            
                                // Initial Vanilla User:
                            
                                const string vanillaUserName = "vanillaUser@example.com";
                            
                                const string vanillaUserPassword = "Vanilla@123456";
                            
                             
                            
                                // Add a plain vannilla Users Role:
                            
                                const string usersRoleName = "Users";
                            
                                const string usersRoleDescription = "Plain vanilla User";
                            
                             
                            
                                //Create Role Users if it does not exist
                            
                                var usersRole = roleManager.FindByName(usersRoleName);
                            
                                if (usersRole == null)
                            
                                {
                            
                                    usersRole = new ApplicationRole(usersRoleName);
                            
                             
                            
                                    // Set the new custom property:
                            
                                    usersRole.Description = usersRoleDescription;
                            
                                    var userRoleresult = roleManager.Create(usersRole);
                            
                                }
                            
                             
                            
                                // Create Vanilla User:
                            
                                var vanillaUser = userManager.FindByName(vanillaUserName);
                            
                                if (vanillaUser == null)
                            
                                {
                            
                                    vanillaUser = new ApplicationUser 
                            
                                    { 
                            
                                        UserName = vanillaUserName, 
                            
                                        Email = vanillaUserName 
                            
                                    };
                            
                             
                            
                                    // Set the new custom properties:
                            
                                    vanillaUser.Address = address;
                            
                                    vanillaUser.City = city;
                            
                                    vanillaUser.State = state;
                            
                                    vanillaUser.PostalCode = postalCode;
                            
                             
                            
                                    var result = userManager.Create(vanillaUser, vanillaUserPassword);
                            
                                    result = userManager.SetLockoutEnabled(vanillaUser.Id, false);
                            
                                }
                            
                             
                            
                                // Add vanilla user to Role Users if not already added
                            
                                var rolesForVanillaUser = userManager.GetRoles(vanillaUser.Id);
                            
                                if (!rolesForVanillaUser.Contains(usersRole.Name))
                            
                                {
                            
                                    userManager.AddToRole(vanillaUser.Id, usersRole.Name);
                            
                                }
                            
                            }
                            

                             

                            Above, we have added a new role "Users" and another initial sample user.

                            Next, let's modify the [Authorize] attribute on our AccountController class, and add a Role argument:

                            Modified [Authorize] Attribute for AccountController:
                            [Authorize(Roles= "Admin")]
                            
                            [RoutePrefix("api/Account")]
                            
                            public class AccountController : ApiController
                            
                            {
                            
                                // ... All the Code ...
                            
                            }

                             

                            Now, we just need to change up our client code to attempt to access some methods from each of the two controllers to see how our Role-Based Authorization is working for us.

                            Modify Client Code to Attempt Controller Access

                            Here, we will simply set up some client code to attempt to retreive some basic data from both AccountController and ValuesController. We will do this as a user in the Admin Role, and then also as a user in the Users Role.

                            Change the code in your Console application to match the following:

                            Modified Client Code to Access Both Controllers with Different Roles:
                            class Program
                            
                            {
                            
                                // You will need to substitute your own host Url here:
                            
                                static string host = "http://localhost:63074/";
                            
                             
                            
                                static void Main(string[] args)
                            
                                {
                            
                                    // Use the User Names/Emails and Passwords we set up in IdentityConfig:
                            
                                    string adminUserName = "admin@example.com";
                            
                                    string adminUserPassword = "Admin@123456";
                            
                             
                            
                                    string vanillaUserName = "vanillaUser@example.com";
                            
                                    string vanillaUserPassword = "Vanilla@123456";
                            
                             
                            
                                    // Use the new GetToken method to get a token for each user:
                            
                                    string adminUserToken = GetToken(adminUserName, adminUserPassword);
                            
                                    string vaniallaUserToken = GetToken(vanillaUserName, vanillaUserPassword);
                            
                             
                            
                                    // Try to get some data as an Admin:
                            
                                    Console.WriteLine("Attempting to get User info as Admin User");
                            
                                    string adminUserInfoResult = GetUserInfo(adminUserToken);
                            
                                    Console.WriteLine("Admin User Info Result: {0}", adminUserInfoResult);
                            
                                    Console.WriteLine("");
                            
                             
                            
                                    Console.WriteLine("Attempting to get Values info as Admin User");
                            
                                    string adminValuesInfoResult = GetValues(adminUserToken);
                            
                                    Console.WriteLine("Admin Values Info Result: {0}", adminValuesInfoResult);
                            
                                    Console.WriteLine("");
                            
                             
                            
                                    // Try to get some data as a plain old user:
                            
                                    Console.WriteLine("Attempting to get User info as Vanilla User");
                            
                                    string vanillaUserInfoResult = GetUserInfo(vaniallaUserToken);
                            
                                    Console.WriteLine("Vanilla User Info Result: {0}", vanillaUserInfoResult);
                            
                                    Console.WriteLine("");
                            
                             
                            
                                    Console.WriteLine("Attempting to get Values info as Vanilla User");
                            
                                    string vanillaValuesInfoResult = GetValues(vaniallaUserToken);
                            
                                    Console.WriteLine("Vanilla Values Info Result: {0}", vanillaValuesInfoResult);
                            
                                    Console.WriteLine("");
                            
                             
                            
                                    Console.Read();
                            
                                }
                            
                             
                            
                             
                            
                                static string GetToken(string userName, string password)
                            
                                {
                            
                                    HttpClient client = new HttpClient();
                            
                                    var pairs = new List<KeyValuePair<string, string>>
                            
                                                {
                            
                                                    new KeyValuePair<string, string>( "grant_type", "password" ), 
                            
                                                    new KeyValuePair<string, string>( "username", userName ), 
                            
                                                    new KeyValuePair<string, string> ( "Password", password )
                            
                                                };
                            
                                    var content = new FormUrlEncodedContent(pairs);
                            
                             
                            
                                    // Attempt to get a token from the token endpoint of the Web Api host:
                            
                                    HttpResponseMessage response =
                            
                                        client.PostAsync(host + "Token", content).Result;
                            
                                    var result = response.Content.ReadAsStringAsync().Result;
                            
                             
                            
                                    // De-Serialize into a dictionary and return:
                            
                                    Dictionary<string, string> tokenDictionary =
                            
                                        JsonConvert.DeserializeObject<Dictionary<string, string>>(result);
                            
                                    return tokenDictionary["access_token"];
                            
                                }
                            
                             
                            
                             
                            
                                static string GetUserInfo(string token)
                            
                                {
                            
                                    using (var client = new HttpClient())
                            
                                    {
                            
                                        client.DefaultRequestHeaders.Authorization =
                            
                                        new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
                            
                                        var response = client.GetAsync(host + "api/Account/UserInfo").Result;
                            
                                        return response.Content.ReadAsStringAsync().Result;
                            
                                    }
                            
                                }
                            
                             
                            
                             
                            
                                static string GetValues(string token)
                            
                                {
                            
                                    using (var client = new HttpClient())
                            
                                    {
                            
                                        client.DefaultRequestHeaders.Authorization =
                            
                                        new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
                            
                             
                            
                                        var response = client.GetAsync(host + "api/Values").Result;
                            
                                        return response.Content.ReadAsStringAsync().Result;
                            
                                    }
                            
                                }
                            
                            }

                             

                            In the above, we changed our code up a bit. We now have a GetToken() method, which accepts a User Name/Password as arguments, and returns only the access_token string from the request to the Token endpoint of our Web Api.

                            Next, we added two different calls to our Web Api. One method calls the GetUserInfo() method on the AccountController, and the other calls the Get() method on our ValuesController.

                            If we spin up our Web Api, and, after it has spun up, run our client application, we should see the following output in our Console:

                            Console Output from Access Permissions Comparison:
                            Attempting to get User info as Admin User
                            
                            Admin User Info Result: {"Email":"admin@example.com","HasRegistered":true,"Login
                            
                            Provider":null}
                            
                            Attempting to get Values info as Admin User
                            
                            Admin Values Info Result: ["value1","value2"]
                            
                            Attempting to get User info as Vanilla User
                            
                            Vanilla User Info Result: {"Message":"Authorization has been denied for this req
                            
                            uest."}
                            
                            Attempting to get Values info as Vanilla User
                            
                            Vanilla Values Info Result: ["value1","value2"]

                             

                            Note the output from that third attempt. We are trying to call into GetUserInfo() as a plain vanilla User, in the Users Role. Appropriately, our Web Api has returned an authorization error, since Users are not allowed access to the method by virtue of the [Authorize(Roles="Admin")] attribute on the class declaration for AccountController.

                            In contrast, both users are able to access the Get() method on ValuesController, since this controller is decorated with a simple [Authorize] attribute, which requires only an authenticated user for access.

                            Accessing Custom User Properties

                            We have also added some custom properties to our ApplicationUserModel. Let's take a look and see if we can work with those in the context of our examples here.

                            If we look more closely at the GetUserInfo() method on AccountController, we find that the actual return type for this method is UserInfoViewModel, which is found in the Models => AccountViewModels.cs file. Now, in our crude, simple Console application we are not going to all the effort of de-serializing the JSON from our GET request into an object, but we COULD.

                            For our purposes, here, it will be sufficient to modify the UserInfoViewModel to reflect the additional properties we want to return, and then update the GetUserInfo() method to suit.

                            Add our custom User properties to the UserInfoViewModel class:
                            public class UserInfoViewModel
                            
                            {
                            
                                public string Email { get; set; }
                            
                                public bool HasRegistered { get; set; }
                            
                                public string LoginProvider { get; set; }
                            
                             
                            
                                // Add our custom properties from ApplicationUser:
                            
                                public string Address { get; set; }
                            
                                public string City { get; set; }
                            
                                public string State { get; set; }
                            
                                public string PostalCode { get; set; }
                            
                            }

                             

                            Next, update the GetUserInfo() method to provide values for the additional properties we just added to UserInfoViewModel:

                            Update GetUserInfo() Method on AccountController with Custom Properties:
                            public UserInfoViewModel GetUserInfo()
                            
                            {
                            
                                ExternalLoginData externalLogin 
                            
                                    = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
                            
                             
                            
                                // We wouldn't normally be likely to do this:
                            
                                var user = UserManager.FindByName(User.Identity.Name);
                            
                                return new UserInfoViewModel
                            
                                {
                            
                                    Email = User.Identity.GetUserName(),
                            
                                    HasRegistered = externalLogin == null,
                            
                                    LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null,
                            
                             
                            
                                    // Pass the custom properties too:
                            
                                    Address = user.Address,
                            
                                    City = user.City,
                            
                                    State = user.State,
                            
                                    PostalCode = user.PostalCode
                            
                                };
                            
                            }

                             

                            Above, we have called out to UserManager to retreive an instance of our user, so we can get at the new properties we have added. We then set the corresponding values on the UserInfoViewModel before returning.

                            This example is a little contrived, and we most likely would NOT do this in a production application this way. But for now, it will serve to demonstrate that our new and improved ApplicationUser indeed has the custom properties we added to the model, and that we are retrieving them from the database as expected.

                            If we run our Console Client application one last time, we should see the following output:

                            Output from the Console Application with Custom User Properties:
                            Attempting to get User info as Admin User
                            
                            Admin User Info Result: {"Email":"admin@example.com","HasRegistered":true,"Login
                            
                            Provider":null,"Address":"1234 Sesame Street","City":"Portland","State":"OR",
                            
                            "PostalCode":"97209"}
                            
                            Attempting to get Values info as Admin User
                            
                            Admin Values Info Result: ["value1","value2"]
                            
                            Attempting to get User info as Vanilla User
                            
                            Vanilla User Info Result: {"Message":"Authorization has been denied for this 
                            
                            request."}
                            
                            Attempting to get Values info as Vanilla User
                            
                            Vanilla Values Info Result: ["value1","value2"]

                             

                            And we see, our custom user properties are returned with the JSON response.

                            Role-Based Authorization Versus Claims-Based Authorization

                            In this post, we have looked briefly at implementing Role-Based Authorization in the context of an ASP.NET Web Api project, and we had the briefest look at how Bearer Tokens work. It is important to note though, that for any but the simplest of authorization/access control schemes, Role-Based Authorization ("RBA") is rapidly being overshadowed by Claims-Based Authorization.

                            Claims-Based Identity offers greater flexibility, and more effectively separates the Authentication and Authorization mechanism from your code. Note, in this example project, we need to specify, as part of the [Authorize] attribute, precisely which Roles are allowed access to which controllers and/or methods.

                            Claims-Based Auth is more complex than RBA, but is generally going to be a more natural fit for a Web Api scenario, unless your needs are fairly simple.

                            We will examine Tokens, and Claims-Based Identity, in an upcoming post.

                            Additional Resources and Items of Interest

                             

                            Posted on October 26 2014 03:23 PM by jatten     

                            Comments (11)

                            ASP.NET Web Api: Unwrapping HTTP Error Results and Model State Dictionaries Client-Side

                            Posted on September 28 2014 08:42 PM by jatten in ASP.Net, ASP.NET MVC, C#, CodeProject   ||   Comments (0)

                            404 on wall-320When working with ASP.NET Web Api from a .NET client, one of the more confounding things can be handling the case where errors are returned from the Api. Specifically, unwrapping the various types of errors which may be returned from a specific API action method, and translating the error content into meaningful information for use be the client.

                            How we handle the various types of errors that may be returned to our Api client applications can be very dependent upon specific application needs, and indeed, the type of client we are building.

                            Image by Damien Roué  |  Some Rights Reserved

                            In this post we'll look at some general types of issues we might run into when handing error results client-side, and hopefully find some insight we can apply to specific cases as they arise.

                            Understanding HTTP Response Creation in the ApiController

                            Most Web Api Action methods will return one of the following:

                            • Void: If the action method returns void, the HTTP Response created by ASP.NET Web Api will have a 204 status code, meaning "no content."
                            • HttpResponseMessage: If the Action method returns an HttpResponseMessage, then the value will be converted directly into an HTTP response message. We can use the Request.CreateResponse() method to create instances of HttpResponseMessage, and we can optionally pass domain models as a method argument, which will then be serialized as part of the resulting HTTP response message.
                            • IHttpActionResult: Introduced with ASP.NET Web API 2.0, the IHttpActionResult interface provides a handy abstraction over the mechanics of creating an HttpResponseMessage. Also, there are a host of pre-defined implementations for IHttpActionResult defined in System.Web.Http.Results, and the ApiController class provides helper methods which return various forms of IHttpActionResult, usable directly within the controller.
                            • Other Type: Any other return type will need to be serialized using an appropriate media formatter.

                            For more details on the above, see Action Results in Web API 2 by Mike Wasson.

                            From Web Api 2.0 onward, the recommended return type for most Web Api Action methods is IHttpActionResult unless this type simply doesn't make sense.

                            Create a New ASP.NET Web Api Project in Visual Studio

                            To keep things general and basic, let's start by spinning up a standard ASP.NET Web Api project using the default Visual Studio Template. If you are new to Web Api, take a moment to review the basics, and get familiar with the project structure and where things live.

                            Make sure to update the Nuget packages after you create the project.

                            Create a Basic Console Client Application

                            Next, let's put together a very rudimentary client application. Open another instance of Visual Studio, and create a new Console application. Then, use the Nuget package manager to install the ASP.NET Web Api Client Libraries into the solution.

                            We're going to use the simple Register() method as our starting point to see how we might need to unwrap some errors in order to create a more useful error handling model on the client side.

                            The Register Method from the Account Controller

                            If we return to our Web Api project and examine the Register() method, we see the following:

                            The Register() method from AccountController:
                            [AllowAnonymous]
                            
                            [Route("Register")]
                            
                            public async Task<IHttpActionResult> Register(RegisterBindingModel model)
                            
                            {
                            
                                if (!ModelState.IsValid)
                            
                                {
                            
                                    return BadRequest(ModelState);
                            
                                }
                            
                             
                            
                                var user = new ApplicationUser() 
                            
                                { 
                            
                                    UserName = model.Email, 
                            
                                    Email = model.Email 
                            
                                };
                            
                             
                            
                                IdentityResult result = await UserManager.CreateAsync(user, model.Password);
                            
                             
                            
                                if (!result.Succeeded)
                            
                                {
                            
                                    return GetErrorResult(result);
                            
                                }
                            
                                return Ok();
                            
                            }

                             

                            In the above, we can see that there are a number of options for what might be returned as our IHttpActionResult.

                            First, if the Model state is invalid, the BadRequest() helper method defined as part of the ApiController class will be called, and will be passed the current ModelStateDictionary. This represents simple validation, and no additional processes or database requests have been called.

                            If the Mode State is valid, the CreateAsync() method of the UserManager is called, returning an IdentityResult. If the Succeeded property is not true, then GetErrorResult() is called, and passed the result of the call to CreateAsync().

                            GetErrorResult() is a handy helper method which returns the appropriate IHttpActionResult for a given error condition.

                            The GetErrorResult Method from AccountController
                            private IHttpActionResult GetErrorResult(IdentityResult result)
                            
                            {
                            
                                if (result == null)
                            
                                {
                            
                                    return InternalServerError();
                            
                                }
                            
                                if (!result.Succeeded)
                            
                                {
                            
                                    if (result.Errors != null)
                            
                                    {
                            
                                        foreach (string error in result.Errors)
                            
                                        {
                            
                                            ModelState.AddModelError("", error);
                            
                                        }
                            
                                    }
                            
                                    if (ModelState.IsValid)
                            
                                    {
                            
                                        // No ModelState errors are available to send, 
                            
                                        // so just return an empty BadRequest.
                            
                                        return BadRequest();
                            
                                    }
                            
                                    return BadRequest(ModelState);
                            
                                }
                            
                                return null;
                            
                            }

                             

                            From the above, we can see we might get back a number of different responses, each with a slightly different content, which should assist the client in determining what went wrong.

                            Making a Flawed Request - Validation Errors

                            So, let's see some of the ways things can go wrong when making a simple POST request to the Register() method from our Console client application.

                            Add the following code to the console application. Note that we are intentionally making a flawed request. We will pass a valid password and a matching confirmation password, but we will pass an invalid email address. We know that Web Api will not like this, and should kick back a Model State Error as a result.

                            Flawed Request Code for the Console Client Application:
                            static void Main(string[] args)
                            
                            {
                            
                                // This is not a valid email address, so the POST should fail:
                            
                                string email = "john";
                            
                                string password = "Password@123";
                            
                                string confirmPassword = "Password@123";
                            
                             
                            
                                HttpResponseMessage result = 
                            
                                    Register(email, password, confirmPassword);
                            
                             
                            
                                if(result.IsSuccessStatusCode)
                            
                                {
                            
                                    Console.WriteLine(
                            
                                        "The new user {0} has been successfully added.", email);
                            
                                }
                            
                                else
                            
                                {
                            
                                    Console.WriteLine(result.ReasonPhrase);
                            
                                }
                            
                                Console.Read();
                            
                            }
                            
                             
                            
                             
                            
                            public static HttpResponseMessage Register(
                            
                                string email, string password, string confirmPassword)
                            
                            {
                            
                                //Attempt to register:
                            
                                using (var client = new HttpClient())
                            
                                {
                            
                                    var response =
                            
                                        client.PostAsJsonAsync("http://localhost:51137/api/Account/Register",
                            
                             
                            
                                        // Pass in an anonymous object that maps to the expected 
                            
                                        // RegisterUserBindingModel defined as the method parameter 
                            
                                        // for the Register method on the API:
                            
                                        new
                            
                                        {
                            
                                            Email = email,
                            
                                            Password = password,
                            
                                            ConfirmPassword = confirmPassword
                            
                                        }).Result;
                            
                                    return response;
                            
                                }
                            
                            }

                            If we run our Web Api application, wait for it to spin up, and then run our console app, we see the following output:

                            Console output from the flawed request:
                            Bad Request

                             

                            Well, that's not very helpful.

                            If we de-serialize the response content to a string, we see there is more information to be had. Update the Main() method as follows:

                            De-serialize the Response Content:
                            static void Main(string[] args)
                            
                            {
                            
                                // This is not a valid email address, so the POST should fail:
                            
                                string email = "john";
                            
                                string password = "Password@123";
                            
                                string confirmPassword = "Password@123";
                            
                             
                            
                                HttpResponseMessage result = 
                            
                                    Register(email, password, confirmPassword);
                            
                             
                            
                                if(result.IsSuccessStatusCode)
                            
                                {
                            
                                    Console.WriteLine(
                            
                                        "The new user {0} has been successfully added.", email);
                            
                                }
                            
                                else
                            
                                {
                            
                                    string content = result.Content.ReadAsStringAsync().Result;
                            
                                    Console.WriteLine(content);
                            
                                }
                            
                                Console.Read();
                            
                            }

                             

                            Now, if we run the Console application again, we see the following output:

                            Output from the Console Application with De-Serialized Response Content:
                            {"Message":"The request is invalid.","ModelState":{"":["Email 'john' is invalid."]}}

                             

                            Now, what we see above is JSON. Clearly the JSON object contains a Message property and a ModelState property. But the ModelState property, itself another JSON object, contains an unnamed property, an array containing the error which occurred when validating the model.

                            Since a JSON object is essentially nothing but a set of key/value pairs, we would normally expect to be able to unroll a JSON object into a Dictionary<string, object>. However, the nameless property(ies) enumerated in the ModelState dictionary on the server side makes this challenging.

                            Unwrapping such an object using the Newtonsoft.Json library is doable, but slightly painful. Equally important, an error returned from our API may, or may not have a ModelState dictionary associated with it.

                            Another Flawed Request - More Validation Errors

                            Say we figured out that we need to provide a valid email address when we submit our request to the Register() method. Suppose instead, we are not paying attention and instead enter two slightly different passwords, and also forget that passwords have a minimum length.

                            Modify the code in the Main() method again as follows:

                            Flawed Request with Password Mismatch:
                            {
                            
                                "Message":"The request is invalid.",
                            
                                "ModelState": {
                            
                                    "model.Password": [
                            
                                        "The Password must be at least 6 characters long."],
                            
                                    "model.ConfirmPassword": [
                            
                                        "The password and confirmation password do not match."]
                            
                                }
                            
                            }

                             

                            In this case, it appears the items in the ModelState Dictionary are represented by valid key/value pairs, and the value for each key is an array.

                            Server Errors and Exceptions

                            We've seen a few examples of what can happen when the model we are passing with our POST request is invalid. But what happens if our Api is unavailable?

                            Let's pretend we finally managed to get our email and our passwords correct, but now the server is off-line.

                            Stop the Web Api application, and then re-run the Console application. Of course, after a reasonable server time-out, our client application throws an AggregateException.

                            What's an AggregateException? Well, it is what we get when an exception occurs during execution of an async method. If we pretend we don't know WHY our request failed, we would need to dig down into the InnerExceptions property of the AggregateException to find some useful information.

                            In the context of our rudimentary Console application, we will implement some top-level exception handling so that our Console can report the results of any exceptions like this to us.

                            Update the Main() method once again, as follows:

                            Add Exception Handling to the Main() Method of the Console Application:
                            static void Main(string[] args)
                            
                            {
                            
                                // This is not a valid email address, so the POST should fail:
                            
                                string email = "john@example.com";
                            
                                string password = "Password@123";
                            
                                string confirmPassword = "Password@123";
                            
                             
                            
                                // Add a Try/Catch in case something goes wrong and the server throws:
                            
                                try
                            
                                {
                            
                                    HttpResponseMessage result =
                            
                                        Register(email, password, confirmPassword);
                            
                             
                            
                                    if (result.IsSuccessStatusCode)
                            
                                    {
                            
                                        Console.WriteLine(
                            
                                            "The new user {0} has been successfully added.", email);
                            
                                    }
                            
                                    else
                            
                                    {
                            
                                        string content = result.Content.ReadAsStringAsync().Result;
                            
                                        Console.WriteLine(content);
                            
                                    }
                            
                                }
                            
                                catch (AggregateException ex)
                            
                                {
                            
                                    Console.WriteLine("One or more exceptions has occurred:");
                            
                                    foreach (var exception in ex.InnerExceptions)
                            
                                    {
                            
                                        Console.WriteLine("  " + exception.Message);
                            
                                    }
                            
                                }
                            
                                Console.Read();
                            
                            }

                             

                            If we run our console app now, while our Web Api application is offline, we get the following result:

                            Console Output with Exception Handling and Server Time-Out:
                            One or more exceptions has occurred:
                            
                              An error occurred while sending the request.

                             

                            Here, we are informed that "An error occurred while sending the request" which at least tells us something, and averts the application crashing due to an unhandled AggregateException.

                            Unwrapping and Handling Errors and Exceptions in Web Api

                            We've seen a few different varieties of errors and exceptions which may arise when registering a user from our client application.

                            While outputting JSON from the response content is somewhat helpful, I doubt it's what we are looking for as Console output. What we need is a way to unwrap the various types of response content, and display useful console messages in a clean, concise format that is useful to the user.

                            While I was putting together a more in-depth, interactive console project for a future article, I implemented a custom exception, and a special method to handle these cases.

                            ApiException - a Custom Exception for Api Errors

                            Yeah, yeah, I know. Some of the cases above don't technically represent "Exceptions" by the hallowed definition of the term. In the case of a simple console application, however, a simple, exception-based system makes sense. Further, unwrapping all of our Api errors up behind a single abstraction makes it easy to demonstrate how to unwrap them.

                            Mileage may vary according to the specific needs of YOUR application. Obviously, GUI-based applications may extend or expand upon this approach, relying less on Try/Catch and throwing exceptions, and more upon the specifics of the GUI elements available.

                            Add a class named ApiException to the Console project, and add the following code:

                            ApiException - a Custom Exception
                            using System;
                            
                            using System.Collections.Generic;
                            
                            using System.Linq;
                            
                            using System.Net;
                            
                            using System.Net.Http;
                            
                             
                            
                            namespace ApiWithErrorsTest
                            
                            {
                            
                                public class ApiException : Exception
                            
                                {
                            
                                    public HttpResponseMessage Response { get; set; }
                            
                                    public ApiException(HttpResponseMessage response)
                            
                                    {
                            
                                        this.Response = response;
                            
                                    }
                            
                             
                            
                             
                            
                                    public HttpStatusCode StatusCode
                            
                                    {
                            
                                        get
                            
                                        {
                            
                                            return this.Response.StatusCode;
                            
                                        }
                            
                                    }
                            
                             
                            
                             
                            
                                    public IEnumerable<string> Errors
                            
                                    {
                            
                                        get
                            
                                        {
                            
                                            return this.Data.Values.Cast<string>().ToList();
                            
                                        }
                            
                                    }
                            
                                }
                            
                            }

                             

                            Unwrapping Error Responses and Model State Dictionaries

                            Next, let's add a method to our Program which accepts an HttpResponseMessage as a method argument, and returns an instance of ApiException. Add the following code the the Program class of the Console application:

                            Add the CreateApiException Method the to Program Class:
                            public static ApiException CreateApiException(HttpResponseMessage response)
                            
                            {
                            
                                var httpErrorObject = response.Content.ReadAsStringAsync().Result;
                            
                             
                            
                                // Create an anonymous object to use as the template for deserialization:
                            
                                var anonymousErrorObject = 
                            
                                    new { message = "", ModelState = new Dictionary<string, string[]>() };
                            
                             
                            
                                // Deserialize:
                            
                                var deserializedErrorObject = 
                            
                                    JsonConvert.DeserializeAnonymousType(httpErrorObject, anonymousErrorObject);
                            
                             
                            
                                // Now wrap into an exception which best fullfills the needs of your application:
                            
                                var ex = new ApiException(response);
                            
                             
                            
                                // Sometimes, there may be Model Errors:
                            
                                if (deserializedErrorObject.ModelState != null)
                            
                                {
                            
                                    var errors = 
                            
                                        deserializedErrorObject.ModelState
                            
                                                                .Select(kvp => string.Join(". ", kvp.Value));
                            
                                    for (int i = 0; i < errors.Count(); i++)
                            
                                    {
                            
                                        // Wrap the errors up into the base Exception.Data Dictionary:
                            
                                        ex.Data.Add(i, errors.ElementAt(i));
                            
                                    }
                            
                                }
                            
                                // Othertimes, there may not be Model Errors:
                            
                                else
                            
                                {
                            
                                    var error = 
                            
                                        JsonConvert.DeserializeObject<Dictionary<string, string>>(httpErrorObject);
                            
                                    foreach (var kvp in error)
                            
                                    {
                            
                                        // Wrap the errors up into the base Exception.Data Dictionary:
                            
                                        ex.Data.Add(kvp.Key, kvp.Value);
                            
                                    }
                            
                                }
                            
                                return ex;
                            
                            }

                             

                            In the above, we get a sense for what goes into unwrapping an HttpResponseMessage which contains a mode state dictionary.

                            When the response content includes a property named ModeState, we unwind the ModelState dictionary using the magic of LINQ. We knit the string key together with the contents of the value array for each item present, and then add each item to the exception Data dictionary using an integer index for the key.

                            If no ModelState property is present in the response content, we simply unwrap the other errors present, and add them to the Data dictionary of the exception.

                            Error and Exception Handling in the Example Application

                            We've already added some minimal exception handling at the top level of our application. Namely, we have caught and handled AggregateExceptions which may be thrown by async calls to our api, which are not handled deeper in the call stack.

                            Now that we have added a custom exception, and a method for unwinding certain types error responses, let's add some additional exception handling, and see if we can do a little better, farther down.

                            Update the Register() method as follows:

                            Add Handle Errors in the Register() Method:
                            public static HttpResponseMessage Register(
                            
                                string email, string password, string confirmPassword)
                            
                            {
                            
                                //Attempt to register:
                            
                                using (var client = new HttpClient())
                            
                                {
                            
                                    var response =
                            
                                        client.PostAsJsonAsync("http://localhost:51137/api/Account/Register",
                            
                              
                            
                                        // Pass in an anonymous object that maps to the expected 
                            
                                        // RegisterUserBindingModel defined as the method parameter 
                            
                                        // for the Register method on the API:
                            
                                        new
                            
                                        {
                            
                                            Email = email,
                            
                                            Password = password,
                            
                                            ConfirmPassword = confirmPassword
                            
                                        }).Result;
                            
                             
                            
                                    if(!response.IsSuccessStatusCode)
                            
                                    {
                            
                                        // Unwrap the response and throw as an Api Exception:
                            
                                        var ex = CreateApiException(response);
                            
                                        throw ex;
                            
                                    }
                            
                                    return response;
                            
                                }
                            
                            }

                             

                            You can see here, we are examining the HttpStatusCode associated with the response, and if it is anything other than successful, we call our CreateApiException() method, grab the new ApiException, and then throw.

                            In reality, for this simple console example we likely could have gotten by with creating a plain old System.Exception instead of a custom Exception implementation. However, for anything other than the simplest of cases, the ApiException will contain useful additional information.

                            Also, the fact that it is a custom exception allows us to catch ApiException and handle it specifically, as we will probably want our application to behave differently in response to an error condition in an Api response than we would other exceptions.

                            Now, all we need to do (for our super-simple example client, anyway) is handle ApiException specifically in our Main() method.

                            Catch ApiException in Main() Method

                            Now we want to be able to catch any flying ApiExceptions in Main(). Our Console application, shining example of architecture and complex design requirements that it is, pretty much only needs a single point of error handling to properly unwrap exceptions and write them out as console text!

                            Add the following code to Main() :

                            Handle ApiException in the Main() Method:
                            static void Main(string[] args)
                            
                            {
                            
                                // This is not a valid email address, so the POST should fail:
                            
                                string email = "john@example.com";
                            
                                string password = "Password@123";
                            
                                string confirmPassword = "Password@123";
                            
                             
                            
                                // Add a Try/Cathc in case something goes wrong and the server throws:
                            
                                try
                            
                                {
                            
                                    HttpResponseMessage result =
                            
                                        Register(email, password, confirmPassword);
                            
                                    if (result.IsSuccessStatusCode)
                            
                                    {
                            
                                        Console.WriteLine(
                            
                                            "The new user {0} has been successfully added.", email);
                            
                                    }
                            
                                    else
                            
                                    {
                            
                                        string content = result.Content.ReadAsStringAsync().Result;
                            
                                        Console.WriteLine(content);
                            
                                    }
                            
                                }
                            
                                catch (AggregateException ex)
                            
                                {
                            
                                    Console.WriteLine("One or more exceptions has occurred:");
                            
                                    foreach (var exception in ex.InnerExceptions)
                            
                                    {
                            
                                        Console.WriteLine("  " + exception.Message);
                            
                                    }
                            
                                }
                            
                                catch(ApiException apiEx)
                            
                                {
                            
                                    var sb = new StringBuilder();
                            
                                    sb.AppendLine("  An Error Occurred:");
                            
                                    sb.AppendLine(string.Format("    Status Code: {0}", apiEx.StatusCode.ToString()));
                            
                                    sb.AppendLine("    Errors:");
                            
                                    foreach (var error in apiEx.Errors)
                            
                                    {
                            
                                        sb.AppendLine("      " + error);
                            
                                    }
                            
                                    // Write the error info to the console:
                            
                                    Console.WriteLine(sb.ToString());
                            
                                }
                            
                                Console.Read();
                            
                            }

                             

                            All we are doing in the above is unwinding the ApiException and transforming the contents for the Data dictionary into console output (with some pretty hackey indentation).

                            Now let's see how it all works.

                            Running Through More Error Scenarios with Error and Exception Handling

                            Stepping all the way back to the beginning, lets see what happens now if we try to register a user with an invalid email address.

                            Change our registration values in Main() back to the following:

                            // This is not a valid email address, so the POST should fail:
                            
                            string email = "john";
                            
                            string password = "Password@123";
                            
                            string confirmPassword = "Password@123";

                             

                            Run the Web Api application once more. Once it has properly started, run the Console application with the modified registration values. The output to the console should look like this:

                            Register a User with Invalid Email Address:
                            An Error Occurred:
                            
                            Status Code: BadRequest
                            
                            Errors:
                            
                              Email 'john' is invalid.

                             

                            Similarly, if we use a valid email address, but password values which are both too short, and also do not match, we get the following output:

                            Register a User with Invalid Password:
                            An Error Occurred:
                            
                            Status Code: BadRequest
                            
                            Errors:
                            
                              The Password must be at least 6 characters long.
                            
                              The password and confirmation password do not match.

                             

                            Finally, let's see what happens if we attempt to register the same user more than once.

                            Change the registration values to the following:

                            Using Valid Registration Values:
                            string email = "john@example.com";
                            
                            string password = "Password@123";
                            
                            string confirmPassword = "Password@123";

                             

                            Now, run the console application twice in a row. The first time, the console output should be:

                            Console Output from Successful User Registration:
                            The new user john@example.com has been successfully added.
                            

                             

                            The next time, however, an error result is returned from our Web Api:

                            Console Output from Duplicate User Registration:
                            An Error Occurred:
                            
                            Status Code: BadRequest
                            
                            Errors:
                            
                              Name john@example.com is already taken.. Email 'simon@example.com' is already taken.

                             

                            Oh No You did NOT Use Exceptions to Deal with Api Errors!!

                            Oh, yes I did . . . at least, in this case. This is a simple, console-based application in which nearly every result needs to end up as text output. Also, I'm just a rebel like that, I guess. Sometimes.

                            The important thing to realize is how to get the information we need out of the JSON which makes up the response content, and that is not as straightforward as it may seem in this case. How different errors are dealt with will, as always, need to be addressed within terms best suited for your application.

                            In a good many cases, treating Api errors as exceptions, to me, has merit. Doing so most likely will rub some architecture purists the wrong way (many of the errors incoming in response content don't really meet the textbook definition of "exception"). That said, for less complex .NET-based Api Client applications, unwrapping the errors from the response content, and throwing as exceptions to be caught by an appropriate handler can save on a lot of duplicate code, and provides a known mechanism for handling problems.

                            In other cases, or for your own purposes, you may choose to re-work the code above to pull out what you need from the incoming error response, but otherwise deal with the errors without using exceptions. Register() (and whatever other methods you use to call into your Api) might, in the case of a simple console application, return strings, ready for output. In this case, you could side-step the exception issue.

                            Needless to say, a good bit of the time, you will likely by calling into your Web Api application not from a desktop .NET application, but instead from a web client, probably using Ajax or something.

                            That's a Long and Crazy Post about Dealing with Errors - Wtf?

                            Well, I am building out a more complex, interactive console-based application in order to demo some concepts in upcoming posts. One of the more irritating aspects of that process was figuring out a reasonable way to deal with the various issues that may arise, when all one has to work with is a command line interface to report output to the user.

                            This was part of that solution (ok, in the application I'm building, things are a little more complex, a little more organized, and there's more to it. But here we saw some of the basics).

                            But . . . Can't We Just do it Differently on the Server?

                            Well . . . YES!

                            In all likelihood, you just might tune up how and what you are pushing out to the client, depending upon the nature of your Web Api and the expected client use case. In this post, I went with the basic, default set-up (and really, we only looked at one method). But, depending upon how your Api will be used, you might very will handle errors and exceptions differently on the server side, which may impact how you handle things on the client side.

                            Additional Resources and Items of Interest

                             

                            Posted on September 28 2014 08:42 PM by jatten     

                            Comments (0)

                            ASP.NET Identity 2.0: Introduction to Working with Identity 2.0 and Web API 2.2

                            Posted on September 21 2014 07:09 PM by jatten in ASP.Net, ASP.NET MVC, C#, CodeProject   ||   Comments (2)

                            Lock-320In recent posts, I've covered a lot of ground using ASP.NET Identity 2.0 in the context of an MVC application. Since it's RTM in March of this year, Identity 2.0 has offered a substantial expansion of the Authentication/Authorization . toolset available to MVC applications.

                            Similarly, Identity 2.0 expands the Identity management tools available through ASP.NET Web Api. The same flexibility and extensibility are available, although things work just a little differently.

                            In this post, I'm going to take a quick tour of a basic Identity 2.0-based Web Api application, and look at some basic usage examples. If you are an experienced web developer, there is probably nothing new for you here – this is intended to be a bit of a 101 level introduction. If you are new to Identity 2.0 and/or ASP.NET Web Api you will likely find some of this information useful.

                            Image by Universal Pops  |  Some Rights Reserved

                            In the next few posts, we'll take a closer look at using Identity 2.0 with Web Api. We'll see how to extend the Identity model, similar to what we did with Identity 2.0 for MVC. We'll create a more easily extensible Identity/Web Api project. We will also explore various Authentication and Authorization schemes, and develop a better understanding of effective Web Api security.

                            For a review of what we have covered on using Identity 2.0 in the context of an ASP.NET MVC project, see:

                            For now, we'll take a high-level look at the basic Web Api project template shipped with Visual Studio, and get familiar with the basic structure.

                            Getting Started - Create a New ASP.NET Web Api Project

                            As of Visual Studio 2013 Update 3, WebApi 2.0 and Identity 2.0 are part of the out-of-the-box Web Api project template. While this default project template is a bit cluttered for my tastes, it is a good starting point to get familiar with the basics.

                            We are going to create a basic Web Api project in visual studio, and then update the various Nuget packages. In part, because we generally want to tbe using the latest package versions, but in particular because we want to work with the latest release of Web Api (version 2.2 as of this writing, released in July 2014).

                            First, do File => New Project, and then select ASP.NET Web Application:

                            Create a New Web Application Project:

                            New Project

                            Update the Nuget Packages for the Solution

                            Then, Open the Nuget Package Manager and update the Nuget packages for the solution. Select "Update" from the sidebar on the left, then "Update All":

                            Update Nuget Packages for Solution:

                            update-nuget-packages

                            When you get partway through the update process, you will be greeted by a scary-looking window warning you that some project files are about to be overwritten. These are .cshtml View files, and SHOULD be overwritten, as they are being updated to work with the new libraries.

                            Resolve File Conflicts by Clicking “Yes to All”:

                            nuget-file-conflict-yes-to-all

                            View files, you say? I thought we were making a Web Api project?

                            We are. But as I said, the default Web Api project includes a bit 'o MVC, for the "Help" page, and a basic home page. More on this in a bit.

                            Run and Test the Default Web Api Project

                            Now that we have created a new Web Api project, let's give it a quick test spin and see if everything is working property.

                            Open another instance of Visual Studio, and create a console application. Next, use the Nuget package manager to install the Web Api Client Libraries:

                            Install Web Api Client Libraries into Console Application:

                            install-webapi-client-libraries-in-console-app

                            After the installation is complete, check for updates. Then, add the following code to the Program.cs file (be sure to add System.Net.Http and Newtonsoft.Json to the using statements at the top of the class):

                            Example code to run against Web Api Application:
                            using System;
                            
                            using System.Collections.Generic;
                            
                            using System.Linq;
                            
                            using System.Text;
                            
                            using System.Threading.Tasks;
                            
                            using System.Net.Http;
                            
                            using Newtonsoft.Json;
                            
                             
                            
                            namespace TestApi
                            
                            {
                            
                                class Program
                            
                                {
                            
                                    static void Main(string[] args)
                            
                                    {
                            
                                        string userName = "john@example.com";
                            
                                        string password = "Password@123";
                            
                                        var registerResult = Register(userName, password);
                            
                             
                            
                                        Console.WriteLine("Registration Status Code: {0}", registerResult);
                            
                             
                            
                                        string token = GetToken(userName, password);
                            
                                        Console.WriteLine("");
                            
                                        Console.WriteLine("Access Token:");
                            
                                        Console.WriteLine(token);
                            
                                        Console.Read();
                            
                                    }
                            
                             
                            
                             
                            
                                    static string Register(string email, string password)
                            
                                    {
                            
                                        var registerModel = new 
                            
                                        { 
                            
                                            Email = email, 
                            
                                            Password = password, 
                            
                                            ConfirmPassword = password 
                            
                                        };
                            
                                        using (var client = new HttpClient())
                            
                                        {
                            
                                            var response =
                            
                                                client.PostAsJsonAsync(
                            
                                                "http://localhost:62069/api/Account/Register", 
                            
                                                registerModel).Result;
                            
                                            return response.StatusCode.ToString();
                            
                                        } 
                            
                                    }
                            
                             
                            
                             
                            
                                    static string GetToken(string userName, string password)
                            
                                    {
                            
                                        var pairs = new List<KeyValuePair<string, string>>
                            
                                                    {
                            
                                                        new KeyValuePair<string, string>( "grant_type", "password" ), 
                            
                                                        new KeyValuePair<string, string>( "username", userName ), 
                            
                                                        new KeyValuePair<string, string> ( "Password", password )
                            
                                                    };
                            
                                        var content = new FormUrlEncodedContent(pairs);
                            
                                        using (var client = new HttpClient())
                            
                                        {
                            
                                            var response = 
                            
                                                client.PostAsync("http://localhost:62069/Token", content).Result;
                            
                                            return response.Content.ReadAsStringAsync().Result;
                            
                                        }
                            
                                    }
                            
                                }
                            
                            }

                             

                            Now, Run your Web Api application. Once it has finished starting up, run the new Console application.

                            What we are going to do here is POST content representing a new user to the Register() method defined on the AccountsController of our Api. Next, we will make a call to the Token endpoint of our Api and retrieve an access token, which we can use to authenticate ourselves during subsequent calls to our Api.

                            If you aren't quite sure what is happening in the code above, don't worry about it for the moment. We'll look more closely at what is going on here (and what this "token" business is all about) in a bit. For the moment, let's just see if our application works.

                            If all went as expected, our console output should look something like this:

                            Console Output from Register and GetTokenDictionary Methods:
                            resitration Status Code: OK
                            
                            Access Token:
                            
                            {"access_token":"8NeiVoKARt5Rm_50mP2ZfudNvvPRkm-FehohX8cLmUmrm1y8kZj0PTccsH1nKbT
                            
                            PFTGuKoFSfi2mfD2KD-UMOEQWVJ0PJPfiSebJubSPLElzYfR7vk_V8gcbbkLK6cZ0zS7gWrMhdbgQrrQ
                            
                            yDPyR83gbkjZcE1ooQQiv9d7AEfjCassj_R76Q44PW7goMHcbFZl66dZLBKGKhf9t7lpcvWStoyS6z8a
                            
                            m7B3SWppVeaTjAC5BZ6uHOG1d_0mzL8FiR_eV8NaA1w3-GfV-upErG6xk5-qykdoLHoe7zmv4tX5Dm7-
                            
                            w2n3G0gAdVlPcbfAJgSvu1AmUQe85g5ABbWJ6e0OXoPtH658kYZWC0FxbWiFPLGz66wPMbUCwjk_Hq_p
                            
                            rLDjOshWP6lIE3qwQ88U5ScB1XcGAXbrYYFZ9AYkwSt4o5cC2Vpw8xP2OFdLdDOUs2ESPtVK8FThhaAh
                            
                            yFUUDpXSlXwhQ2nEuu27ISw1MK0bh06-xx4vcnfoaW9XuHBXm","token_type":"bearer","expire
                            
                            s_in":1209599,"userName":"john@example.com",".issued":"Wed, 17 Sep 2014 02:14:29
                            
                             GMT",".expires":"Wed, 01 Oct 2014 02:14:29 GMT"}

                             

                            Congratulations! You have just registered a user, and retrieved an access token from your new Web Api application.

                            What you see in the console output is, first, the result from registering a new user. After that you see what looks like a gob of Json because, well, it is. We de-serialized the response content into a string, and an ginormous blob of JSON is what we get for that.

                            If you look close, you can see first the access_token property, and there at the end are a few other properties, such as token_type, a time until the token expires, userName, and a few other properties. We will be looking more closely at tokens shortly.

                            Now that we see everything is working, let's take a look at how the Web Api application is structured, and identify the important pieces we need to be aware of when working with Web Api and Identity. Then we'll learn a little more about this token business. 

                            Important Note: The code above is strictly to see if our application starts up and runs, and as an example of obtaining an access token. In reality, you would always want to implement SSL before sending/receiving Bearer tokens in this manner.

                            Web Api Project Structure - Overview

                            As mentioned previously, the default project template contains some fluff we may or may not wish to use in a "real" Web Api project. There is a folder named "Areas" which contains a bunch of stuff for managing a "help" page, there is a Views folder containing, well, a few .cshtml Views, and a few other folders mostly supporting the MVC side of our project, such as the Content folder, fonts folder, Etc. For the moment, it is safe to say we can ignore the following project folders (although they may be useful later on, depending upon your needs):

                            VS Web Api Project Folders we can Ignore (for now):

                            • Areas folder
                            • Content folder
                            • Fonts folder
                            • Scripts folder
                            • Views folder

                            On the other hand, while we are getting started with Web Api and Identity 2.0, the following folders DO Matter to us, and form a critical part of the project structure. We need to be familiar with these in order to extend/adapt/customize the default project to our needs:

                            VS Web Api Project Folders we ARE Interested in:

                            • App-Start folder
                            • Controllers folder
                            • Models folder
                            • Providers folder

                            The App_Start Folder

                            The App_Start folder contains various configuration items which need to happen at… application startup. Of particular interest to us, from the context of an Identity-based Web Api application, are the files named IdentityConfig.cs, WebApiConfig.cs, and Startup.Auth.cs.

                            As the names of these files imply, these are each respectively where we might setup and/or modify configuration options for each of the services indicated. IdentityConfig.cs and Startup.Auth.cs are where we configure most of the authentication and authorization options for our application. WebApiConfig.cs is where we set up our default routes for incoming requests.

                            The Controllers Folder

                            This should be familiar to any who have worked with a basic MVC project before. However, note that controllers in a Web Api application are not the same as controllers in an MVC application - Web Api controllers all inherit from the ApiController base class, whereas the familiar MVC controller inherits from the Controller base class.

                            Controllers are where we process incoming Http requests, and either return the requested resource, or respond with the appropriate Http status code. We'll get a closer look at this soon.

                            The Models Folder

                            In a Web Api application, much like an MVC application, we define the business entities used by our application in terms of Models, ViewModels, and BindingModels. The Models folder is generally where we will find the business objects we need to make our application work.

                            As a general rule, Models represent core business objects, often persisted in our back-end data store. ViewModels generally represent the data required for use by a user of our Api. BindingModels are similar to Viewmodels, but they generally represent incoming data, and are used by the controller to deserialize incoming content from Xml or Json into objects our application can use.

                            The Providers Folder

                            In the default Visual Studio Web Api Solution, the Providers folder contains a single class, the ApplicationOAuthProvider. In the default configuration, this class is consumed by the ConfigureAuth() method defined on Startup.Auth, and defines how Authentication and Authorization tokens are handled.

                            Identity 2.0 and hence, Web Api 2.2 rely heavily on Owin as the default Authorization and Authentication middleware.

                            Default Identity Models for Web Api

                            If we start with the IdentityModels.cs file, we can see there isn't much going on here. We have a basic ApplicationUser class, and the ApplicationDbContext, and that's it:

                            The Identity Models Class:
                            public class ApplicationUser : IdentityUser
                            
                            {
                            
                                public async Task<ClaimsIdentity> GenerateUserIdentityAsync(
                            
                                    UserManager<ApplicationUser> manager, string authenticationType)
                            
                                {
                            
                                    // Note the authenticationType must match the one defined in 
                            
                                    // CookieAuthenticationOptions.AuthenticationType
                            
                                    var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
                            
                              
                            
                                    // Add custom user claims here
                            
                                    return userIdentity;
                            
                                }
                            
                            }
                            
                              
                            
                              
                            
                            public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
                            
                            {
                            
                                public ApplicationDbContext()
                            
                                    : base("DefaultConnection", throwIfV1Schema: false)
                            
                                {
                            
                                }
                            
                              
                            
                                
                            
                                public static ApplicationDbContext Create()
                            
                                {
                            
                                    return new ApplicationDbContext();
                            
                                }
                            
                            }

                             

                            The AccountViewModels.cs and AccountBindingModels.cs files contain the various ViewModels and BindingModels used by the AccountController methods. Pop those open and get a feel for what's there. The ViewModels represent what our Api expects to serialize and push OUT as content with a response, and the BindingModels generally represent data we expect to RECEIVE as content in an HTTP request, after being de-serialized by the appropriate format provider.

                            AccountController - an ApiController

                            Next let's take a look at AccountController. We won't walk through the code for this here in the post, but take a quick look at the various methods in this class. Many will look suspiciously similar to the AccountController in an MVC project. However, there are some important differences.

                            First off,  note that the methods on AccountController return either a data entity of some sort (such as the UserInfoViewModel returned by the GetUserInfo() method), or they return an instance of IHttpActionResult.

                            In other words, as we already suspected, Action methods on a Web Api controller don't return Views. They ultimately return HttpResponse messages (with some downstream assistance from the controller itself), with any content encoded in the response body.

                            By way of comparison, we find that the HomeController, also in the Controllers folder, inherits from the familiar Controller base class, and the Action methods defined on HomeController return the standard ActionResult, generally Views. That's because HomeController, here, is included to support the MVC component of our Web Api. We can ignore HomeController for now, as in currently doesn't provide access to any of our Api Methods.

                            Identity Configuration

                            In the App_Start folder, open the IdentityConfig.cs file. There's a lot less going on here than in the similar file found in a standard ASP.NET MVC project. Essentially, the only thing that happens here is that the ApplicationUserManager is defined for the Web Api Application. However, there are some important configuration items taking place here.

                            The ApplicationUserManager Class Defined in IdentityConfig.cs
                                public static ApplicationUserManager Create(
                            
                                    IdentityFactoryOptions<ApplicationUserManager> options, 
                            
                                    IOwinContext context)
                            
                                {
                            
                                    var manager = new ApplicationUserManager(
                            
                                        new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
                            
                              
                            
                                    // Configure validation logic for usernames
                            
                                    manager.UserValidator = new UserValidator<ApplicationUser>(manager)
                            
                                    {
                            
                                        AllowOnlyAlphanumericUserNames = false,
                            
                                        RequireUniqueEmail = true
                            
                                    };
                            
                                    // Configure validation logic for passwords
                            
                                    manager.PasswordValidator = new PasswordValidator
                            
                                    {
                            
                                        RequiredLength = 6,
                            
                                        RequireNonLetterOrDigit = true,
                            
                                        RequireDigit = true,
                            
                                        RequireLowercase = true,
                            
                                        RequireUppercase = true,
                            
                                    };
                            
                                    
                            
                                    var dataProtectionProvider = options.DataProtectionProvider;
                            
                                    if (dataProtectionProvider != null)
                            
                                    {
                            
                                        manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(
                            
                                            dataProtectionProvider.Create("ASP.NET Identity"));
                            
                                    }
                            
                                    return manager;
                            
                                }
                            
                            }
                            

                             

                            In this file, we define the password requirements for our application, as well as place potential constraints on user names and email addresses.

                            While not included in the default implementation for a Web Api project, this is also where we might define two-factor authentication providers for email or SMS text, and also where we might define email account confirmation configuration.

                            If we wanted to extend the Identity implementation in our Web Api application to include Roles, and some database initialization, this file is where we would most likely define and configure a RoleManager and DbInitializer.

                            Startup Authentication and Authorization Configuration

                            The primary authentication and authorization strategy in Web Api is token-based. We'll look closer at what this means momentarily, but for now, assume that in order to access any secured portion of your Api, you will need to present an access token as part of any incoming Http Request.

                            In the Startup.Auth.cs file, we se where configuration for authentication processing is performed. We see that the Startup.Auth.cs file contains a partial class, Startup, which extends the default Startup class defined at the root level of our project.

                            If we take a look at the latter (the Startup class at the root level of the project) we see a single method call, to the ConfigureAuth() method:

                            The Core Startup Class:
                            public partial class Startup
                            
                            {
                            
                                public void Configuration(IAppBuilder app)
                            
                                {
                            
                                    ConfigureAuth(app);
                            
                                }
                            
                            }

                             

                            The ConfigureAuth() method is defined on the partial class in Startup.Auth.cs:

                            The ConfigureAuth Method from Startup.Auth.cs:
                            public void ConfigureAuth(IAppBuilder app)
                            
                            {
                            
                                // Configure the db context and user manager to use a single instance per request
                            
                                app.CreatePerOwinContext(ApplicationDbContext.Create);
                            
                                app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
                            
                              
                            
                                // Enable the application to use a cookie to store information for the signed in user
                            
                                // and to use a cookie to temporarily store information about a 
                            
                                // user logging in with a third party login provider
                            
                                app.UseCookieAuthentication(new CookieAuthenticationOptions());
                            
                                app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
                            
                              
                            
                                // Configure the application for OAuth based flow
                            
                                PublicClientId = "self";
                            
                                OAuthOptions = new OAuthAuthorizationServerOptions
                            
                                {
                            
                                    TokenEndpointPath = new PathString("/Token"),
                            
                                    Provider = new ApplicationOAuthProvider(PublicClientId),
                            
                                    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
                            
                                    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
                            
                                    AllowInsecureHttp = true
                            
                                };
                            
                              
                            
                                // Enable the application to use bearer tokens to authenticate users
                            
                                app.UseOAuthBearerTokens(OAuthOptions);
                            
                              
                            
                                // Uncomment the following lines to enable logging in with 
                            
                                // third party login providers
                            
                                //app.UseMicrosoftAccountAuthentication(
                            
                                //    clientId: "",
                            
                                //    clientSecret: "");
                            
                              
                            
                                //app.UseTwitterAuthentication(
                            
                                //    consumerKey: "",
                            
                                //    consumerSecret: "");
                            
                              
                            
                                //app.UseFacebookAuthentication(
                            
                                //    appId: "",
                            
                                //    appSecret: "");
                            
                              
                            
                                //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
                            
                                //{
                            
                                //    ClientId = "",
                            
                                //    ClientSecret = ""
                            
                                //});
                            
                            }

                             

                            In this method, we configure options for cookie and token authentication, and, optionally, allow sign-in via various third-party providers such as Facebook or Twitter.

                            Of particular interest to us is the highlighted line configuring the application to use Bearer tokens. We'll find out why this is important soon enough. For now, realize that the Startup.Auth.cs file is where you configure the authorization and authentication options for the application as a whole, and determine what your application will, and won't, accept as credentials.

                            Token Authentication and Bearer Tokens

                            The standard Visual Studio Web Api template is configured to use OAuth bearer tokens as a primary means of authentication. Bearer Tokens are exactly what the name implies - Web Api will consider the bearer of the token to be properly authenticated (provided the token is not expired, per the configuration settings in Startup.Auth.cs).

                            This can have some serious security implications. If a malicious actor were able to intercept a client request and get hold of the Bearer Token from the request header, they would then be able to gain access as an authenticated Api user.

                            For this reason, if you are planning to deploy a Web Api application using the default Bearer Token authentication scheme, it is critical that the production application implement SSL/TSL(Meaning, HTTPS) to encrypt and protect traffic between the client an  your Api.

                            If we take another look at the console output when we ran our application earlier, we the information we grabbed from the token:

                            Contents of the Default Access Token:
                            registration Status Code: OK
                            
                            Access Token:
                            
                            {"access_token":"8NeiVoKARt5Rm_50mP2ZfudNvvPRkm-FehohX8cLmUmrm1y8kZj0PTccsH1nKbT
                            
                            PFTGuKoFSfi2mfD2KD-UMOEQWVJ0PJPfiSebJubSPLElzYfR7vk_V8gcbbkLK6cZ0zS7gWrMhdbgQrrQ
                            
                            yDPyR83gbkjZcE1ooQQiv9d7AEfjCassj_R76Q44PW7goMHcbFZl66dZLBKGKhf9t7lpcvWStoyS6z8a
                            
                            m7B3SWppVeaTjAC5BZ6uHOG1d_0mzL8FiR_eV8NaA1w3-GfV-upErG6xk5-qykdoLHoe7zmv4tX5Dm7-
                            
                            w2n3G0gAdVlPcbfAJgSvu1AmUQe85g5ABbWJ6e0OXoPtH658kYZWC0FxbWiFPLGz66wPMbUCwjk_Hq_p
                            
                            rLDjOshWP6lIE3qwQ88U5ScB1XcGAXbrYYFZ9AYkwSt4o5cC2Vpw8xP2OFdLdDOUs2ESPtVK8FThhaAh
                            
                            yFUUDpXSlXwhQ2nEuu27ISw1MK0bh06-xx4vcnfoaW9XuHBXm", "token_type":"bearer" ,"expire
                            
                            s_in":1209599,"userName":"john@example.com",".issued":"Wed, 17 Sep 2014 02:14:29
                            
                             GMT",".expires":"Wed, 01 Oct 2014 02:14:29 GMT"}

                             

                            We see that the token_type property identifies this as a Bearer token.

                            To use a rather cliché example, Bearer tokens can be compared to currency - they are valid for whoever presents them.

                            The examples we have used to this point, and for the rest of this article will be using standard HTTP. While you are developing your application or trying on the examples, this is fine. However, if you are going to deploy a production application you will most definitely want to implement SSL/TSL, or investigate alternative authentication/authorization schemes.

                            We will take a closer look at Tokens, and alternate authentication mechanisms in an upcoming post.

                            Using Token Authentication and Accessing Web Api from a Client Application

                            We've already seen how to obtain a basic bearer token from our Api. Now let's take a more detailed look at grabbing a token, and then making some requests against the basic, default Web Api project.

                            First, let's change up our code a little bit. What we want to do is pull apart the JSON token into a Dictionary<string, string> so we can get at the access_token property. It is this we need to submit in the header of subsequent Api requests in order to authenticate ourselves.

                            Change the GetToken() method as follows, so that instead of returning s string, it returns a Dictionary. Let's also rename it as GetTokenDictionary() to better reflect what it does:

                            The New GetTokenDictionary() Method:
                            static Dictionary<string, string> GetTokenDictionary(
                            
                                string userName, string password)
                            
                            {
                            
                                var pairs = new List<KeyValuePair<string, string>>
                            
                                            {
                            
                                                new KeyValuePair<string, string>( "grant_type", "password" ), 
                            
                                                new KeyValuePair<string, string>( "username", userName ), 
                            
                                                new KeyValuePair<string, string> ( "Password", password )
                            
                                            };
                            
                                var content = new FormUrlEncodedContent(pairs);
                            
                             
                            
                                using(var client = new HttpClient())
                            
                                {
                            
                                    var response =
                            
                                        client.PostAsync("http://localhost:62069/Token", content).Result;
                            
                                    var result = response.Content.ReadAsStringAsync().Result;
                            
                             
                            
                                    // Deserialize the JSON into a Dictionary<string, string>
                            
                                    Dictionary<string, string> tokenDictionary =
                            
                                        JsonConvert.DeserializeObject<Dictionary<string, string>>(result);
                            
                                    return tokenDictionary;
                            
                                }
                            
                            }

                             

                            Now let's update the Main() method to use our new dictionary, and write the various token properties out individually to the console:

                            Update Main() Method to Consume Deserialized Token Information:
                            static void Main(string[] args)
                            
                            {
                            
                                string userName = "john@example.com";
                            
                                string password = "Password@123";
                            
                                var registerResult = Register(userName, password);
                            
                             
                            
                                Console.WriteLine("resitration Status Code: {0}", registerResult);
                            
                             
                            
                                //string token = GetToken(userName, password);
                            
                                Dictionary<string, string> token = GetTokenDictionary(userName, password);
                            
                                Console.WriteLine("");
                            
                                Console.WriteLine("Access Token:");
                            
                                Console.WriteLine(token);
                            
                             
                            
                                // Write each item in the dictionary out to the console:
                            
                                foreach (var kvp in token)
                            
                                {
                            
                                    Console.WriteLine("{0}: {1}", kvp.Key, kvp.Value);
                            
                                }
                            
                             
                            
                                Console.WriteLine("");
                            
                                Console.Read();
                            
                            }

                             

                            If we run the two applications now, we see the console output looks a little different:

                            Console Output Using the GetTokenDictionary() Method:
                            Registration Status Code: BadRequest
                            
                             
                            
                            Access Token:
                            
                            System.Collections.Generic.Dictionary`2[System.String,System.String]
                            
                            access_token: zPrC7-CyHljhkPYQIDawhu7kgRd86p7oSJpRqHGifuANmEtM61syU5t6ciPGJrA3RX
                            
                            I9u79IIOFaV3w5_GAQeF28DlUnc2HSkCxZsnqaYojLWfJ6gc8gfUlZo76SeJ7iO7MT6fdo8C5XgM_Geq
                            
                            yun_8ykut9N456F41dI5PrrR6CyNc0ss_hy9OzdxnoqUdERglooNUrEcEt7WdZ9FHJ-cAi15fVPfV4z4
                            
                            dUZZylrIyHuNSLVReet-zL769IEPvhgYixrp_hMgGQ6lDx8YMPTWvK_SVbe4W89DrHl1PbqfkiVgbJgJ
                            
                            M09kmmIytNCFl_ua_GOdx1WyxXfPv0TLOmAgPX3klI4r_pglZl1QA0vihTN7zLsP2bkxIbMCBac3kq8z
                            
                            4JT1JalxZ0OgArkW-Gy2qZJ-o-mPATCPUXLHtEd3z4lze17ECuCJyZzfLts3NN-hJgNwbmcqvGNvcakp
                            
                            Y6SQ6U_ACdBJ3Q2JgZZeWf75pDupjeQbMhTqAPWUq9n35k
                            
                            token_type: bearer
                            
                            expires_in: 1209599
                            
                            userName: john@example.com
                            
                            .issued: Wed, 17 Sep 2014 02:55:45 GMT
                            
                            .expires: Wed, 01 Oct 2014 02:55:45 GMT

                             

                            First of all, why does it say "Bad Request" for our registration status code? We haven't changed THAT part of the code at all . . .  and there is the problem.

                            We have already registered a user named john@example.com, and Web Api has tossed back a validation error (the details of which are actually buried in the response content, but we'll worry about that later).

                            We can ignore that for now.

                            The next part of our console output is what we're interested in. Notice that, while the big, encrypted access_token still looks like a big blob of gibberish, our token as a whole is nicely split into its component properties on each line.

                            Now, let's try to access some of the Identity information from our Api. If we take a look at the AccountController in our Web Api project, we see there is a controller method named GetUserInfo() . Because our GetUserInfo() is decorated with the [Route] attribute “UserInfo" and our account controller [Route] attribute specifies a route of “api/Account” we know we can access this  method using the route “<host>/api/Account/UserInfo.”

                            Add the following static method to the Program class in our console application:

                            Add a GetUserInfo() Method to the Program Class:
                            static string GetUserInfo()
                            
                            {
                            
                                using(var client = new HttpClient())
                            
                                {
                            
                                    var response = 
                            
                                        client.GetAsync("http://localhost:62069/api/Account/UserInfo").Result;
                            
                                    return response.Content.ReadAsStringAsync().Result;
                            
                                }
                            
                            }

                             

                            This simple method uses an instance of HttpClient to access the GetUserInfo() method on our AccountController, reads the response content as a string, and returns the result.

                            Now, we can call this method from the Main() method of our Console program and see what we get back. Update the Main() method of the Program class as follows:

                            Add a Call to GetUserInfo() to the Main() Method of the Program Class:
                            static void Main(string[] args)
                            
                            {
                            
                                string userName = "john@example.com";
                            
                                string password = "Password@123";
                            
                                var registerResult = Register(userName, password);
                            
                             
                            
                                Console.WriteLine("Registration Status Code: {0}", registerResult);
                            
                             
                            
                                Dictionary<string, string> token = GetTokenDictionary(userName, password);
                            
                                Console.WriteLine("");
                            
                                Console.WriteLine("Access Token:");
                            
                                Console.WriteLine(token);
                            
                             
                            
                                // Write each item in the dictionary out to the console:
                            
                                foreach (var kvp in token)
                            
                                {
                            
                                    Console.WriteLine("{0}: {1}", kvp.Key, kvp.Value);
                            
                                }
                            
                             
                            
                                Console.WriteLine("");
                            
                             
                            
                                Console.WriteLine("Getting User Info:");
                            
                                Console.WriteLine(GetUserInfo());
                            
                            }

                             

                            Now, start your Web Api project again, wait for it to spin up completely, and then run the console application. Your console output should look something like this:

                            Console Output After Updating Client with Call to GetUserInfo() Method:
                            Registration Status Code: BadRequest
                            
                             
                            
                            Access Token:
                            
                            System.Collections.Generic.Dictionary`2[System.String,System.String]
                            
                            access_token: c47uW0q-1qsIm88et81YqFzGz0Nt2GflLZJ3nLLDUPIS8epwMiBMkG9lCmF7-Rk8Ji
                            
                            KA33JJkgtFl-3mjn78N-iQcX2pLxEsYf4h65njj2BaSRCSheCyfWY5WcS2MPipRFfwr1e-wx49R4Awo3
                            
                            DHk2nJmMe_ARIASzw7Ger4gpJgNrqxt8B4QWcJyjgrr7RwK95alKQ4MY-ZlzJyNdWthdCSeykTvzLJQ-
                            
                            rGjH7KT-SYwknyt62Fm2bwE7WzcudFgs1RIq8HDzuPiM9Fx9dBLhhPT2sCq8iV1dDFrCTnDsNoLA5ncG
                            
                            y9BncGFb5fmkqibP1tV8k2xW0OxqaAuVz4jaS212--o2P9JQ5kCmC6gSYH0faBgNva3SV8uZnM64YoyY
                            
                            gJ3i_lOql0pxxjtqJYUYQLYSDwzYVJXXl_PiKLHGrjZhtn480kzSdfkgdv1i-jiqap2ymzIeJjGcd6Tm
                            
                            fePqCXeiKLUEO3FsOJ4VeRkXFXEkXpFBbuGWIe6N64M4M3
                            
                            token_type: bearer
                            
                            expires_in: 1209599
                            
                            userName: john@example.com
                            
                            .issued: Sun, 21 Sep 2014 00:56:49 GMT
                            
                            .expires: Sun, 05 Oct 2014 00:56:49 GMT
                            
                             
                            
                            Getting User Info:
                            
                            {"Message":"Authorization has been denied for this request."}

                             

                            But . . . wait. What happened? The last line, the result of our call to GetUserInfo() says authorization has been denied. What are we missing?

                            Notice that our AccountController class is decorated with an [Authorize] attribute in the class declaration. As you probably already realize, this mean that not just any old body can call into the AccountController. The class itself is decorated with the [Authorize] Attribute, so by default only authorized users can call Action methods defined on the class using HTTP. The Register() method has been decorated with an [AllowAnonymous] attribute, so unauthorized/unregistered users can register. but the rest of the methods are not accessible to unauthorized users.

                            In our case, despite the fact that we have actually obtained an access token from our Web Api by calling the Token endpoint of the Web Api application, we have not actually provided the token with our request to GetUserInfo().

                            Setting the Default Access Token For Web Api Requests

                            In order to create pass a token in with our HTTP requests, we can use the DefaultRequestHeaders property of HttpClient like so:

                            Set the Default Authorization Header for Client Requests:
                            HttpClient client = new HttpClient();
                            
                            client.DefaultRequestHeaders.Authorization = 
                            
                                new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);

                             

                            We can modify the GetUserInfo() method on our Program class like so:

                            Set Default Authorization Header on Request for GetUserInfo() Method:
                            static string GetUserInfo(string token)
                            
                            {
                            
                                using(var client = new HttpClient())
                            
                                {
                            
                                    client.DefaultRequestHeaders.Authorization = 
                            
                                    new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
                            
                             
                            
                                    var response = client.GetAsync("http://localhost:62069/api/Account/UserInfo").Result;
                            
                                    return response.Content.ReadAsStringAsync().Result;
                            
                                }
                            
                            }

                             

                            Notice we are now passing an access token in as a method argument, specifying that it is a Bearer token, and then using that token to set in our default Authorization header.

                            Now, we simply need to pass that token to the method when we call it from Main() :

                            Pass the Access Token to GetUserInfo() when Calling from Main():
                            static void Main(string[] args)
                            
                            {
                            
                                string userName = "john@example.com";
                            
                                string password = "Password@123";
                            
                                var registerResult = Register(userName, password);
                            
                             
                            
                                Console.WriteLine("Registration Status Code: {0}", registerResult);
                            
                             
                            
                                Dictionary<string, string> token = GetTokenDictionary(userName, password);
                            
                                Console.WriteLine("");
                            
                                Console.WriteLine("Access Token:");
                            
                                Console.WriteLine(token);
                            
                             
                            
                                // Write each item in the dictionary out to the console:
                            
                                foreach (var kvp in token)
                            
                                {
                            
                                    Console.WriteLine("{0}: {1}", kvp.Key, kvp.Value);
                            
                                }
                            
                             
                            
                                Console.WriteLine("");
                            
                             
                            
                                Console.WriteLine("Getting User Info:");
                            
                                Console.WriteLine(GetUserInfo(token["access_token"]));
                            
                            }

                             

                            Take note – we are not passing in the entire token dictionary returned from the GetToken() method here – only the actual access_token itself. In this code, we are pulling it out of our token dictionary using the ”access_token” key when we pass it as an argument to the GetUserInfo() method.

                            If we run the Console application now, we get the following:

                            Running the Console Application and Passing a Proper Auth Token:
                            Registration Status Code: BadRequest
                            
                            Access Token:
                            
                            System.Collections.Generic.Dictionary`2[System.String,System.String]
                            
                            access_token: ygYowtlVKbwyd3J9Lown2Py2IcMEwGgjfS5YAbJJjlhADh4HURG6upqIah4zQjqLgH
                            
                            MjlyuiwKEcpzDv95Y0OpIqGO5pU_I4MmHNnLttMFORDFo-u4B0q9KUsiGskHjt_q25cIy5ZZNAejmA4B
                            
                            u8qJKuxWagK33-XlQYMD_USVTShfUFkjMpi7IxffPmjpzWl5ipUzxnu4t-4LpR87QuWwIv7novf_o8Sl
                            
                            9EAXc7ySqDZ0SzB1WgtDK4or7oLeIFMkouwOD9PK-E3FJTTmfpPtXT6RIdL93FEYM5oxgxTiHSLt_cRL
                            
                            1Mb5kyIILcl6dCR7OuGn_8QN3jabKOmXg5q5XE52m--BMzJwUESTzXjDge-_2XoNWI09jTki9RXWg2fV
                            
                            PL7DIhSwSfIff8AE0hiZm2cvEYaqPHzej221TKI_YX9DQGOrtmfLLpxx_lmtfbN1rbnwYYSa51d_vPDV
                            
                            yzsfZbC2vA-xzxWJS3LP4Qm_I8ZvJp-JKVu47Q-Y5Z0ZG_
                            
                            token_type: bearer
                            
                            expires_in: 1209599
                            
                            userName: john@example.com
                            
                            .issued: Sun, 21 Sep 2014 02:23:36 GMT
                            
                            .expires: Sun, 05 Oct 2014 02:23:36 GMT
                            
                             
                            
                            Getting User Info:
                            
                            {"Email":"john@example.com","HasRegistered":true,"LoginProvider":null}

                             

                            Now, that’s more like it.

                            Clean Up that Damn Code!

                            You may have notices that our examples have begun to take on some “code smells.” Well, for one, these are just examples, dammit. However, we can immediately see some opportunities to clean things up a bit, do a little refactoring.

                            How you would actually approach this would totally depend upon your specific project needs. But we can see right away that every time we go to access our Web Api from our client, we need an instance of HttpClient . Also, it is very likely that more often than not, we will need to be including an Auth token as part of our request.

                            We might add a static factory method, CreateClient() to handle this for us, and then call this from each of our client methods:

                            Add a CreateClient() Method (for example):
                            static HttpClient CreateClient(string accessToken = "")
                            
                            {
                            
                                var client = new HttpClient();
                            
                                if(!string.IsNullOrWhiteSpace(accessToken))
                            
                                {
                            
                                    client.DefaultRequestHeaders.Authorization = 
                            
                                        new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
                            
                                }
                            
                                return client;            
                            
                            }

                             

                            Now, if a token is provided, the default Authorization header will be added. If not, an HttpClient will be returned without Authorization header.

                            Now, our GetUserInfo() method from the Console application might look like this:

                            Modified GetUserInfo() method:
                            static string GetUserInfo(string token)
                            
                            {
                            
                                using(var client = CreateClient(token))
                            
                                {
                            
                                    var response = client.GetAsync("http://localhost:62069/api/Account/UserInfo").Result;
                            
                                    return response.Content.ReadAsStringAsync().Result;
                            
                                }
                            
                            }

                             

                            We still have the using block, and we really only saved one line of code. However, the initialization of the HttpClient, and the setting of the access token now all happen in the same place.

                            A Note About Security, Bearer Tokens, and SSL

                            As mentioned earlier, an ASP.NET Web Api project uses Bearer tokens out of the box when using the default Visual Studio project template. In order to implement proper security, Api’s which use this sort of authentication should ALWAYS use SSL/TLS, especially when accepting user credentials in order to provide the access token at the Token endpoint.

                            What the Default ASP.NET Web Api Project does NOT Have

                            You may notice that there are no Role management methods, no RolesAdminController (or even a UserAdminController) in the basic Identity-based Web Api project. In fact, there are no Roles at all at this early stage. The AccountController contains methods sufficient to Register a new user, perform some basic management (such as changing passwords), and not much else.

                            If we want role-based authorization in our Web Api project, we will need to add it ourselves. Similarly, if we want a little more admin flexibility via our Api (or even using a few MVC pages within the application for GUI-based Administration), we will also need to add that ourselves.

                            Last, before we charge in adding Roles and such, we will want to examine whether Role-Based Authorization is the best fit for our needs. Identity 2.0, and Web Api easily support Claims-Based Authorization, which offers some distinct advantages for more complex authorization scenarios.

                            We will look at all of this, and more, in upcoming posts.

                            Looking Deeper into Identity 2.0 and Web Api

                            In this post, we’ve taken a very broad look at the structure of a Web Api 2.2 project, and where the major pieces of the Identity 2.0 framework fit. We’ve looked at using Bearer tokes as the default authentication mechanism, how to retrieve a token from your Api Token endpoint, and how to perform some very basic Api access.

                            In upcoming posts we will:

                            • Look more closely at Token-based authentication and authorization, and how it relates to the familiar Users and Roles model we are familiar with
                            • Extend Identity 2.0 models in the context of ASP.NET Web Api
                            • Serializing and de-serializing Model data to and from HTTP requests and responses
                            • Customizing Identity Management for Web Api
                            • Creating an Authentication Service using Identity 2.0 and ASP.NET Web Api

                            This has been basically the 101-level introduction to using Identity 2.0 and ASP.NET Web Api. There is more to come, and a lot to understand in order to get the most out of the ASP.NET Identity framework in a Web Api context, and to properly secure a Web Api.

                            Additional Resources and Items of Interest

                             

                            Posted on September 21 2014 07:09 PM by jatten     

                            Comments (2)

                            About the author

                            My name is John Atten, and my "handle" on many of my online accounts is xivSolutions. I am Fascinated by all things technology and software development. I work mostly with C#, JavaScript/Node, and databases of many flavors. Actively learning always. I dig web development. I am always looking for new information, and value your feedback (especially where I got something wrong!). You can email me at:

                            jatten at typecastexception dot com

                            Web Hosting by