If I grasped your problem statement, I think OWIN might be an option: you decouple your application from underlying hosting and get an extensible pipeline that you can inject middleware into (pretty much like .net core works out of the box).
Even better - it comes with JWT support out of the box (well, you need to install a few nuget packages - see below). Then you simply enable it on your IAppBuilder
and roll with standard [Authorize]
attributes.
To demo this setup, I've put together a working GitHub repo here to illustrate WebApi middleware.
Apart from Microsoft.AspNet.WebApi.Owin
, Microsoft.Owin.Host.SystemWeb
and Microsoft.Owin.Security.Jwt
nuget packages, it's pretty much a stock standard asp.net WebApi project with the following files changed:
/Startup.cs
using System.Text;
using System.Web.Http;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Jwt;
using Owin;
namespace OWIN.WebApi
{
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config); // bootstrap your existing WebApi config
appBuilder.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true, // I guess you don't even have to sign the token
ValidIssuer = "http://localhost",
ValidAudience = "http://localhost",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("jwt_signing_secret_key"))
}
});
appBuilder.UseWebApi(config); // instruct OWIN to take over
}
}
}
/Controllers/ProtectedValuesController.cs
using System.Collections.Generic;
using System.Web.Http;
namespace OWIN.WebApi.Controllers
{
[Authorize]
public class ProtectedValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
}
/Controllers/ObtainJwtController.cs
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using System.Web.Http;
using Microsoft.IdentityModel.Tokens;
using Claim = System.Security.Claims.Claim;
namespace OWIN.WebApi.Controllers
{
// this class is literally just a test harness to help me generate a valid token for popping into Postman.
public class ObtainJwtController: ApiController
{
private string CraftJwt()
{
string key = "jwt_signing_secret_key"; //Secret key which will be used later during validation
var issuer = "http://localhost";
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var permClaims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim("valid", "1"),
new Claim("userid", "1"),
new Claim("name", "test")
};
var token = new JwtSecurityToken(issuer,
issuer,
permClaims,
expires: DateTime.Now.AddDays(1),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public string Get()
{
return $"Bearer {CraftJwt()}";
}
}
}
This appears to work for MVC too
I have added a few extra nuget packages to do with ASP.NET Identity, which seems to have enabled me to successfully protect the following controller:
/Controllers/Home.cs
using System.Web.Mvc;
namespace OWIN.WebApi.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Title = "Home Page";
return View();
}
[Authorize]
public ActionResult Protected()
{
return View();
}
}
}
Hopefully that gives you some options to explore