Thanks for visiting my blog!
In my Pluralsight courses1 on ASP.NET Core, I show how to use JWT Tokens to secure your API. In building a new example for my upcoming Vue.js course, I decided to only use JWT (not cookies and JWT like many of my examples are).
But I kept getting redirects on failure to call an API made me realize that I wasn’t sure how to make JWT the only provider. After some fiddling I figured it out. This blog post is mostly to remind me of how to do it.
UPDATED!
After help from @khellang - I found the real culprit. See new section at the bottom.
Prior Blogpost
If you haven’t seem how to handle Cookies & JwtBearer tokens, see my other post:
https://wildermuth.com/2017/08/19/Two-AuthorizationSchemes-in-ASP-NET-Core-2
Using JwtBearer
I knew I had to add the JwtBearer when I setup the AddAuthentication call:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<IdentityUser, IdentityRole>(cfg =>
{
cfg.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<StoreContext>();
services.AddAuthentication()
.AddJwtBearer(cfg =>
{
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = _config["Security:Tokens:Issuer"],
ValidateAudience = true,
ValidAudience = _config["Security:Tokens:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Security:Tokens:Key"])),
};
});
services.AddDbContext<StoreContext>();
services.AddScoped<IStoreRepository, ProductRepository>();
services.AddScoped<StoreDbInitializer>();
services.AddMvc();
}
But just adding a single provider didn’t work making it the default. Originally, I decided to use the same method as the dual-authentication by adding the
[Route("api/[controller]")]
[Authorize(JwtBearerDefaults.AuthenticationScheme)]
public class OrdersController : Controller
But my goal was not to have to specify it, I want it to be the default. The trick seemed to be that I needed to tell Authentication that the default AuthenticateScheme and the DefaultChallengeScheme needed to be using the JwtBearer:
services.AddAuthentication(cfg =>
{
cfg.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
cfg.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = _config["Security:Tokens:Issuer"],
ValidateAudience = true,
ValidAudience = _config["Security:Tokens:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Security:Tokens:Key"])),
};
});
Then I could just use Authorize attribute and it would work:
[Route("api/[controller]")]
[Authorize]
public class OrdersController : Controller
UPDATE
While this worked, it wasn’t quite right. One approach I didn’t mention was just setting the default authentication when configuring it:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
...
This also works except the redirection from cookie authentication is still there. The problem was that I was using AddIdentity before. When pointed at the source code, it was clear that this code was adding a lot of cookie based identity as the defaults:
So I could just move the AddIdentity call to after the AddAuthentication but I hate it when order matters. So I was told that what I should have done was AddIdentityCore instead:
services.AddIdentityCore<identityuser> services.AddIdentityCore<IdentityUser>(cfg =>
{
cfg.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<StoreContext>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(cfg =>
{
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = _config["Security:Tokens:Issuer"],
ValidateAudience = true,
ValidAudience = _config["Security:Tokens:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Security:Tokens:Key"])),
};
});
I like that it works and it’s more correct. Thanks Kristian!
1 http://shawnw.me/learnaspnetcore2 & http:// http://shawnw.me/corewebapi