banner



How To Create Token Based Authentication In Web Api

Last week I was looking at the top viewed posts on my blog and I noticed that visitors are interested in the authentication part of ASP.NET Web API, CORS Support, and how to authenticate users in single page applications built with AngularJS using token based approach.

So I decided to compile mini tutorial of three five posts which covers and connects those topics. In this tutorial we'll build SPA using AngularJS for the front-end, and ASP.NET Web API 2, Owin middleware, and ASP.NET Identity for the back-end.

  • AngularJS Token Authentication using ASP.NET Web API 2, Owin, and ASP.NET Identity – Part 2.
  • Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin – Part 3.
  • ASP.NET Web API 2 external logins with Facebook and Google in AngularJS app – Part 4.
  • Decouple OWIN Authorization Server from Resource Server – Part 5.

  • New post Aug-2016 : Secure ASP.NET Web API 2 Using Azure AD B2C (Business to Consumer).
  • New post: Two Factor Authentication in ASP.NET Web API & AngularJS using Google Authenticator.

The demo application can be accessed on (http://ngAuthenticationWeb.azurewebsites.net). The back-end API can be accessed on (http://ngAuthenticationAPI.azurewebsites.net/) and both are hosted on Microsoft Azure, for learning purposes feel free to integrate and play with the back-end API with your front-end application. The API supports CORS and accepts HTTP calls from any origin. You can check the source code for this tutorial on Github.

AngularJS Authentication

Token Based Authentication

As I stated before we'll use token based approach to implement authentication between the front-end application and the back-end API, as we all know the common and old way to implement authentication is the cookie-based approach were the cookie is sent with each request from the client to the server, and on the server it is used to identify the authenticated user.

With the evolution of front-end frameworks and the huge change on how we build web applications nowadays the preferred approach to authenticate users is to use signed token as this token sent to the server with each request, some of the benefits for using this approach are:

  • Scalability of Servers: The token sent to the server is self contained which holds all the user information needed for authentication, so adding more servers to your web farm is an easy task, there is no dependent on shared session stores.
  • Loosely Coupling: Your front-end application is not coupled with specific authentication mechanism, the token is generated from the server and your API is built in a way to understand this token and do the authentication.
  • Mobile Friendly: Cookies and browsers like each other, but storing cookies on native platforms (Android, iOS, Windows Phone) is not a trivial task, having standard way to authenticate users will simplify our life if we decided to consume the back-end API from native applications.

What we'll build in this tutorial?

The front-end SPA will be built using HTML5, AngularJS, and Twitter Bootstrap. The back-end server will be built using ASP.NET Web API 2 on top of Owin middleware not directly on top of ASP.NET; the reason for doing so that we'll configure the server to issue OAuth bearer token authentication using Owin middleware too, so setting up everything on the same pipeline is better approach. In addition to this we'll use ASP.NET Identity system which is built on top of Owin middleware and we'll use it to register new users and validate their credentials before generating the tokens.

As I mentioned before our back-end API should accept request coming from any origin, not only our front-end, so we'll be enabling CORS (Cross Origin Resource Sharing) in Web API as well for the OAuth bearer token provider.

Use cases which will be covered in this application:

  • Allow users to signup (register) by providing username and password then store credentials in secure medium.
  • Prevent anonymous users from viewing secured data or secured pages (views).
  • Once the user is logged in successfully, the system should not ask for credentials or re-authentication for the next 24 hours 30 minutes because we are using refresh tokens.

So in this post we'll cover step by step how to build the back-end API, and on the next post we'll cover how we'll build and integrate the SPA with the API.

Enough theories let's get our hands dirty and start implementing the API!

Building the Back-End API

Step 1: Creating the Web API Project

In this tutorial I'm using Visual Studio 2013 and .Net framework 4.5, you can follow along using Visual Studio 2012 but you need to install Web Tools 2013.1 for VS 2012 by visiting this link.

Now create an empty solution and name it "AngularJSAuthentication" then add new ASP.NET Web application named "AngularJSAuthentication.API", the selected template for project will be as the image below. Notice that the authentication is set to "No Authentication" taking into consideration that we'll add this manually.

Web API Project Template

Step 2: Installing the needed NuGet Packages:

Now we need to install the NuGet packages which are needed to setup our Owin server and configure ASP.NET Web API to be hosted within an Owin server, so open NuGet Package Manger Console and type the below:

Install-Package Microsoft.AspNet.WebApi.Owin -Version 5.1.2

Install-Package Microsoft.Owin.Host.SystemWeb -Version 2.1.0

The  package "Microsoft.Owin.Host.SystemWeb" is used to enable our Owin server to run our API on IIS using ASP.NET request pipeline as eventually we'll host this API on Microsoft Azure Websites which uses IIS.

Step 3: Add Owin "Startup" Class

Right click on your project then add new class named "Startup". We'll visit this class many times and modify it, for now it will contain the code below:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

using Microsoft . Owin ;

using Owin ;

using System ;

using System . Collections . Generic ;

using System . Linq ;

using System . Web ;

using System . Web . Http ;

[ assembly : OwinStartup ( typeof ( AngularJSAuthentication . API . Startup ) ) ]

namespace AngularJSAuthentication . API

{

public class Startup

{

public void Configuration ( IAppBuilder app )

{

HttpConfiguration config = new HttpConfiguration ( ) ;

WebApiConfig . Register ( config ) ;

app . UseWebApi ( config ) ;

}

}

}

What we've implemented above is simple, this class will be fired once our server starts, notice the "assembly" attribute which states which class to fire on start-up. The "Configuration" method accepts parameter of type "IAppBuilder" this parameter will be supplied by the host at run-time. This "app" parameter is an interface which will be used to compose the application for our Owin server.

The "HttpConfiguration" object is used to configure API routes, so we'll pass this object to method "Register" in "WebApiConfig" class.

Lastly, we'll pass the "config" object to the extension method "UseWebApi" which will be responsible to wire up ASP.NET Web API to our Owin server pipeline.

Usually the class "WebApiConfig" exists with the templates we've selected, if it doesn't exist then add it under the folder "App_Start". Below is the code inside it:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public static class WebApiConfig

{

public static void Register ( HttpConfiguration config )

{

// Web API routes

config . MapHttpAttributeRoutes ( ) ;

config . Routes . MapHttpRoute (

name : "DefaultApi" ,

routeTemplate : "api/{controller}/{id}" ,

defaults : new { id = RouteParameter . Optional }

) ;

var jsonFormatter = config . Formatters . OfType < JsonMediaTypeFormatter > ( ) . First ( ) ;

jsonFormatter . SerializerSettings . ContractResolver = new CamelCasePropertyNamesContractResolver ( ) ;

}

}

Step 4: Delete Global.asax Class

No need to use this class and fire up the Application_Start event after we've configured our "Startup" class so feel free to delete it.

Step 5: Add the ASP.NET Identity System

After we've configured the Web API, it is time to add the needed NuGet packages to add support for registering and validating user credentials, so open package manager console and add the below NuGet packages:

Install-Package Microsoft.AspNet.Identity.Owin -Version 2.0.1

Install-Package Microsoft.AspNet.Identity.EntityFramework -Version 2.0.1

The first package will add support for ASP.NET Identity Owin, and the second package will add support for using ASP.NET Identity with Entity Framework so we can save users to SQL Server database.

Now we need to add Database context class which will be responsible to communicate with our database, so add new class and name it "AuthContext" then paste the code snippet below:

public class AuthContext : IdentityDbContext < IdentityUser >

{

public AuthContext ( )

: base ( "AuthContext" )

{

}

}

As you can see this class inherits from "IdentityDbContext" class, you can think about this class as special version of the traditional "DbContext" Class, it will provide all of the Entity Framework code-first mapping and DbSet properties needed to manage the identity tables in SQL Server. You can read more about this class on Scott Allen Blog.

Now we want to add "UserModel" which contains the properties needed to be sent once we register a user, this model is POCO class with some data annotations attributes used for the sake of validating the registration payload request. So under "Models" folder add new class named "UserModel" and paste the code below:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public class UserModel

{

[ Required ]

[ Display ( Name = "User name" ) ]

public string UserName { get ; set ; }

[ Required ]

[ StringLength ( 100 , ErrorMessage = "The {0} must be at least {2} characters long." , MinimumLength = 6 ) ]

[ DataType ( DataType . Password ) ]

[ Display ( Name = "Password" ) ]

public string Password { get ; set ; }

[ DataType ( DataType . Password ) ]

[ Display ( Name = "Confirm password" ) ]

[ Compare ( "Password" , ErrorMessage = "The password and confirmation password do not match." ) ]

public string ConfirmPassword { get ; set ; }

}

Now we need to add new connection string named "AuthContext" in our Web.Config class, so open you web.config and add the below section:

<connectionStrings>

<add name = "AuthContext" connectionString = "Data Source=.\sqlexpress;Initial Catalog=AngularJSAuth;Integrated Security=SSPI;" providerName = "System.Data.SqlClient" />

</connectionStrings>

Step 6: Add Repository class to support ASP.NET Identity System

Now we want to implement two methods needed in our application which they are: "RegisterUser" and "FindUser", so add new class named "AuthRepository" and paste the code snippet below:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

public class AuthRepository : IDisposable

{

private AuthContext _ctx ;

private UserManager < IdentityUser > _userManager ;

public AuthRepository ( )

{

_ctx = new AuthContext ( ) ;

_userManager = new UserManager < IdentityUser > ( new UserStore < IdentityUser > ( _ctx ) ) ;

}

public async Task < IdentityResult > RegisterUser ( UserModel userModel )

{

IdentityUser user = new IdentityUser

{

UserName = userModel . UserName

} ;

var result = await _userManager . CreateAsync ( user , userModel . Password ) ;

return result ;

}

public async Task < IdentityUser > FindUser ( string userName , string password )

{

IdentityUser user = await _userManager . FindAsync ( userName , password ) ;

return user ;

}

public void Dispose ( )

{

_ctx . Dispose ( ) ;

_userManager . Dispose ( ) ;

}

}

What we've implemented above is the following: we are depending on the "UserManager" that provides the domain logic for working with user information. The "UserManager" knows when to hash a password, how and when to validate a user, and how to manage claims. You can read more about ASP.NET Identity System.

Step 7: Add our "Account" Controller

Now it is the time to add our first Web API controller which will be used to register new users, so under file "Controllers" add Empty Web API 2 Controller named "AccountController" and paste the code below:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

[ RoutePrefix ( "api/Account" ) ]

public class AccountController : ApiController

{

private AuthRepository _repo = null ;

public AccountController ( )

{

_repo = new AuthRepository ( ) ;

}

// POST api/Account/Register

[ AllowAnonymous ]

[ Route ( "Register" ) ]

public async Task < IHttpActionResult > Register ( UserModel userModel )

{

if ( ! ModelState . IsValid )

{

return BadRequest ( ModelState ) ;

}

IdentityResult result = await _repo . RegisterUser ( userModel ) ;

IHttpActionResult errorResult = GetErrorResult ( result ) ;

if ( errorResult != null )

{

return errorResult ;

}

return Ok ( ) ;

}

protected override void Dispose ( bool disposing )

{

if ( disposing )

{

_repo . Dispose ( ) ;

}

base . Dispose ( disposing ) ;

}

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 ;

}

}

