Search code examples
asp.net-coreasp.net-core-2.0openiddict

AddJwtBearer OnAuthenticationFailed return custom error


I am using Openidict.
I am trying to return custom message with custom status code, but I am unable to do it. My configuration in startup.cs:

services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(o =>
            {
                o.Authority = this.Configuration["Authentication:OpenIddict:Authority"];
                o.Audience = "MyApp";           //Also in Auhorization.cs controller.
                o.RequireHttpsMetadata = !this.Environment.IsDevelopment();
                o.Events = new JwtBearerEvents()
                {
                    OnAuthenticationFailed = context =>
                    {
                        context.Response.StatusCode = HttpStatusCodes.AuthenticationFailed;
                        context.Response.ContentType = "application/json";
                        var err = this.Environment.IsDevelopment() ? context.Exception.ToString() : "An error occurred processing your authentication.";
                        var result = JsonConvert.SerializeObject(new {err});
                        return context.Response.WriteAsync(result);
                    }
                };
            });

But the problem is no content is returned. Chrome developer tools report

(failed)

error

for Status and

Failed to load response data

error

for response.

I also tried:

context.Response.WriteAsync(result).Wait();
return Task.CompletedTask;

but the result is the same.

Desired behaviour:
I would like to return custom status code with message what went wrong.


Solution

  • It's important to note that both the aspnet-contrib OAuth2 validation and the MSFT JWT handler automatically return a WWW-Authenticate response header containing an error code/description when a 401 response is returned:

    enter image description here

    If you think the standard behavior is not convenient enough, you can use the events model to manually handle the challenge. E.g:

    services.AddAuthentication()
        .AddJwtBearer(options =>
        {
            options.Authority = "http://localhost:54540/";
            options.Audience = "resource_server";
            options.RequireHttpsMetadata = false;
            options.Events = new JwtBearerEvents();
            options.Events.OnChallenge = context =>
            {
                // Skip the default logic.
                context.HandleResponse();
    
                var payload = new JObject
                {
                    ["error"] = context.Error,
                    ["error_description"] = context.ErrorDescription,
                    ["error_uri"] = context.ErrorUri
                };
    
                context.Response.ContentType = "application/json";
                context.Response.StatusCode = 401;
    
                return context.Response.WriteAsync(payload.ToString());
            };
        });