Подтвердить что ты не робот

Работа с длинными токенами-носителями из webapi путем предоставления суррогатного токена

Я создаю веб-api, используя ASP.NET WebApi 2, используя проверку подлинности, и мои пользователи могут иметь очень большое количество претензий. С большим количеством требований токен-носитель быстро растет очень быстро, поэтому я пытаюсь найти способ возврата гораздо более короткого токена-носителя.

До сих пор я обнаружил, что могу предоставить IAuthenticationTokenProvider свойство OAuth OAuthAuthorizationServerOptions.AccessTokenProvider:

OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AccessTokenExpireTimeSpan = TimeSpan.FromHours(12),
    AccessTokenProvider = new GuidProvider() // <-- here
};

И это дает мне шанс перехватить AuthenticationTicket и спрятать его, заменив его чем-то более простым - в моем примере ниже хэшированного guid. (Примечание: на данный момент этот класс просто содержит ConcurrentDictionary<string,AuthenticationTicket> с моими сеансами - в реальном мире я намерен хранить сеансы в некотором постоянном хранилище)

public class GuidProvider : IAuthenticationTokenProvider
{
    private static ConcurrentDictionary<string, AuthenticationTicket> tokens 
        = new ConcurrentDictionary<string, AuthenticationTicket>();

    public void Create(AuthenticationTokenCreateContext context)
    {
        throw new NotImplementedException();
    }

    public async System.Threading.Tasks.Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        var guid = Guid.NewGuid().ToString();

        var ticket = Crypto.Hash(guid);

        tokens.TryAdd(ticket, context.Ticket);

        context.SetToken(ticket);
    }

    public void Receive(AuthenticationTokenReceiveContext context)
    {
        throw new NotImplementedException();
    }

    public async System.Threading.Tasks.Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {
        AuthenticationTicket ticket;

        if (tokens.TryGetValue(context.Token, out ticket))
        {
            if (ticket.Properties.ExpiresUtc.Value < DateTime.UtcNow)
            {
                tokens.TryRemove(context.Token, out ticket);
            }
            context.SetTicket(ticket);
        }
    }
}

Итак, мои вопросы:

  • Является ли это подходящим (и безопасным!) способом предоставления суррогатного ключа вместо моего долгожданного токена?
  • Может быть, лучше/проще, где я должен делать это в стеке webapi/OAuth?

Еще одна вещь, которую следует отметить, это то, что я намерен поддерживать токены обновления, и на самом деле приведенный выше пример был вырван из примеров, которые используют этот механизм для токена Refresh - кроме как с токеном обновления, они кажутся одноразовыми, поэтому метод ReceiveAsync обычно всегда удаляет токен обновления, указанный в ConcurrentDictionary, я не совсем уверен, что понимаю, почему?

4b9b3361

Ответ 1

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

Я предлагаю вам использовать JNON Web Token JWT вместо формата токенов доступа по умолчанию, для этого вам нужно реализовать свой формат токена доступа CustomOAuthProvider в свойстве Provider в OAuthAuthorizationServerOptions в качестве кода ниже:

 OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            //For Dev enviroment only (on production should be AllowInsecureHttp = false)
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/oauth2/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
            Provider = new CustomOAuthProvider(),
            AccessTokenFormat = new CustomJwtFormat("http://jwtauthzsrv.azurewebsites.net")
        };

Я заметил, что добавление большего количества претензий к токену JWT не будет сильно увеличивать его размер в случае формата токена доступа по умолчанию.

Ниже образца из 2 JWT с разными претензиями внутри каждого из них, второй - больше, чем первый, всего на 50 символов. Я рекомендую вам проверить кодированное содержимое каждого из них, используя jwt.io Первый JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6InRhaXNlZXIiLCJzdWIiOiJ0YWlzZWVyIiwicm9sZSI6WyJNYW5hZ2VyIiwiU3VwZXJ2aXNvciJdLCJpc3MiOiJodHRwOi8vand0YXV0aHpzcnYuYXp1cmV3ZWJzaXRlcy5uZXQiLCJhdWQiOiIwOTkxNTNjMjYyNTE0OWJjOGVjYjNlODVlMDNmMDAyMiIsImV4cCI6MTQxODY0NzMyNywibmJmIjoxNDE4NjQ1NTI3fQ.vH9XPtjtAv2-6SwlyX4fKNJfm5ZTVHd_9a3bRgkA_LI

Второй JWT (больше претензий):

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6InRhaXNlZXIiLCJzdWIiOiJ0YWlzZWVyIiwicm9sZSI6WyJNYW5hZ2VyIiwiU3VwZXJ2aXNvciIsIlN1cGVydmlzb3IxIiwiU3VwZXJ2aXNvcjIiLCJTdXBlcnZpc29yMyJdLCJpc3MiOiJodHRwOi8vand0YXV0aHpzcnYuYXp1cmV3ZWJzaXRlcy5uZXQiLCJhdWQiOiIwOTkxNTNjMjYyNTE0OWJjOGVjYjNlODVlMDNmMDAyMiIsImV4cCI6MTQxODY0NzQ1NiwibmJmIjoxNDE4NjQ1NjU2fQ.TFEGDtz1RN8VmCQu7JH4Iug0B8UlWDLVrIlvc-7IK3E

Формат JWT становится стандартным способом выдачи токенов на предъявителя OAuth 2.0, а также будет работать с обновлением токена обновления. Но имейте в виду, что JWT - это только подписанные токены, а не зашифрованные как случай в формате токена доступа по умолчанию, поэтому не храните конфиденциальные данные.

Я написал подробное сообщение в блоге на bitoftech.net о том, как использовать токены JWT в веб-интерфейсе ASP.NET вместе с живой демонстрацией API и исходный код на GIthub, не стесняйтесь проверить его и сообщить мне, если вам нужна дополнительная помощь.

Удачи!