Click here to Skip to main content
15,115,261 members
Articles / Web Development
Technical Blog
Posted 16 Feb 2015

Stats

117.5K views
31 bookmarked

Implement OAuth JSON Web Tokens Authentication in ASP.NET Web API and Identity 2.1 – Part 3

Rate me:
Please Sign up or sign in to vote.
4.93/5 (16 votes)
16 Feb 2015CPOL10 min read
Part 3: Tutorial shows how to implement OAuth JSON Web Tokens Authentication (JWT) using ASP.NET Web API 2.2 and ASP.NET Identity 2.1

This is the third part of Building Simple Membership system using ASP.NET Identity 2.1, ASP.NET Web API 2.2 and AngularJS. The topics we’ll cover are:

The source code for this tutorial is available on GitHub.

Implement JSON Web Tokens Authentication in ASP.NET Web API and Identity 2.1

Featured Image

Currently our API doesn’t support authentication and authorization, all the requests we receive to any end point are done anonymously, In this post, we’ll configure our API which will act as our Authorization Server and Resource Server at the same time to issue JSON Web Tokens for authenticated users and those users will present this JWT to the protected end points in order to access it and process the request.

I will use a step by step approach as usual to implement this, but I highly recommend you to read the post JSON Web Token in ASP.NET Web API 2 before completing this one; where I cover deeply what is JSON Web Tokens, the benefits of using JWT over default access tokens, and how they can be used to decouple Authorization server from Resource server. In this tutorial and for the sake of keeping it simple; both OAuth 2.0 roles (Authorization Server and Recourse Server) will live in the same API.

Step 1: Implement OAuth 2.0 Resource Owner Password Credential Flow

We are going to build an API which will be consumed by a trusted client (AngularJS front-end) so we are only interested in implementing a single OAuth 2.0 flow where the registered user will present username and password to a specific end point, and the API will validate those credentials, and if all is valid, it will return a JWT for the user where the client application used by the user should store it securely and locally in order to present this JWT with each request to any protected end point.

The nice thing about this JWT is that it is a self contained token which contains all user claims and roles inside it, so there is no need to do any extra DB queries to fetch those values for the authenticated user. This JWT token will be configured to expire after 1 day of its issue date, so the user is requested to provide credentials again in order to obtain new JWT token.

If you are interested in knowing how to implement sliding expiration tokens and how you can keep the user logged in; I recommend you read my other post Enable OAuth Refresh Tokens in AngularJS App which covers this in depth, but adds more complexity to the solution. To keep this tutorial simple, we’ll not add refresh tokens here but you can refer to the post and implement it.

To implement the Resource Owner Password Credential flow; we need to add a new folder named “Providers” then add a new class named “CustomOAuthProvider”, after you add then paste the code below:

C#
public class CustomOAuthProvider : OAuthAuthorizationServerProvider
    {
        public override Task ValidateClientAuthentication
		(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
            return Task.FromResult<object>(null);
        }

        public override async Task GrantResourceOwnerCredentials
		(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var allowedOrigin = "*";

            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", 
		new[] { allowedOrigin });

            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;
            }

            if (!user.EmailConfirmed)
            {
                context.SetError("invalid_grant", "User did not confirm email.");
                return;
            }

            ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
        
            var ticket = new AuthenticationTicket(oAuthIdentity, null);
            
            context.Validated(ticket);           
        }
    }

This class inherits from class “OAuthAuthorizationServerProvider” and overrides the below two methods:

  • As you notice, the “ValidateClientAuthentication” is empty, we are considering the request valid always, because in our implementation our client (AngularJS front-end) is a trusted client and we do not need to validate it.
  • The method “GrantResourceOwnerCredentials” is responsible for receiving the username and password from the request and validating them against our ASP.NET 2.1 Identity system, if the credentials are valid and the email is confirmed, we are building an identity for the logged in user, this identity will contain all the roles and claims for the authenticated user, until now we didn’t cover roles and claims part of the tutorial, but for the mean time you can consider all users registered in our system without any roles or claims mapped to them.
  • The method “GenerateUserIdentityAsync” is not implemented yet, we’ll add this helper method in the next step. This method will be responsible to fetch the authenticated user identity from the database and returns an object of type “ClaimsIdentity”.
  • Lastly, we are creating an Authentication ticket which contains the identity for the authenticated user, and when we call “context.Validated(ticket)” this will transfer this identity to an OAuth 2.0 bearer access token.

