'ASP Core API - Custom Unauthorized body

I'm developing ASP Core Web API using dotnet core v3.1.

I'm using JWT tokens for authentication. And for authorization I use the [Authorize] attribute.

How can I create my own response if the user is not logged in (while trying to access the action marked with the [Authorize] attribute) or the user's token is not authenticated.

I came across a solution using a custom authorization attribute inherited from the default one. And in this example, the HandleUnauthorizedRequest method is overridden. But I don't see such a method inside the AuthorizeAttribute class.

Is there a way to create custom unauthorized responses with http body?



Solution 1:[1]

For .net core 5 web api project with jwt authentication use this middleware in Configure method of Startup.cs for show ErrorDto in Swagger:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "LoginService v1"));
    }

    app.ConfigureExceptionHandler();
    app.UseHttpsRedirection();
    app.UseRouting();
        
    // Unauthorized (401) MiddleWare
    app.Use(async (context, next) =>
    {
        await next();
     
        if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized) // 401
        {
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsync(new ErrorDto()
            {
                StatusCode = 401,
                Message = "Token is not valid"
            }.ToString());
        }
    });

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

ErrorDto :

public class ErrorDto
{
    public int StatusCode { get; set; }
    public string Message { get; set; }
    
    public override string ToString()
    {
        return JsonSerializer.Serialize(this);
    }
}

Solution 2:[2]

This is what I came up with for responding with the same ProblemDetails you would get from returning Unauthorized() in an ApiController:

.AddJwtBearer(options =>
{
    // Other configs...
    options.Events = new JwtBearerEvents
    {
        OnChallenge = async context =>
        {
            // Call this to skip the default logic and avoid using the default response
            context.HandleResponse();

            var httpContext = context.HttpContext;
            var statusCode = StatusCodes.Status401Unauthorized;

            var routeData = httpContext.GetRouteData();
            var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());

            var factory = httpContext.RequestServices.GetRequiredService<ProblemDetailsFactory>();
            var problemDetails = factory.CreateProblemDetails(httpContext, statusCode);

            var result = new ObjectResult(problemDetails) { StatusCode = statusCode };
            await result.ExecuteResultAsync(actionContext);
        }
    };
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 satma0745
Solution 2 dahlbyk