By looking at the "Register" method you will notice that we've configured the endpoint for this method to be "/api/account/register" so any user wants to register into our system must issue HTTP POST request to this URI and the pay load for this request will contain the JSON object as below:

{

"userName" : "Taiseer" ,

"password" : "SuperPass" ,

"confirmPassword" : "SuperPass"

}

Now you can run your application and issue HTTP POST request to your local URI: "http://localhost:port/api/account/register" or you can try the published API using this end point: http://ngauthenticationapi.azurewebsites.net/api/account/register if all went fine you will receive HTTP status code 200 and the database specified in connection string will be created automatically and the user will be inserted into table "dbo.AspNetUsers".

Note: It is very important to send this POST request over HTTPS so the sensitive information get encrypted between the client and the server.

The "GetErrorResult" method is just a helper method which is used to validate the "UserModel" and return the correct HTTP status code if the input data is invalid.

Step 8: Add Secured Orders Controller

Now we want to add another controller to serve our Orders, we'll assume that this controller will return orders only for Authenticated users, to keep things simple we'll return static data. So add new controller named "OrdersController" under "Controllers" folder and paste the code below:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

[ RoutePrefix ( "api/Orders" ) ]

public class OrdersController : ApiController

{

[ Authorize ]

[ Route ( "" ) ]

public IHttpActionResult Get ( )

{

return Ok ( Order . CreateOrders ( ) ) ;

}

}

#region Helpers

public class Order

{

public int OrderID { get ; set ; }

public string CustomerName { get ; set ; }

public string ShipperCity { get ; set ; }

public Boolean IsShipped { get ; set ; }

public static List < Order > CreateOrders ( )

{

List < Order > OrderList = new List < Order >

{

new Order { OrderID = 10248 , CustomerName = "Taiseer Joudeh" , ShipperCity = "Amman" , IsShipped = true } ,

new Order { OrderID = 10249 , CustomerName = "Ahmad Hasan" , ShipperCity = "Dubai" , IsShipped = false } ,

new Order { OrderID = 10250 , CustomerName = "Tamer Yaser" , ShipperCity = "Jeddah" , IsShipped = false } ,

new Order { OrderID = 10251 , CustomerName = "Lina Majed" , ShipperCity = "Abu Dhabi" , IsShipped = false } ,

new Order { OrderID = 10252 , CustomerName = "Yasmeen Rami" , ShipperCity = "Kuwait" , IsShipped = true }

} ;

return OrderList ;

}

}

0 Response to "How To Create Token Based Authentication In Web Api"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel