最近同事用iOS App调用Open API时遇到一个问题:在access token过期后,用refresh token刷新access token时,服务器响应"invalid_grant"错误;而在access token没有过期的情况下,能正常刷新access token。

先查看了一下OAuth规范中的“Refreshing an Expired Access Token”流程图,以确认客户端的操作流程有没有问题。

问题发生在上图中的(G)操作步骤。iOS App就是按上图的流程进行操作的——在调用Open API时,如果服务器响应access token无效,就用refresh token刷新access token,客户端的操作流程一点问题没有。

于是将问题锁定在服务端。打点写日志,发现refresh token刷新access token时服务端先调用了IAuthenticationTokenProvider.ReceiveAsync(),然后调用了IOAuthAuthorizationServerProvider.GrantRefreshToken()方法。对应于我们的实现就是CNBlogsRefreshTokenProvider.ReceiveAsync()与CNBlogsAuthorizationServerProvider.GrantRefreshToken()。

查看ReceiveAsync()方法的实现代码:

public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var refreshToken = await _refreshTokenService.Get(context.Token);
if (refreshToken != null)
{
context.DeserializeTicket(refreshToken.ProtectedTicket);
await _refreshTokenService.Remove(context.Token);
}
}

从上面的代码可以得知,用refresh token刷新access token,实际就是将存储在数据库中的ProtectedTicket反序列为包含access token的ticket,然后根据这个ticket生成access token返回给客户端。而refreshToken.ProtectedTicket是在生成refresh token时由包含access token的ticket序列化生成的,refresh token无法刷新access token,问题很可能就出在它身上。

于是查看生成refresh token部分的代码——CNBlogsRefreshTokenProvider(继承自AuthenticationTokenProvider)的CreateAsync()方法:

var refreshToken = new RefreshToken()
{
Id = refreshTokenId,
ClientId = new Guid(clientId),
UserName = context.Ticket.Identity.Name
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddSeconds(Convert.ToDouble(refreshTokenLifeTime)),
ProtectedTicket = context.SerializeTicket()
}; context.Ticket.Properties.IssuedUtc = refreshToken.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = refreshToken.ExpiresUtc;

当时一看代码,立马恍然大悟。应该是先设置IssuedUtc与ExpiresUtc,然后再SerializeTicket();实际被写成了先SerializeTicket(),后设置IssuedUtc与ExpiresUtc。结果造成refreshToken.ProtectedTicket的过期时间不正确,从而无法刷新access token。

解决方法很简单,只需将context.SerializeTicket()移至设置Ticket的IssuedUtc与ExpiresUtc的代码之后:

var refreshToken = new RefreshToken()
{
Id = refreshTokenId,
ClientId = new Guid(clientId),
UserName = context.Ticket.Identity.Name
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddSeconds(Convert.ToDouble(refreshTokenLifeTime)), };
context.Ticket.Properties.IssuedUtc = refreshToken.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = refreshToken.ExpiresUtc;
refreshToken.ProtectedTicket = context.SerializeTicket();

