AspNet.Core, IdentityServer 4: Несанкционированный (401) во время рукопожатия через веб-сокет с SignalR 1.0 с использованием токена-носителя JWT - PullRequest
0 голосов
/ 01 июня 2018

У меня есть два сервиса aspnet.core.Один для IdentityServer 4 и один для API, используемого клиентами Angular4 +.Концентратор SignalR работает на API.Все решение работает на докере, но это не должно иметь значения (см. Ниже).

Я использую неявный поток аутентификации, который работает безупречно.Приложение NG перенаправляет на страницу входа в IdentityServer, где пользователь входит в систему. После этого браузер перенаправляется обратно в приложение NG с токеном доступа.Затем токен используется для вызова API и создания связи с SignalR.Я думаю, что прочитал все, что доступно (см. Источники ниже).

Поскольку SignalR использует веб-сокеты, которые не поддерживают заголовки, токен должен быть отправлен в строке запроса.Затем на стороне API токен извлекается и устанавливается для запроса так же, как это было в заголовке.Затем токен проверяется, и пользователь авторизуется.

API работает без каких-либо проблем, пользователи получают авторизацию, а заявки могут быть получены на стороне API.Таким образом, с IdentityServer не должно возникнуть никаких проблем, поскольку SignalR не требует специальной настройки.Я прав?

Когда я не использую атрибут [Авторизованный] в концентраторе SignalR, рукопожатие завершается успешно .Вот почему я думаю, что нет ничего плохого в используемой инфраструктуре док-станции и обратном прокси-сервере (прокси настроен на включение веб-сокетов).

Итак, без авторизации SignalR работает.С авторизацией клиент NG получает следующий ответ во время рукопожатия:

Failed to load resource: the server responded with a status of 401
Error: Failed to complete negotiation with the server: Error
Error: Failed to start the connection: Error

Запрос

Request URL: https://publicapi.localhost/context/negotiate?signalr_token=eyJhbGciOiJSUz... (token is truncated for simplicity)
Request Method: POST
Status Code: 401 
Remote Address: 127.0.0.1:443
Referrer Policy: no-referrer-when-downgrade

Ответ, который я получаю:

access-control-allow-credentials: true
access-control-allow-origin: http://localhost:4200
content-length: 0
date: Fri, 01 Jun 2018 09:00:41 GMT
server: nginx/1.13.10
status: 401
vary: Origin
www-authenticate: Bearer

Согласнологи, токен успешно проверен.Я могу включить полные журналы, однако я подозреваю, где проблема.Поэтому я включу эту часть здесь:

[09:00:41:0561 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler AuthenticationScheme: Identity.Application was not authenticated.
[09:00:41:0564 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler AuthenticationScheme: Identity.Application was not authenticated.

Я получаю их в лог-файл, и я не уверен, что это значит.Я включил часть кода в API, где я получаю и извлекаю токен вместе с конфигурацией аутентификации.

services.AddAuthentication(options =>
    {
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultSignOutScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddIdentityServerAuthentication(options =>
        {
            options.Authority = "http://identitysrv";
            options.RequireHttpsMetadata = false;
            options.ApiName = "publicAPI";
            options.JwtBearerEvents.OnMessageReceived = context =>
            {
                if (context.Request.Query.TryGetValue("signalr_token", out StringValues token))
                {
                    context.Options.Authority = "http://identitysrv";
                    context.Options.Audience = "publicAPI";
                    context.Token = token;
                    context.Options.Validate();
                }

                return Task.CompletedTask;
            };
        });

Других ошибок, исключений в системе нет.Я могу отладить приложение, и все вроде бы нормально.

Что означают включенные строки журнала?Как я могу отладить то, что происходит во время авторизации?

РЕДАКТИРОВАТЬ: Я почти забыл упомянуть, что я думал, что проблема со схемами аутентификации, поэтому я установил каждую схему наодин, я думаю, был необходим.Однако, к сожалению, это не помогло.

Я немного невежественен, поэтому я ценю любое предложение.Спасибо.

Источники информации:

Передача токена авторизации в SignalR

Защита SignalR с помощью IdentityServer

Документы Microsoft по авторизации SignalR

Еще один вопрос GitHub

Аутентификация против SignalR

Identity.Application не был аутентифицирован

1 Ответ

0 голосов
/ 02 июня 2018

Я должен ответить на свой вопрос, потому что у меня был крайний срок, и на удивление мне удалось решить этот вопрос.Поэтому я записываю это, надеясь, что это поможет кому-то в будущем.

Сначала мне нужно было понять, что происходит, поэтому я заменил весь механизм авторизации на свой собственный.Я мог бы сделать это с помощью этого кода.Это не требуется для решения, однако если кому-то это нужно, это способ сделать.

services.Configure<AuthenticationOptions>(options =>
{
    var scheme = options.Schemes.SingleOrDefault(s => s.Name == JwtBearerDefaults.AuthenticationScheme);
    scheme.HandlerType = typeof(CustomAuthenticationHandler);
});

С помощью IdentityServerAuthenticationHandler и переопределив все возможные методы, я наконец понял, чтособытие OnMessageRectained выполняется после проверки токена.Поэтому, если бы не было никакого токена во время вызова HandleAuthenticateAsync, ответ был бы 401. Это помогло мне выяснить, куда поместить мой пользовательский код.

Мне нужно было реализовать свой собственный «протокол» во время получения токена,Поэтому я заменил этот механизм.

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;                
}).AddIdentityServerAuthentication(JwtBearerDefaults.AuthenticationScheme,
    options =>
    {
        options.Authority = "http://identitysrv";
        options.TokenRetriever = CustomTokenRetriever.FromHeaderAndQueryString;
        options.RequireHttpsMetadata = false;
        options.ApiName = "publicAPI";
    });

Важная часть - это свойство TokenRetriever и то, что его заменяет.

public class CustomTokenRetriever
{
    internal const string TokenItemsKey = "idsrv4:tokenvalidation:token";
    // custom token key change it to the one you use for sending the access_token to the server
    // during websocket handshake
    internal const string SignalRTokenKey = "signalr_token";

    static Func<HttpRequest, string> AuthHeaderTokenRetriever { get; set; }
    static Func<HttpRequest, string> QueryStringTokenRetriever { get; set; }

    static CustomTokenRetriever()
    {
        AuthHeaderTokenRetriever = TokenRetrieval.FromAuthorizationHeader();
        QueryStringTokenRetriever = TokenRetrieval.FromQueryString();
    }

    public static string FromHeaderAndQueryString(HttpRequest request)
    {
        var token = AuthHeaderTokenRetriever(request);

        if (string.IsNullOrEmpty(token))
        {
            token = QueryStringTokenRetriever(request);
        }

        if (string.IsNullOrEmpty(token))
        {
            token = request.HttpContext.Items[TokenItemsKey] as string;
        }

        if (string.IsNullOrEmpty(token) && request.Query.TryGetValue(SignalRTokenKey, out StringValues extract))
        {
            token = extract.ToString();
        }

        return token;
    }

И это мой собственный алгоритм получения токена, который использует стандартзаголовок и строка запроса в первую очередь для поддержки распространенных ситуаций, таких как вызовы веб-API.Но если токен все еще пуст, он пытается получить его из строки запроса, куда клиент поместил его во время рукопожатия websocket.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...