AddJwtBearer OnAuthenticationFailed возвращает настраиваемую ошибку

Я использую Openidict.
Я пытаюсь вернуть настраиваемое сообщение с настраиваемым кодом состояния, но Я не могу этого сделать. Моя конфигурация в 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);
                    }
                };
            });

Но проблема в том, что контент не возвращается. Отчет об инструментах разработчика Chrome

(не смогли)

error

для статуса и

Не удалось загрузить данные ответа

error

для ответа.

Я также пробовал:

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

но результат тот же.

Желаемое поведение:
Я хотел бы вернуть пользовательский код статуса с сообщением о том, что пошло не так.


person Makla    schedule 06.02.2018    source источник
comment
Не размещайте изображения кода или ошибок! Изображения и скриншоты могут быть хорошим дополнением в сообщение, но убедитесь, что сообщение по-прежнему понятно и полезно без них. Если вы публикуете изображения кода или сообщения об ошибках, убедитесь, что вы также скопировали и вставили или набрали фактический код / ​​сообщение непосредственно в сообщение.   -  person Rob    schedule 06.02.2018
comment
Не загружается vendor.js. Вы используете app.UseStaticFiles() в своем Startup.cs файле? В таком случае его необходимо зарегистрировать до app.UseAuthentication().   -  person Brad    schedule 07.02.2018
comment
OnAuthenticationFailed для JwtBearer больше не может возвращать контент в 2.0. он не имеет необходимого контроля над потоком запросов.   -  person Tratcher    schedule 07.02.2018
comment
@Rob Понятно, я не был уверен, хватит ли текста. Для меня действительно странно получить ошибку для кода состояния, хотя я обнаружил, что фактический код возвращается, но после запроса процесса Angular этот код изменяется на сбой.   -  person Makla    schedule 07.02.2018
comment
@Brad Я использую UseStaticFiles, и он зарегистрирован до UseAuthentication. Загружается vendor, но ошибка возникает из vendor.   -  person Makla    schedule 07.02.2018
comment
Я не уверен, почему, но проверка истечения срока действия токена не удалась, но я могу использовать api   -  person Silly Volley    schedule 20.03.2020
comment
Спасибо за этот вопрос. Ответы помогли мне с решением.   -  person Jeremy Ray Brown    schedule 12.01.2021


Ответы (4)


Важно отметить, что как проверка aspnet-contrib OAuth2, так и обработчик MSFT JWT автоматически возвращают заголовок ответа WWW-Authenticate, содержащий код / ​​описание ошибки, когда возвращается ответ 401:

введите описание изображения здесь

Если вы считаете, что стандартное поведение недостаточно удобно, вы можете использовать модель событий, чтобы вручную справиться с проблемой. Например:

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());
        };
    });
person Kévin Chalet    schedule 11.02.2018
comment
Как узнать, произошла ли ошибка? HandleResponse возвращает что-нибудь полезное? - person Makla; 12.02.2018
comment
HandleResponse ничего не возвращает (согласен, название плохое). Он просто сообщает обработчику JWT, что вы хотите позаботиться об ответе и не хотите, чтобы применялась логика по умолчанию (возвращающая заголовок WWW-Authenticate). - person Kévin Chalet; 12.02.2018
comment
Без HandleResponse () я получаю журналы ошибок. System.InvalidOperationException: StatusCode не может быть установлен, потому что ответ уже начался. Добавив его, я больше не вижу эту ошибку. Эта фраза мне очень помогла. Спасибо. - person Kes; 19.02.2019
comment
@ KévinChalet спасибо за ваш ответ, есть ли способ вернуть json, а не текст? - person yavg; 24.08.2020
comment
@yavg просто убедитесь, что вы вернули заголовок Content-Type с application/json. Я обновил свой ответ, чтобы отразить это. - person Kévin Chalet; 24.08.2020
comment
Извините, что снова беспокою вас, вы знаете, как я могу изменить код? это возвращает 200 - person yavg; 24.08.2020
comment
Это сработало! Спасибо. - person Jeremy Ray Brown; 12.01.2021

Столкнулся с той же проблемой, попробовал решение, предоставленное Pinpoint, но оно не сработало для меня на ядре ASP.NET 2.0. Но на основе решения Pinpoint и некоторых проб и ошибок следующий код мне подходит.

var builder = services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(o =>
        {
            o.Authority = "http://192.168.0.110/auth/realms/demo";
            o.Audience = "demo-app";
            o.RequireHttpsMetadata = false;

            o.Events = new JwtBearerEvents()
            {
                OnAuthenticationFailed = c =>
                {
                    c.NoResult();
                    c.Response.StatusCode = 500;
                    c.Response.ContentType = "text/plain";
                    c.Response.WriteAsync(c.Exception.ToString()).Wait();
                    return Task.CompletedTask;
                },
                OnChallenge = c =>
                {
                    c.HandleResponse();
                    return Task.CompletedTask;
                }
            };
        });
person Ryan Teh    schedule 21.05.2018

Это то, что сработало у меня после обнаружения проблем, связанных с этим исключением, которые, казалось, появились после обновления пакетов.

System.InvalidOperationException: StatusCode cannot be set because the response has already started.
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ThrowResponseAlreadyStartedException(String value)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.set_StatusCode(Int32 value)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.set_StatusCode(Int32 value)
   at Microsoft.AspNetCore.Http.DefaultHttpResponse.set_StatusCode(Int32 value)

Реализация ниже,

                OnAuthenticationFailed = context =>
                {
                    context.NoResult();
                    context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    context.Response.ContentType = "application/json";

                    string response =
                        JsonConvert.SerializeObject("The access token provided is not valid.");
                    if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                    {
                        context.Response.Headers.Add("Token-Expired", "true");
                        response =
                            JsonConvert.SerializeObject("The access token provided has expired.");
                    }

                    context.Response.WriteAsync(response);
                    return Task.CompletedTask;
                },
                OnChallenge = context =>
                {
                    context.HandleResponse();
                    return Task.CompletedTask;
                }
person Tyler V    schedule 19.10.2020

Пожалуйста, ознакомьтесь с приведенным ниже кодом для .net core 2.1.

OnAuthenticationFailed =context =>
                {
                    context.Response.OnStarting(async () =>
                    {
                        context.NoResult();
                        context.Response.Headers.Add("Token-Expired", "true");
                        context.Response.ContentType = "text/plain";
                        context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                        await context.Response.WriteAsync("Un-Authorized");
                    });

                    return Task.CompletedTask;                        
                },
person Debashis Chowdhury    schedule 16.03.2020