Step 2: Add Method “GenerateUserIdentityAsync” to “ApplicationUser” Class

Now we’ll add the helper method which will be responsible to get the authenticated user identity (all roles and claims mapped to the user). The “UserManager” class contains a method named “CreateIdentityAsync” to do this task, it will basically query the DB and get all the roles and claims for this user, to implement this open class “ApplicationUser” and paste the code below:

C#
//Rest of code is removed for brevity
public async Task<ClaimsIdentity> GenerateUserIdentityAsync
(UserManager<ApplicationUser> manager, string authenticationType)
{
	var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
	// Add custom user claims here
	return userIdentity;
}

Step 3: Issue JSON Web Tokens instead of Default Access Tokens

Now we want to configure our API to issue JWT tokens instead of default access tokens, to understand what is JWT and why it is better to use it, you can refer back to this post.

First thing is we need to install 2 NueGet packages as below:

Install-package System.IdentityModel.Tokens.Jwt -Version 4.0.1
Install-package Thinktecture.IdentityModel.Core -Version 1.3.0

There is no direct support for issuing JWT in ASP.NET Web API, so in order to start issuing JWTs, we need to implement this manually by implementing the interface “ISecureDataFormat” and implement the method “Protect”.

To implement this, add new file named “CustomJwtFormat” under folder “Providers” and paste the code below:

C#
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
    {    
        private readonly string _issuer = string.Empty;

        public CustomJwtFormat(string issuer)
        {
            _issuer = issuer;
        }

        public string Protect(AuthenticationTicket data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];

            string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];

            var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);

            var signingKey = new HmacSigningCredentials(keyByteArray);

            var issued = data.Properties.IssuedUtc;
            
            var expires = data.Properties.ExpiresUtc;

            var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, 
            issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);

            var handler = new JwtSecurityTokenHandler();

            var jwt = handler.WriteToken(token);

            return jwt;
        }

        public AuthenticationTicket Unprotect(string protectedText)
        {
            throw new NotImplementedException();
        }
    }

What we’ve implemented in this class is the following:

  • The class “CustomJwtFormat” implements the interface “ISecureDataFormat<AuthenticationTicket>”, the JWT generation will take place inside method “Protect”.
  • The constructor of this class accepts the “Issuer” of this JWT which will be our API. This API acts as Authorization and Resource Server on the same time, this can be string or URI, in our case we’ll fix it to URI.
  • Inside “Protect” method, we are doing the following:
    • As we stated before, this API serves as Resource and Authorization Server at the same time, so we are fixing the Audience Id and Audience Secret (Resource Server) in web.config file, this Audience Id and Secret will be used for HMAC265 and hash the JWT token, I’ve used this implementation to generate the Audience Id and Secret.
    • Do not forget to add 2 new keys “as:AudienceId” and “as:AudienceSecret” to the web.config AppSettings section.
    • Then we prepare the raw data for the JSON Web Token which will be issued to the requester by providing the issuer, audience, user claims, issue date, expiry date, and the signing key which will sign (hash) the JWT payload.
    • Lastly, we serialize the JSON Web Token to a string and return it to the requester.
  • By doing this, the requester for an OAuth 2.0 access token from our API will receive a signed token which contains claims for an authenticated Resource Owner (User) and this access token is intended to certain (Audience) as well.

Step 4: Add Support for OAuth 2.0 JWT Generation

Till this moment, we didn’t configure our API to use OAuth 2.0 Authentication workflow, to do so open class “Startup” and add new method named “ConfigureOAuthTokenGeneration” as shown below:

C#
private void ConfigureOAuthTokenGeneration(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);

            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                //For Dev environment only (on production should be AllowInsecureHttp = false)
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/oauth/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = new CustomOAuthProvider(),
                AccessTokenFormat = new CustomJwtFormat("http://localhost:59822")
            };

            // OAuth 2.0 Bearer Access Token Generation
            app.UseOAuthAuthorizationServer(OAuthServerOptions);
        }

