【问题标题】:OAuth 2.0 token based Authentication Question on storing token values关于存储令牌值的 OAuth 2.0 基于令牌的身份验证问题
【发布时间】:2020-01-22 08:41:39
【问题描述】:

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

生成Token的代码如下

        /// <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-api oauth-2.0 token owin


    【解决方案1】:

    来自documentation

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

    请注意,他们谈论的是 reference tokensrefresh tokens。不存储 JWT 访问令牌和身份令牌。

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

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

    我对IdentityServer3不熟悉,不过你可以看一下github,搜索一下实现RevocationEndpoint的代码,就是从store中取出刷新令牌的地方。这可能会提供有关如何访问和使用商店的线索。

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

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

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

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

    【讨论】:

    • 感谢您的建议。我正在为 Identity Server 使用以下版本。我认为我没有使用 IdentityServer 3/4。我擅长使用数据库存储令牌值,但是如果我在项目中使用 JSON 文件并将令牌存储在该文件中,我还有另一个问题,它们是持久的吗?你认为哪种方式可行?
    • 我以为您在谈论 IdentityServer3,因为您的问题中也提到了这一点。看来您的意思是 Asp.Net Identity 3。无论哪种方式,它都以某种方式连接,因为它都是关于实现相同的规范。您仍然可以查看 IdentityServer4 文档,了解它是如何实现的。
    • 无论你如何存储它,只要你存储它就是持久的。数据库可能是存储信息的更好解决方案,因为您可能希望定期更新信息。 Json 文件更适用于固定值,例如数据库连接。
    猜你喜欢
    • 1970-01-01
    • 2015-10-07
    • 2016-01-23
    • 2016-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-28
    相关资源
    最近更新 更多