I'm trying to implement authentication and access control with IdentityServer4 on an ASP.NET MVC Core app (.NetCore 2). While it's not the first time I implement a backend, it's the first time with .net, and I'm struggling with some things.
I've followed the instructions at https://identityserver4.readthedocs.io/en/release/quickstarts/1_client_credentials.html as well as the page before that.
I have also added the sample IdentityController
as they show:
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace leafserver.Controllers
{
[Route("/api/identity")]
[Authorize]
public class IdentityController : Controller
{
[HttpGet]
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
}
There are a few differences between my implementation and their example. As far as I can see:
ControllerBase
instead of Controller
as a superclassWhat I noticed is the following:
[Authorize]
, all is well. I get a 200 OK with the expected result[Authorize]
annotation is there, but I use no authentication bearer token, I am redirected to the login page (which doesn't work since this is a web api, but that's a problem for later)[Authorize]
annotation is there, and I use (what I think is) a correct authentication token, I get a 404 response.I was expecting to have a 401 response instead. Why would my routing not work because I'm using an authentication token?
Also, I'm not getting any log from the server, which doesn't help...
For me the answer was setting the Authorize attribute on my controller like this
[Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme)]
which is outlined in the docs that leastprivilege has pointed to. https://identityserver4.readthedocs.io/en/release/topics/add_apis.html
If I just had [Authorize] then it would produce 404's
Alright, I've found the problem.
In my Startup.ConfigureServices
, I modified the order in which I add the services.
// https://identityserver4.readthedocs.io/en/release/quickstarts/1_client_credentials.html
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetTestUsers()); // TODO Remove for PROD
// This MUST stay below the AddIdentityServer, otherwise [Authorize] will cause 404s
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(o =>
{
o.Authority = "http://localhost:5000";
o.RequireHttpsMetadata = false; // TODO Remove for PROD
o.ApiName = "leaf_api";
});
If you add authentication before the identity server, then you'll get the 404s. In this order, it works just fine.
Here's the full Startup.cs
file for reference:
using leafserver.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace leaf_server
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<LeafContext>(options => options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
// https://identityserver4.readthedocs.io/en/release/quickstarts/1_client_credentials.html
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetTestUsers()); // TODO Remove for PROD
// This MUST stay below the AddIdentityServer, otherwise [Authorize] will cause 404s
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(o =>
{
o.Authority = "http://localhost:5000";
o.RequireHttpsMetadata = false; // TODO Remove for PROD
o.ApiName = "leaf_api";
});
// https://dotnetcoretutorials.com/2017/01/17/api-versioning-asp-net-core/
services.AddApiVersioning(o =>
{
o.ReportApiVersions = true;
o.AssumeDefaultVersionWhenUnspecified = true;
o.DefaultApiVersion = new ApiVersion(1, 0);
o.ApiVersionReader = new HeaderApiVersionReader("x-api-version");
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseStatusCodePages();
}
app.UseIdentityServer();
app.UseAuthentication();
app.UseMvc();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With