Reputation: 3906
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
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