ASP.NET Identity验证ResetPassword令牌是否已过期 [英] ASP.NET Identity verify if ResetPassword token has expired

查看:140
本文介绍了ASP.NET Identity验证ResetPassword令牌是否已过期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的API中,我有2个端点,第一个端点生成用于重置密码形式的电子邮件(我使用UserManager.GeneratePasswordResetTokenAsync生成令牌).
第二个端点用于实际的密码重置(我使用UserManager.ResetPasswordAsync).

In my API I have 2 endpoints, first that generates email to reset password form (I generate token using UserManager.GeneratePasswordResetTokenAsync).
Second endpoint is for actual password reset (I use UserManager.ResetPasswordAsync).

我的要求是验证密码重置所需的令牌是否未过期.

My requirement was to verify if token that is required for password reset isn't expired.

在GitHub上搜索时,我发现此问题以及从我发现的结果中这是设计无法实现的.

Searching over GitHub I found this issue and from what I found this isn't possible by design.

无论如何深入研究,我发现ValidateAsync. symbolsource.org/Public/Metadata/NuGet/Project/Microsoft.AspNet.Identity.Owin/2.2.1/Release/Default/Microsoft.AspNet.Identity.Owin/Microsoft.AspNet.Identity.Owin/DataProtectorTokenProvider.cs?ImageName= Microsoft.AspNet.Identity.Owin"rel =" noreferrer> Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider

However searching deeper I've found that UserManager.ResetPasswordAsync internally uses ValidateAsync from Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider

有了这个,我创建了这个扩展方法:

Having this I've created this extension method:

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using System;
using System.Globalization;
using System.IO;
using System.Text;

namespace Api.Extensions
{
    public enum TokenValidity
    {
        VALID,
        INVALID,
        INVALID_EXPIRED,
        ERROR
    }

    public static class UserManagerExtensions
    {
        public static TokenValidity IsResetPasswordTokenValid<TUser, TKey>(this UserManager<TUser, TKey> manager, TUser user, string token) where TKey : IEquatable<TKey> where TUser : class, IUser<TKey>
        {
            return IsTokenValid(manager, user, "ResetPassword", token);
        }

        public static TokenValidity IsTokenValid<TUser, TKey>(this UserManager<TUser, TKey> manager, TUser user, string purpose, string token) where TKey : IEquatable<TKey> where TUser : class, IUser<TKey>
        {
            try
            {
                //not sure if this is needed??
                if (!(manager.UserTokenProvider is DataProtectorTokenProvider<TUser, TKey> tokenProvider)) return TokenValidity.ERROR;

                var unprotectedData = tokenProvider.Protector.Unprotect(Convert.FromBase64String(token));
                var ms = new MemoryStream(unprotectedData);
                using (var reader = ms.CreateReader())
                {
                    var creationTime = reader.ReadDateTimeOffset();
                    var expirationTime = creationTime + tokenProvider.TokenLifespan;

                    var userId = reader.ReadString();
                    if (!String.Equals(userId, Convert.ToString(user.Id, CultureInfo.InvariantCulture)))
                    {
                        return TokenValidity.INVALID;
                    }

                    var purp = reader.ReadString();
                    if (!String.Equals(purp, purpose))
                    {
                        return TokenValidity.INVALID;
                    }

                    var stamp = reader.ReadString();
                    if (reader.PeekChar() != -1)
                    {
                        return TokenValidity.INVALID;
                    }

                    var expectedStamp = "";
                    //if supported get security stamp for user
                    if (manager.SupportsUserSecurityStamp)
                    {
                        expectedStamp = manager.GetSecurityStamp(user.Id);
                    }

                    if (!String.Equals(stamp, expectedStamp)) return TokenValidity.INVALID;

                    if (expirationTime < DateTimeOffset.UtcNow)
                    {
                        return TokenValidity.INVALID_EXPIRED;
                    }

                    return TokenValidity.VALID;
                }
            }
            catch
            {
                // Do not leak exception
            }
            return TokenValidity.INVALID;
        }
    }

    internal static class StreamExtensions
    {
        internal static readonly Encoding DefaultEncoding = new UTF8Encoding(false, true);

        public static BinaryReader CreateReader(this Stream stream)
        {
            return new BinaryReader(stream, DefaultEncoding, true);
        }

        public static BinaryWriter CreateWriter(this Stream stream)
        {
            return new BinaryWriter(stream, DefaultEncoding, true);
        }

        public static DateTimeOffset ReadDateTimeOffset(this BinaryReader reader)
        {
            return new DateTimeOffset(reader.ReadInt64(), TimeSpan.Zero);
        }

        public static void Write(this BinaryWriter writer, DateTimeOffset value)
        {
            writer.Write(value.UtcTicks);
        }
    }
}

所以现在我可以添加此支票:

so now I'm able to add this check:

if (UserManager.IsResetPasswordTokenValid(user, model.Code) == TokenValidity.INVALID_EXPIRED)
{
    return this.BadRequest("errorResetingPassword", "Link expired");
}

我的问题是:

My question are:

1.是否有更简单的方法?
我的目的是向用户显示电子邮件中的链接已过期的信息,因为现在他所看到的只是密码重置存在问题.

1.Is there an easier way of doing this?
My intention is to show user information that link in email has expired, because right now all he can see is that there was problem with resetting password.

2.如果没有内置的方法可以执行此操作,则潜在的安全漏洞是什么?我将扩展方法用作附加检查.如果我的方法返回true,我仍然会使用ResetPasswordAsync.

2.If there isn't build in method of doing this what are the potential security vulnerabilities? I use my extension method as an additional check. If my method return true I still use ResetPasswordAsync.

推荐答案

UserManager具有您可以使用的VerifyUserTokenAsync和VerifyUserToken方法.

The UserManager has VerifyUserTokenAsync and VerifyUserToken methods that you can use.

有关更多详细信息,请参见 Wouter对问题我如何检查密码重置令牌是否已过期?"的答案.

see Wouter's answer to the question "How can I check if a password reset token is expired?" for more details.

所以您可以使用类似的东西

So you could use something like

if (!UserManager.VerifyUserToken(userId, "ResetPassword", model.code)){
  return this.BadRequest("errorResetingPassword", "Link expired");
}

这篇关于ASP.NET Identity验证ResetPassword令牌是否已过期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