密码更改后如何使OAuth令牌无效?

时间:2014-11-06 09:52:45

标签: asp.net-web-api oauth asp.net-identity

我们在带有SimpleAuthorizationServerProvider的Web Api项目中使用ASP.NET Identity,我们使用OAuth-tokens来授权来自客户端的每个请求。 (令牌有时间和到期时间,我们不使用刷新令牌。)

当用户更改密码时,我想使他们可能拥有的令牌无效,可能在其他设备上。有没有办法明确这样做?我进行了实验,发现密码更改后现有令牌的运行没有任何问题,应该加以防范。

我考虑过将密码哈希或OAuth令牌中的部分哈希作为声明,并在派生的AuthorizeAttribute过滤器的OnAuthorization方法中进行验证。
这是解决问题的正确方法吗?

2 个答案:

答案 0 :(得分:12)

我不建议将密码的哈希值作为声明,我相信在密码更改时没有直接的方法来使令牌无效。

但是,如果您通过从客户端应用程序发送到受保护的API端点的每个请求命中数据库,那么您需要为授予资源所有者请求的每个令牌存储令牌标识符(可能是Guid)。然后,您将令牌标识符指定为此令牌的自定义声明,之后您需要通过查找令牌标识符和资源所有者的用户名来检查每个请求的此表。

更改密码后,您删除此资源所有者(用户)的此令牌标识符记录,下次从客户端发送令牌时,它将被拒绝,因为此令牌标识符和资源所有者的记录已被删除。 / p>

答案 1 :(得分:12)

我根据Taiseer的建议采用了我的方法。解决方案的要点如下。每次用户更改其密码(以及注册时)时,都会生成一个新的GUID并将其保存在User表的数据库中。我将此GUID称为密码标记,并将其存储在名为LatestPasswordStamp的属性中。

此戳记必须作为声明的一部分发送给客户端作为声明的一部分。这可以通过GrantResourceOwnerCredentials实现的OAuthAuthorizationServerProvider方法中的以下代码来实现。

identity.AddClaim( new Claim( "PasswordTokenClaim", user.LatestPasswordStamp.ToString() ) );

此戳记将在每个请求中从客户端发送到服务器,并验证数据库中的戳记未被更改。如果是,则表示用户可能从其他设备更改了密码。验证是在我们的自定义授权过滤器中完成的。

public class AuthorizeAndCheckStampAttribute : AuthorizeAttribute
{
    public override void OnAuthorization( HttpActionContext actionContext )
    {
        var claimsIdentity = actionContext.RequestContext.Principal.Identity as ClaimsIdentity;
        if( claimsIdentity == null )
        {
            this.HandleUnauthorizedRequest( actionContext );
        }

        // Check if the password has been changed. If it was, this token should be not accepted any more.
        // We generate a GUID stamp upon registration and every password change, and put it in every token issued.
        var passwordTokenClaim = claimsIdentity.Claims.FirstOrDefault( c => c.Type == "PasswordTokenClaim" );

        if( passwordTokenClaim == null )
        {
            // There was no stamp in the token.
            this.HandleUnauthorizedRequest( actionContext );
        }
        else
        {
            MyContext ctx = (MyContext)System.Web.Mvc.DependencyResolver.Current.GetService( typeof( MyContext ) );

            var userName = claimsIdentity.Claims.First( c => c.Type == ClaimTypes.Name ).Value;

            if( ctx.Users.First( u => u.UserName == userName ).LatestPasswordStamp.ToString() != passwordTokenClaim.Value )
            {
                // The stamp has been changed in the DB.
                this.HandleUnauthorizedRequest( actionContext );
            }
        }

        base.OnAuthorization( actionContext );
    }
}

这样,如果客户端尝试使用在密码更改之前发出的令牌授权自己,则会收到授权错误。