首页 > 解决方案 > 关于存储令牌值的 OAuth 2.0 基于令牌的身份验证问题

问题描述

我已经在我们的 WebAPI 应用程序中实现了基于 OAuth 令牌的身份验证,并针对数据库验证了用户名和密码。但我们不会将访问令牌和刷新令牌同步到任何类型的数据库。这是代码,但是,我有一个问题,令牌值的存储位置。

下面的代码生成令牌

        /// <summary>  
        /// Grant resource owner credentials overload method.  
        /// </summary>  
        /// <param name="context">Context parameter</param>  
        /// <returns>Returns when task is completed</returns>  
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            // Initialization.  
            var usernameVal = context.UserName;
            var passwordVal = context.Password;
            var user = _securityLogic.AuthenticateApiUser(usernameVal, passwordVal);

            // Verification.  
            if (!user)
            {
                // Settings.  
                context.SetError("invalid_grant", "The user name or password is incorrect.");

                // Return info.  
                return;
            }

            // Initialization.  
            var claims = new List<Claim>
            {
                //var userInfo = user.FirstOrDefault();

                // Setting  
                new Claim(ClaimTypes.Name, usernameVal)
            };

            // Setting Claim Identities for OAUTH 2 protocol.  
            ClaimsIdentity oAuthClaimIdentity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
            ClaimsIdentity cookiesClaimIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationType);

            // Setting user authentication.  
            AuthenticationProperties properties = CreateProperties(usernameVal);
            AuthenticationTicket ticket = new AuthenticationTicket(oAuthClaimIdentity, properties);

            // Grant access to authorize user.  
            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(cookiesClaimIdentity);
        }
        #endregion

        #region Token endpoint override method.  
        /// <summary>  
        /// Token endpoint override method  
        /// </summary>  
        /// <param name="context">Context parameter</param>  
        /// <returns>Returns when task is completed</returns>  
        public override Task TokenEndpoint(OAuthTokenEndpointContext context)
        {
            foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
            {
                // Adding.  
                context.AdditionalResponseParameters.Add(property.Key, property.Value);
            }

            // Return info.  
            return Task.FromResult<object>(null);
        }
        #endregion

这是用于生成刷新令牌的代码

        #region GrantRefreshToken

        private static readonly ConcurrentDictionary<string, AuthenticationTicket> RefreshTokens =
        new ConcurrentDictionary<string, AuthenticationTicket>();

        /// <summary>
        /// Grants Refresh Token 
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
        {
            // Change authentication ticket for refresh token requests  
            var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
            // newIdentity.AddClaim(new Claim("newClaim", "newValue"));

            var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
            context.Validated(newTicket);

            return Task.FromResult<object>(null);
        }

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

            // Copy claims from the previous token
            var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
            {
                IssuedUtc = context.Ticket.Properties.IssuedUtc,
                ExpiresUtc = DateTime.UtcNow.AddMinutes(30)
            };

            var refreshTokenTicket = await Task.Run(() =>
                new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties));

            RefreshTokens.TryAdd(guid, refreshTokenTicket);

            // Consider storing only the hash of the handle  
            context.SetToken(guid);
        }
        #endregion

所以,我的问题是 .NET/Owin/IdentityServer3 将它们写入一些内存数据库?如果是这样,是否可以访问它们以进行查看和删除等操作?如果重新启动应用服务器会发生什么,是否所有令牌都被清除了?还是他们坚持不懈?

您是否建议将其存储在数据库中并从数据库中检索?任何帮助表示赞赏,顺便说一句,这段代码工作得很好。

标签: asp.net-web-apioauth-2.0tokenowin

解决方案


文档中:

如果没有特别配置,我们将始终为授权代码、同意、参考和刷新令牌提供内存版本存储。

请注意,他们谈论的是参考令牌刷新令牌。不存储 JWT 访问令牌和身份令牌。

为了在 IdentityServer3 (以及IdentityServer4 )中使用刷新令牌,它必须匹配存储的令牌。

这样做的主要好处是您可以控制令牌。您可以撤销它(从商店中删除它),并定义如何使用它:一次性或重复使用。

我对IdentityServer3不熟悉,但是你可以看一下github并搜索实现RevocationEndpoint的代码,即从商店中删除刷新令牌的地方。这可能会为如何访问和使用商店提供线索。

使用内存存储,令牌在 IdentityServer 重新启动时丢失。因此,将它们持久存储在数据库等持久存储中,对于生产服务器来说是一件好事。对于 IdentityServer4,您可以实现一个操作存储

请注意,无论重新启动服务器,JWT 令牌都保持有效,除非私钥也没有持久化。在这种情况下,IdentityServer 无法验证令牌并且别无选择,只能将 JWT 令牌视为无效。

因此,对于生产环境,您应该保留密钥和数据,并且使用数据库很好。正如您在 IdentityServer4 中看到的那样,对此有支持。

说到 IdentityServer4,既然对 IdentityServer3 的(免费)支持已经结束,我建议您切换到 IdentityServer4,如果您可以这样做的话。由于两个版本都实现了 oidc/auth2,您应该能够继续使用升级后的 IdentityServer 客户端。在 stackoverflow 上有一些问题可以帮助你。并查看 IdentityServer4 文档,它提供了非常丰富的信息。


推荐阅读