Search code examples
asp.netasp.net-coreasp.net-authorization

Logged in user gets 401 Unauthorized error when calling Web API with [Authorize] attribute


I'm trying to build an ASP.NET Core app (.NET 5.0) with Angular 12 integrated in. Both of these are new technologies for me. I'm coming from a .NETFramework/AngularJS environment.

I thought this question might have solved my problem, but it does not.

I built the project with Individual Account authentication type. I created a Users web api controller and simply applied the [Authorize] attribute to the GetUsers() api. It works fine without the attribute, but with it, I get a 401 Unauthorized error while logged in. No roles or policies have been setup.

I would expect the user to be authenticated and be able to access this api through the browser while in an active session. I've tried Postman as well.

Here is the web api code that illustrates the [Authorize] attribute on the GetUsers api:

namespace BrochureManagement.api
{
    [Route("api/private/users")]
    [ApiController]
    public class UserController : ControllerBase
    {
        // GET: api/<UserController>
        [HttpGet]
        [Authorize]
        public object GetUsers()
        {
            var userService = new UserService();

            return userService.GetUsers();
        }

        // GET api/<UserController>/5
        [HttpGet("{id}")]
        public string Get(int id)
        {
            return "value";
        }

        // POST api/<UserController>
        [HttpPost]
        public void Post([FromBody] string value)
        {
        }

        // PUT api/<UserController>/5
        [HttpPut("{id}")]
        public void Put(int id, [FromBody] string value)
        {
        }

        // DELETE api/<UserController>/5
        [HttpDelete("{id}")]
        public void Delete(int id)
        {
        }
    }
}

Here is the ConfigureServices and Configure methods in the Startup.cs file:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddDatabaseDeveloperPageExceptionFilter();

    services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddRoles<Role>()
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

    services.AddAuthentication()
        .AddIdentityServerJwt();
    services.AddControllersWithViews();
    services.AddRazorPages()
        .AddRazorRuntimeCompilation();
    // In production, the Angular files will be served from this directory
    services.AddSpaStaticFiles(configuration =>
    {
        configuration.RootPath = "ClientApp/dist";
    });
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    if (!env.IsDevelopment())
    {
        app.UseSpaStaticFiles();
    }

    app.UseAuthentication();
    app.UseRouting();
    app.UseIdentityServer();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller}/{action=Index}/{id?}");
        endpoints.MapRazorPages();
    });

    app.UseSpa(spa =>
    {
        // To learn more about options for serving an Angular SPA from ASP.NET Core,
        // see https://go.microsoft.com/fwlink/?linkid=864501

        spa.Options.SourcePath = "ClientApp";

        if (env.IsDevelopment())
        {
            //spa.UseAngularCliServer(npmScript: "start");
            spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");
        }
    });
}

Also, the app is configured to use HTTPS. Not sure why this doesn't just work, but hoping someone can help shed some light.


Solution

  • When you use .AddApiAuthorization() in IdentityServer, you have to pass a bearer token to the API when calling it or else you will get a 401 error. This means that you will not be able to call it directly through the browser because it does not automatically add the bearer token.

    Check your IdentityServer logs to see why it is denying the request. Also, check your Angular app to make sure that it is adding the bearer token to the request headers.