jth41
jth41

Reputation: 3906

Consume JWT by calling another service

There are many resources describing the implementation of JWT creation and consumption in C# using .Net Web API. However I am struggling to adapt my current system to any of the tutorials listed.

I have up to this point been sending my JWT token as part of the query string like so:

.../api/users?jwt=aladsfjknasdfnjkladfskjlnajknjknfdsalnjksdfnjldf

and my route would look something like this:

public async Task<IHttpActionResult> GetUsers(string jwt)
{
       var userValidationResult = await Utility.ValidateUser(jwt);

       if (!userValidationResult.Validated)
       {
           return Unauthorized();
       }

       ...
}

You can view the contents of that validate user method below. Note that I actually validate the JWT by sending it to a different server for authentication.

   public class UserValidationResult
   {
       public bool Validated { get; set; }
       public string Email { get; set; }
       public List<string> Roles { get; set; }
   }

   public static Task<UserValidationResult> ValidateUser(string jwt)
   {
       var taskCompletionSource = new TaskCompletionSource<UserValidationResult>();

       var authenticationRoute = "http://authserver:5000/authenticate?jwt=" +jwt;
       var authenticationClient = new RestClient(authenticationRoute);
       var authenticationRequest = new RestRequest(Method.GET);

       authenticationClient.ExecuteAsync(authenticationRequest, authenticationResponse =>
       {
           var authenticationResponseInJson = JObject.Parse(authenticationResponse.Content.ToString());
           var payload = JObject.Parse(authenticationResponseInJson["payload"].ToString());

           taskCompletionSource.SetResult(new UserValidationResult()
           {
               Validated = bool.Parse(authenticationResponseInJson["success"].ToString()),
               Email = ...
               Roles = ...

           });
       });

       return taskCompletionSource.Task;
   }

This works great. However I would prefer to use the nifty [Authorize] attribute you see in all of the OWIN/Katana tutorials. Is there a way I can make Owin Middleware that validates the jwt tokens against my Validate method just like it does now, except by use of the Authorize attribute?

EDIT

So I implemented the following middleware and use it in my startup class:

public class AuthorizationMiddleware : OwinMiddleware
{
    private OwinMiddleware _next;

    public AuthorizationMiddleware(OwinMiddleware next):base(next)
    {

    }

    public override async Task Invoke(IOwinContext context)
    {
        var jwt = context.Request.Query.Get("jwt");

        if (jwt != null)
        {
            var userValidationResult = await Utility.ValidateUser(jwt);

            if (userValidationResult.Validated)
            {
                var jwtoken = new JwtSecurityToken(jwt);

                var identity = new ClaimsIdentity("jwt");

                //foreach (var role in userValidationResult.Roles)
                //    identity.AddClaim(new Claim(ClaimTypes.Role, role));

                identity.AddClaim(new Claim(ClaimTypes.Email, userValidationResult.Email));

                context.Request.User = new ClaimsPrincipal(identity);
            }
        }

        await _next.Invoke(context);
    }
}

public class Startup
{

    public void Configuration(IAppBuilder app)
    {
        app.Use<AuthorizationMiddleware>();

        var config = new HttpConfiguration();

        app.UseWebApi(config);
    }
}

However none of the routes I have decorated with the Authorize attribute seem to hit any of my breakpoints inside the invoke method. And I always get back an unauthorized 401. Why is this?

Upvotes: 0

Views: 1047

Answers (1)

Federico Dipuma
Federico Dipuma

Reputation: 18265

You may implement a basic middleware on top of your OWIN pipeline. Something that simple may satisfy your requirements:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Use(async (ctx, next) =>
        {
            var jwt = ctx.Request.Query.Get("jwt");

            if (jwt != null)
            {
                var userValidationResult = await Utility.ValidateUser(jwt);

                if (userValidationResult.Validated)
                {
                    var identity = new ClaimsIdentity("jwt");

                    foreach(var role in userValidationResult.Roles)
                        identity.AddClaim(new Claim(ClaimTypes.Role, role));

                    identity.AddClaim(new Claim(ClaimTypes.Email, userValidationResult.Email));

                    //etc... add every claim

                    ctx.Request.User = new ClaimsPrincipal(identity);
                }
            }

            await next.Invoke();
        });

        var config = new HttpConfiguration();

        app.UseWebApi(config);

        //etc...
    }
}

This is just a stub, you may need to expand the code in order to make it work in your specific environment.

A middleware like this one will set the Request.User, which will then be used by other middlewares (Web API Authorize filter included). You can then freely decorate your controllers/actions with the [Authorize()] attribute.

If you need something more flexible, then I suggest you to read more about how to implement an authentication middleware.

This resource from Brock Allen could also help you in such an implementation: OWIN Authentication Middleware Architecture.

Upvotes: 2

Related Questions