简体   繁体   English

OWIN ASP.NET-如果访问令牌过期,则无法使用刷新令牌生成访问令牌

[英]OWIN ASP.NET - Cant generate Access Token using Refresh Token if Access Token is expired

When I try to generate an Access Token using the Refresh Token before Access Token expires, the system generate a new one and everything is okey. 当我尝试在访问令牌过期之前使用刷新令牌生成访问令牌时,系统会生成一个新的令牌,并且一切正常。 But if the Access Token is expired, the request return invalid_grant . 但是,如果访问令牌已过期,则请求返回invalid_grant

Isn't the Validated() method from GrantRefreshToken generate the Access Token using the identity I stored in the dictionary from the previously Access Token? GrantRefreshTokenValidated()方法不是使用存储在字典中的身份从以前的访问令牌生成访问令牌的访问令牌吗?

And how can I block clients from asking for new Access Token using the same Refresh Token if the last one isn't expired yet? 如果最后一个尚未过期,我如何阻止客户使用相同的刷新令牌来请求新的访问令牌?

Here is my code: 这是我的代码:

Startup.cs: Startup.cs:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions()
        {
            AllowInsecureHttp = true,

            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),

            Provider = new OAuthProvider(),
            RefreshTokenProvider = new RefreshTokenProvider()
        });
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
        app.UseWebApi(config);
    }
}

OAuthProvider.cs: OAuthProvider.cs:

public class OAuthProvider : OAuthAuthorizationServerProvider
{
    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
        return Task.FromResult<object>(null);
    }

    public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        if (context.UserName == "admin" && context.Password == "123456")
        {
            var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
            claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
            claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));

            var ticket = new AuthenticationTicket(claimsIdentity, null);
            context.Validated(ticket);
        }
        return Task.FromResult<object>(null);
    }

    public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
    {
        context.Validated(context.Ticket);
        return Task.FromResult<object>(null);
    }
}

RefreshTokenProvider.cs: RefreshTokenProvider.cs:

public class RefreshTokenProvider : AuthenticationTokenProvider
{
    private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();

    public override Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        var guid = Guid.NewGuid().ToString();
        _refreshTokens.TryAdd(guid, context.Ticket);

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

    public override Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {
        if (_refreshTokens.TryRemove(context.Token, out AuthenticationTicket ticket))
        {
            context.SetTicket(ticket);
        }
        return Task.FromResult<object>(null);
    }
}

Sorry for bad english, I hope you understand! 对不起,英语不好,希望您能理解!

EDIT: 编辑:

Well, I modified the code to implement Database support and set the Refresh Token expiration to 5 minutes. 好吧,我修改了代码以实现数据库支持,并将“刷新令牌”到期时间设置为5分钟。 The expiration is small for testing purposes. 出于测试目的,到期时间很小。

Here is the result: 结果如下:

OAuthProvider.cs: OAuthProvider.cs:

public class OAuthProvider : OAuthAuthorizationServerProvider
{
    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
        return Task.FromResult<object>(null);
    }

    public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        try
        {
            var account = AccountRepository.Instance.GetByUsername(context.UserName);
            if (account != null && Global.VerifyHash(context.Password, account.Password))
            {
                var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
                claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, account.Username));
                claimsIdentity.AddClaim(new Claim("DriverId", account.DriverId.ToString()));

                var newTicket = new AuthenticationTicket(claimsIdentity, null);
                context.Validated(newTicket);
            }
        }
        catch { }

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

    public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
    {
        context.Validated();
        return Task.FromResult<object>(null);
    }
}

RefreshTokenProvider.cs: RefreshTokenProvider.cs:

public class RefreshTokenProvider : AuthenticationTokenProvider
{
    public override Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        var refreshToken = new TokenModel()
        {
            Subject = context.Ticket.Identity.Name,
            Token = GenerateToken(),
            IssuedUtc = DateTime.UtcNow,
            ExpiresUtc = DateTime.UtcNow.AddMinutes(5)
        };

        context.Ticket.Properties.IssuedUtc = refreshToken.IssuedUtc;
        context.Ticket.Properties.ExpiresUtc = refreshToken.ExpiresUtc;

        refreshToken.Ticket = context.SerializeTicket();

        try
        {
            TokenRepository.Instance.Insert(refreshToken);
            context.SetToken(refreshToken.Token);
        }
        catch { }

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

    public override Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {
        try
        {
            var refreshToken = TokenRepository.Instance.Get(context.Token);
            if (refreshToken != null)
            {
                if (TokenRepository.Instance.Delete(refreshToken))
                {
                    context.DeserializeTicket(refreshToken.Ticket);
                }
            }
        }
        catch { }

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

    private string GenerateToken()
    {
        HashAlgorithm hashAlgorithm = new SHA256CryptoServiceProvider();

        byte[] byteValue = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString("N"));
        byte[] byteHash = hashAlgorithm.ComputeHash(byteValue);

        return Convert.ToBase64String(byteHash);
    }
}