What we’ve implemented here is the following:

  • The path for generating JWT will be as :”http://localhost:59822/oauth/token”.
  • We’ve specified the expiry for token to be 1 day.
  • We’ve specified the implementation on how to validate the Resource owner user credential in a custom class named “CustomOAuthProvider”.
  • We’ve specified the implementation on how to generate the access token using JWT formats, this custom class named “CustomJwtFormat” will be responsible for generating JWT instead of default access token using DPAPI, note that both format will use Bearer scheme.

Do not forget to call the new method “ConfigureOAuthTokenGeneration” in the Startup “Configuration” as the class below:

C#
public void Configuration(IAppBuilder app)
{
	HttpConfiguration httpConfig = new HttpConfiguration();

	ConfigureOAuthTokenGeneration(app);

	//Rest of the code is removed for brevity
}

Our API currently is ready to start issuing JWT access token, so test this out, we can issue HTTP POST request as the image below, and we should receive a valid JWT token for the next 24 hours and accepted only by our API.

JSON Web Token

Step 5: Protect the Existing End Points with [Authorize] Attribute

Now we’ll visit all the end points we have created earlier in previous posts in the “AccountsController” class, and attribute the end points which need to be protected (only authentication user with valid JWT access token can access it) with the [Authorize] attribute as shown below:

GetUsers, GetUser, GetUserByName, and DeleteUser end points should be accessed by users enrolled in Role “Admin”. Roles Authorization is not implemented yet and for now we will only allow any authentication user to access it, the code change will be as simple as shown below:

C#
[Authorize]
[Route("users")]
public IHttpActionResult GetUsers()
{}

[Authorize]
[Route("user/{id:guid}", Name = "GetUserById")]
public async Task<IHttpActionResult> GetUser(string Id)
{}

[Authorize]
[Route("user/{username}")]
public async Task<IHttpActionResult> GetUserByName(string username)
{
}

[Authorize]
[Route("user/{id:guid}")]
public async Task<IHttpActionResult> DeleteUser(string id)
{
}

CreateUser and ConfirmEmail endpoints should be accessed anonymously always, so we need to attribute it with [AllowAnonymous] as shown below:

C#
[AllowAnonymous]
[Route("create")]
public async Task<IHttpActionResult> CreateUser(CreateUserBindingModel createUserModel)
{
}

[AllowAnonymous]
[HttpGet]
[Route("ConfirmEmail", Name = "ConfirmEmailRoute")]
public async Task<IHttpActionResult> 
ConfirmEmail(string userId = "", string code = "")
{
}

ChangePassword endpoint should be accessed by the authenticated user only, so we’ll attribute it with [Authorize] attribute as given below:

C#
[Authorize]
[Route("ChangePassword")]
public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model)
{
}

Step 6: Consume JSON Web Tokens

Now if we tried to obtain an access token by sending a request to the end point “oauth/token”, then try to access one of the protected end points we’ll receive 401 Unauthorized status, the reason for this that our API doesn’t understand those JWT tokens issued by our API yet, to fix this we need to the following:

Install the below NuGet package:

C#
Install-Package Microsoft.Owin.Security.Jwt -Version 3.0.0

The package “Microsoft.Owin.Security.Jwt” is responsible for protecting the Resource server resources using JWT, it only validate and de-serialize JWT tokens.

Now back to our “Startup” class, we need to add the below method “ConfigureOAuthTokenConsumption” as the below:

C#
private void ConfigureOAuthTokenConsumption(IAppBuilder app) {

            var issuer = "http://localhost:59822";
            string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
            byte[] audienceSecret = TextEncodings.Base64Url.Decode
            (ConfigurationManager.AppSettings["as:AudienceSecret"]);

            // Api controllers with an [Authorize] attribute will be validated with JWT
            app.UseJwtBearerAuthentication(
                new JwtBearerAuthenticationOptions
                {
                    AuthenticationMode = AuthenticationMode.Active,
                    AllowedAudiences = new[] { audienceId },
                    IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                    {
                        new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
                    }
                });
        }