ASP.NET OAuth:解决refresh token无法刷新access token的问题的更多相关文章

  1. OAuth 白话简明教程 4.刷新 Access Token

    转自:http://www.cftea.com/c/2016/11/6705.asp OAuth 白话简明教程 1.简述 OAuth 白话简明教程 2.授权码模式(Authorization Code ...

  2. iOS实现OAuth2.0中刷新access token并重新请求数据操作

    一.简要概述 OAuth2.0是OAuth协议的下一版本,时常用于移动客户端的开发,是一种比较安全的机制.在OAuth 2.0中,server将发行一个短有效期的access token和长生命期的r ...

  3. ASP.NET OWIN OAuth:refresh token的持久化

    在前一篇博文中,我们初步地了解了refresh token的用途——它是用于刷新access token的一种token,并且用简单的示例代码体验了一下获取refresh token并且用它刷新acc ...

  4. Access Token 机制详解

    我们在访问很多大公司的开放 api 的时候,都会发现这些 api 要求传递一个 access token 参数.这个参数是什么呢?需要去哪里获取这个 access token 呢? access to ...

  5. Web API与OAuth:既生access token,何生refresh token

    在前一篇博文中,我们基于 ASP.NET Web API 与 OWIN OAuth 以 Resource Owner Password Credentials Grant 的授权方式( grant_t ...

  6. ASP.NET没有魔法——ASP.NET OAuth、jwt、OpenID Connect

    上一篇文章介绍了OAuth2.0以及如何使用.Net来实现基于OAuth的身份验证,本文是对上一篇文章的补充,主要是介绍OAuth与Jwt以及OpenID Connect之间的关系与区别. 本文主要内 ...

  7. ASP.NET OAuth、jwt、OpenID Connect

    ASP.NET OAuth.jwt.OpenID Connect 上一篇文章介绍了OAuth2.0以及如何使用.Net来实现基于OAuth的身份验证,本文是对上一篇文章的补充,主要是介绍OAuth与J ...

  8. Access Token 与 Refresh Token【转载哒科普啊】

    Access Token 与 Refresh Token   access token 是客户端访问资源服务器的令牌.拥有这个令牌代表着得到用户的授权.然而,这个授权应该是临时的,有一定有效期.这是因 ...

  9. Oauth2.0(三):Access Token 与 Refresh Token

    access token 是客户端访问资源服务器的令牌.拥有这个令牌代表着得到用户的授权.然而,这个授权应该是临时的,有一定有效期.这是因为,access token 在使用的过程中可能会泄露.给 a ...

随机推荐

  1. 关于解决python线上问题的几种有效技术

    工作后好久没上博客园了,虽然不是很忙,但也没学生时代闲了.今天上博客园,发现好多的文章都是年终总结,想想是不是自己也应该总结下,不过现在还没想好,等想好了再写吧.今天写写自己在工作后用到的技术干货,争 ...

  2. 深入理解DIP、IoC、DI以及IoC容器

    摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.DI以及Ioc容器等概念.通过本文我们将一起学 ...

  3. 我为NET狂官方面试题-数据库篇答案

    题目:http://www.cnblogs.com/dunitian/p/6028838.html 汇总:http://www.cnblogs.com/dunitian/p/5977425.html ...

  4. 使用etree.HTML的编码问题

    title: 使用etree.HTML的编码问题 date: 2015-10-07 17:56:47 categories: [Python] tags: [Python, lxml, Xpath] ...

  5. 在Ubuntu 16.10 安装 git 并上传代码至 git.oschina.net

    1. 注册一个账号和创建项目 先在git.oschina.net上注册一个账号和新建一个project ,如project name 是"myTest". 2.安装git sudo ...

  6. X86和X86_64和X64有什么区别?

    x86是指intel的开发的一种32位指令集,从386开始时代开始的,一直沿用至今,是一种cisc指令集,所有intel早期的cpu,amd早期的cpu都支持这种指令集,ntel官方文档里面称为&qu ...

  7. C#如何在PDF文件添加图片印章

    文档中添加印章可以起一定的作用,比如,防止文件随意被使用,或者确保文档内容的安全性和权威性.C#添加图片印章其实也有很多实现方法,这里我使用的是免费的第三方软件Free Spire.PDF,向大家阐述 ...

  8. 【基于WinForm+Access局域网共享数据库的项目总结】之篇一:WinForm开发总体概述与技术实现

    篇一:WinForm开发总体概述与技术实现 篇二:WinForm开发扇形图统计和Excel数据导出 篇三:Access远程连接数据库和窗体打包部署 [小记]:最近基于WinForm+Access数据库 ...

  9. 关于CSS inline-block、BFC以及外边距合并的几个小问题

    CSS inline-block和BCF对于初学者来说,总是弄不太明白,下面记录下我在学习这块知识的过程中遇到的几个问题,供大家参考,有不足的地方,欢迎大家批评指正. 一.在什么场景下会出现外边距合并 ...

  10. DBobjectsCompareScript(数据库对象比较).sql

    use master goIF EXISTS (SELECT * FROM sysobjects WHERE id = OBJECT_ID(N'[func_CompareDBobjectsReColu ...