Thanks for the support! 感谢您的支持!

It seems that the refresh token has the same expiration time as the access token. 似乎刷新令牌具有与访问令牌相同的到期时间。 So you'll need to extend the expiration of the refresh token: 因此,您需要延长刷新令牌的有效期:

public override Task CreateAsync(AuthenticationTokenCreateContext context)
{
    var form = context.Request.ReadFormAsync().Result;
    var grantType = form.GetValues("grant_type");

    if (grantType[0] != "refresh_token")
    {
        ...

        // One day
        int expire = 24 * 60 * 60;
        context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddSeconds(expire));
    }
    base.Create(context);
}

-- Update -- -更新-

I've updated the code to answer the question in your comment. 我已更新代码以回答您评论中的问题。 It's more than you've asked for. 它超出了您的要求。 But I think it will help to explain. 但是我认为这将有助于解释。

It all depends on the requirement and chosen strategy. 这完全取决于需求和选择的策略。 To explain the code: 解释代码:

Each time an access token is issued you will also hit this piece of code which will add a refresh token. 每次发出访问令牌时,您也将点击这段代码,这将添加一个刷新令牌。 In this strategy I will issue a new refresh token only when grant_type is not 'refresh_token'. 在此策略中,仅当grant_type不是'refresh_token'时,我才会发出新的刷新令牌。 This means that at some point the refresh token expires and the user has to login again. 这意味着刷新令牌将在某个时刻过期,用户必须再次登录。

In the example the user has to login every day again. 在该示例中,用户必须每天再次登录。 But if the user logs in before the refresh_token expires, a new refresh token will be issued. 但是,如果用户在refresh_token过期之前登录,则会发出新的刷新令牌。 That way the refresh token has an absolute expiration time, forcing the user to login at least once a day. 这样,刷新令牌具有绝对到期时间,从而迫使用户每天至少登录一次。

If you want 'sliding expiration', you can add a refresh token every time you issue an access token. 如果要“到期”,可以在每次发出访问令牌时添加刷新令牌。 Please note that the user may never have to login again, unless the refresh token expires before refreshing. 请注意,除非刷新令牌在刷新之前过期,否则用户可能永远不必再次登录。

In any case I wouldn't block users from creating access tokens when grant_type is not refresh_token. 无论如何,当grant_type不是refresh_token时,我都不会阻止用户创建访问令牌。 Since that is user interaction. 因为那是用户交互。 The access token has a very limited window (think of hours or minutes, not days), so it will expire soon enough. 访问令牌的窗口非常有限(认为是几小时或几分钟,而不是几天),因此它将很快过期。 You do not want to validate an access token each time you receive it in a header, since this means you'll need to check the database for each call. 您不想每次在标头中收到访问令牌时都对其进行验证,因为这意味着您需要为每个调用检查数据库。

Instead think of a strategy to invalidate 'old' refresh tokens. 相反,请考虑使“旧”刷新令牌无效的策略。 You could save the hashed version of the last refresh token in the database. 您可以将最后刷新令牌的哈希版本保存在数据库中。 If it doesn't match then respond with an 'invalid_grant' error. 如果不匹配,则返回“ invalid_grant”错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用JWT令牌使访问令牌过期时刷新令牌调用 - Refresh Token call when Access token expired using JWT token 在 ASP.NET WebAPI 2 中缩短 OWIN 返回的访问令牌 - Shorten access token returned by OWIN in ASP.NET WebAPI 2 刷新令牌在访问令牌后立即过期 - Refresh token expired as soon as access token 访问令牌过期时使用刷新令牌 - Use Refresh Token When Access Token is Expired 使用OWIN在ASP.NET MVC 5应用程序中存储和检索facebook访问令牌 - Store and retrieve facebook access token in ASP.NET MVC 5 app using OWIN 在 ASP.NET MVC 中过期后刷新访问令牌 - Refresh access token after expiry in ASP.NET MVC 如何从 gmail 中的刷新令牌获取访问令牌 asp.net 中的 api - How to get access token from refresh token in gmail api in asp.net 在asp.net core mvc中通过refresh_token无声更新access_token - Silent renew access_token via refresh_token in asp.net core mvc C# Owin 处理 401 访问令牌错误并使用刷新令牌重新生成访问令牌 - C# Owin handle 401 access token errors and regenerate access tokens using refresh token 在ASP.NET MVC5 OWIN中获取Google访问令牌的最佳方法 - Best way to get Google access token in ASP.NET MVC5 OWIN
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM