I have a .Net 5 Web API that is protected by Azure AD. What I want to accomplish is to provide custom error message when authorization fails. For example,
- If a user forgets to add
Authorization
header in the request, I want to tell the user that this header is required. - If the token has expired, I want to tell the user exactly that rather than simply returning
401
status code with no details.
Simply putting [Authorize]
attribute before controller and/or action does not work as it returns 401
status code and does not include any details about this error.
I searched for it and found that I have to write a custom authorization filter and using that it should be possible to accomplish what I am looking for.
Using this blog post
, I was able to write a custom authorization filter however I am still not able to find out how to validate the token and return appropriate error messages.
Here's the code I wrote so far:
Startup.cs
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)
{
AuthorizationPolicy policy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build();
_ = services.AddAuthorization(options =>
{
options.DefaultPolicy = policy;
});
_ = services.AddControllers(options =>
{
options.Filters.Add(new CustomTokenAuthorizationFilter(policy));
});
_ = services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" });
});
_ = services
.AddMicrosoftIdentityWebApiAuthentication(Configuration, "ApiSettings");
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
_ = app.UseDeveloperExceptionPage();
_ = app.UseSwagger();
_ = app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1"));
}
_ = app.UseConfigureRequestHeaders();
_ = app.UseHttpsRedirection();
_ = app.UseRouting();
//_ = app.UseAuthentication();
_ = app.UseAuthorization();
_ = app.UseEndpoints(endpoints =>
{
_ = endpoints.MapControllers();
});
}
}
CustomAuthorizationFilter.cs
public class CustomTokenAuthorizationFilter : AuthorizeFilter
{
public CustomTokenAuthorizationFilter(AuthorizationPolicy policy) : base(policy)
{
}
public override async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// Allow Anonymous skips all authorization
if (context.Filters.Any(item => item is IAllowAnonymousFilter))
{
return;
}
var policyEvaluator = context.HttpContext.RequestServices.GetRequiredService<IPolicyEvaluator>();
var authenticateResult = await policyEvaluator.AuthenticateAsync(Policy, context.HttpContext);
var authorizeResult = await policyEvaluator.AuthorizeAsync(Policy, authenticateResult, context.HttpContext, context);
return;
}
}
With this code, when there's an issue with my token authenticateResult.Succeeded
property is false
and authorizeResult.Challenged
property is true
.
How can I find out what caused the authorization to fail? Any help would be highly appreciated.
I am using Microsoft.Identity.Web
NuGet Package as suggested by Microsoft.