Codementor Events

JWT Authentication and Authorization on Web API using OWIN pipeline and OAuth Grant

Published May 25, 2020

JWT Authentication and Authorization on Web API using OWIN pipeline and OAuth Grant
1.Create New Project -> Asp.net web Applications -> Empty project, Check Template MVC and WebAPI both. Make sure Authentication is No Authentication chosen. Press OK.

2.Once Project is created, Right click on project and select to add class file.
Give this class name ->
MyAuthorizationServerProvider : OAuthAuthorizationServerProvider
This is inherit so that we can override two of its functions.

I.ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
-> Is used to validate our client application.

II.GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
-> We will validate credentials of User, if user is validated, we will generate signed token using which user will access authorized resources of server.

public class MyAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated(); // we just validated our client app
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var identity = new ClaimsIdentity(context.Options.AuthenticationType);
        if (context.UserName == "admin" && context.Password == "admin")
        {
            identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
            identity.AddClaim(new Claim("username", "admin"));
            identity.AddClaim(new Claim(ClaimTypes.Name, "Himanshu Dewangan"));
            context.Validated(identity);
        }
        else if (context.UserName == "user" && context.Password == "user")
        {
            identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
            identity.AddClaim(new Claim("username", "user"));
            identity.AddClaim(new Claim(ClaimTypes.Name, "Ravi Voleti"));
            context.Validated(identity);
        }
        else
        {
            context.SetError("invalid_grant", "Provided username and password is incorrect");
            return;
        }
    }
}

3.Now setup OWIN Startup class file name it – Startup.cs
Here we Configure OAuthAuthorizationServer and define OAuthAuthorizationServerOptions
This Startup.cs required an assembly reference to get identified.
[assembly: OwinStartup(typeof(WebApi.Startup))]
This Startup.cs file is entry point of Owin selfhost pipeline on top of IIS

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);//enable cors origin requests

        //Configuring OAuthAuthorizationServer
        var myProvider = new MyAuthorizationServerProvider();//getting refrence of my provider

        //Defining OAuthAuthorizationServer Options
        OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
        {
            AllowInsecureHttp = true,//do not allow in development environment
            TokenEndpointPath = new PathString("/token"),//this is path where user will get token,after getting validated
            //AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(10),
            Provider = myProvider
        };

        app.UseOAuthAuthorizationServer(options);
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

        //Register WebAPI congfig
        HttpConfiguration config = new HttpConfiguration();
        WebApiConfig.Register(config);
    }

4.Now we will add custom AuthorizeAttribute, reason here is default authorize attribute always give 401, which means Unauthorized - This error indicates that you need to perform authentication before accessing the resource.
But what happens, sometime is Request is not authenticated then its 401, but if request is authenticated, and it’s not authorized to access that resources or has no rights to perform certain operations (i,e Create,Delete) then its 403.

Hence override is required.

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        if (!HttpContext.Current.User.Identity.IsAuthenticated)
        {
            base.HandleUnauthorizedRequest(actionContext);
        }
        else
        {
            actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
        }
    }
} 

5.Now we will add Controller, Go ahead select Controller folder in WebApi project and Right click -> select option Add Controller.
This controller will be having 3 Action Methods
//this action for all anonymous users/ not authenticated
[AllowAnonymous]
[HttpGet]
[Route("api/data/forall")]
public IHttpActionResult Get()
{
return Ok("Now server time is: " + DateTime.Now.ToString());
}

    //this action for all type of authenticated users, whether it is Admin user or normal user
    [MyAuthorize]
    [HttpGet]
    [Route("api/data/authenticate")]
    public IHttpActionResult GetForAuthenticate()
    {
        var identity = (ClaimsIdentity)User.Identity;
        return Ok("Hello " + identity.Name);
    }

    // this action only for Admin role type users
    [MyAuthorize(Roles = "admin")]
    [HttpGet]
    [Route("api/data/authorize")]
    public IHttpActionResult GetForAdmin()
    {
        var identity = (ClaimsIdentity)User.Identity;
        var roles = identity.Claims
                    .Where(c => c.Type == ClaimTypes.Role)
                    .Select(c => c.Value);
        return Ok("Hello " + identity.Name + " Role: " + string.Join(",", roles.ToList()));
    }

6.Now go ahead press F5 and run the web api it will hosted in ie browser

7.Open Postman

Test 1: based on below screen shot execute postman to test Get() Action

Test 2: Token Generation for user

Test 3: Token Generation for admin

Now we will test Authenticate (GetforAuthenticate Action Method) using both the tokens

Test4: Test using Admin token

Test5: Test using User token

NOTE Make sure to Give Single Space between Bearer word and access token string (copied from token) inside Value of Header

Now we will test Authenticate (GetforAdmin Action Method) using both the tokens

Test 6: Test using Admin token

Test 6: Test using User token

As we can see in status showing 403 Forbidden (instead of 401 as effect of Default Authorize Attribute) it means our Custom Authorize Attribute is working.

Discover and read more posts from Himanshu Dewangan
get started