This step will configure our API to trust tokens issued by our Authorization server only, in our case the Authorization and Resource Server are the same server (http://localhost:59822), notice how we are providing the values for audience, and the audience secret we used to generate and issue the JSON Web Token in step 3.

By providing those values to the “JwtBearerAuthentication” middleware, our API will be able to consume only JWT tokens issued by our trusted Authorization server, any other JWT tokens from any other Authorization server will be rejected.

Lastly, we need to call the method “ConfigureOAuthTokenConsumption” in the “Configuration” method as the below:

C#
public void Configuration(IAppBuilder app)
	{
		HttpConfiguration httpConfig = new HttpConfiguration();

		ConfigureOAuthTokenGeneration(app);

		ConfigureOAuthTokenConsumption(app);
		
		//Rest of the code is here
	}

Step 7: Final Testing

All the pieces should be in place now, to test this, we will obtain JWT access token for the user “SuperPowerUser” by issuing POST request to the end point “oauth/token”.

Request JWT Token

Then, we will use the JWT received to access protected end point such as “ChangePassword”, if you remember once we added this end point, we were not able to test it directly because it was anonymous and inside its implementation, we were calling the method “User.Identity.GetUserId()”. This method will return nothing for anonymous user, but after we’ve added the [Authorize] attribute, any user needs to access this end point should be authenticated and has a valid JWT.

To test this out, we will issue POST request to the end point “/accounts/ChangePassword” as the image below, notice that we are setting the Authorization header using Bearer scheme setting its value to the JWT we received for the user “SuperPowerUser”. If all is valid, we will receive 200 OK status and the user password should be updated.

Change Password Web API

The source code for this tutorial is available on GitHub.

In the next post, we’ll see how we’ll implement Roles Based Authorization in our Identity service.

Follow me on Twitter @tjoudeh.

References

The post Implement OAuth JSON Web Tokens Authentication in ASP.NET Web API and Identity 2.1 – Part 3 appeared first on Bit of Technology.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Taiseer Joudeh
Architect Aramex
Jordan Jordan
Taiseer Joudeh has more than 8 years of experience spent in developing and managing different software solutions for finance, transportation, logistics, and e-commerce sectors. He has been deeply involved in .NET development since early framework versions and currently he works on different technologies on the ASP.NET stack with deep passion for Web API, and Microsoft Azure.

Recently Taiseer has been focusing on building Single Page Applications and Hybrid Mobile Solutions using AngularJS.

Taiseer lives in Jordan with his wife and son, works as IT Manager at Aramex, also he is a regular speaker in local events and Dev user groups, he is a avid blogger on http://bitoftech.net, and you can follow him on twitter @tjoudeh

Comments and Discussions

 
SuggestionNice example, but needs update Pin
Member 91737589-May-18 10:42
MemberMember 91737589-May-18 10:42 
QuestionJWT Token - Consume REST API SP2013 Pin
Koolraj5432130-Jul-17 22:03
MemberKoolraj5432130-Jul-17 22:03 
QuestionHow to get AspNetIdentity database? Pin
panesar_200624-May-16 22:29
Memberpanesar_200624-May-16 22:29 
QuestionMy vote of 5 and a question Pin
Dutchpower23-Jun-15 0:08
MemberDutchpower23-Jun-15 0:08 
QuestionMy vote of 5 Pin
DiponRoy3-Jun-15 0:44
mvaDiponRoy3-Jun-15 0:44 
QuestionThanks, need help Pin
Member 36788020-Apr-15 8:39
MemberMember 36788020-Apr-15 8:39 
GeneralMy vote of 5 Pin
saxenaabhi68-Apr-15 18:24
Membersaxenaabhi68-Apr-15 18:24 
QuestionWhere store the token to pass in each call Pin
demodude8-Apr-15 7:09
Memberdemodude8-Apr-15 7:09 
AnswerRe: Where store the token to pass in each call Pin
tahirk5552-Oct-16 5:58
professionaltahirk5552-Oct-16 5:58 
GeneralMy vote of 5 Pin
John Atten17-Feb-15 3:05
MemberJohn Atten17-Feb-15 3:05 
Nicely done, as always...

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.