最近同事用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. 异常处理汇总 ~ 修正果带着你的Net飞奔吧!

    经验库开源地址:https://github.com/dunitian/LoTDotNet 异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983 ...

  2. Python列表去重

    标题有语病,其实是这样的: 假设有两个列表 : L1 = [1,2,3,4] ; L2 = [1,2,5,6] 然后去掉L1中包含的L2的元素 直接这样当然是不行的: def removeExists ...

  3. 窥探Vue.js 2.0 - Virtual DOM到底是个什么鬼?

    引言 你可能听说在Vue.js 2.0已经发布,并且在其中新添加如了一些新功能.其中一个功能就是"Virtual DOM". Virtual DOM是什么 在之前,React和Em ...

  4. 120项改进:开源超级爬虫Hawk 2.0 重磅发布!

    沙漠君在历时半年,修改无数bug,更新一票新功能后,在今天隆重推出最新改进的超级爬虫Hawk 2.0! 啥?你不知道Hawk干吗用的? 这是采集数据的挖掘机,网络猎杀的重狙!半年多以前,沙漠君写了一篇 ...

  5. C#泛型详解(转)

    初步理解泛型: http://www.cnblogs.com/wilber2013/p/4291435.html 泛型中的类型约束和类型推断 http://www.cnblogs.com/wilber ...

  6. BPM配置故事之案例11-操作外部数据源

    小明:可以获取ERP数据了-- 老李:哦,这么快?小伙子,我非常看好你,来来,别急着走,再陪我聊会-- 小明:--您老人家不是又要改流程吧? 老李:没有没有,哎嘿嘿嘿,我们这不都是为公司效率着想嘛,这 ...

  7. iOS开发 判断当前APP版本和升级

    从iOS8系统开始,用户可以在设置里面设置在WiFi环境下,自动更新安装的App.此功能大大方便了用户,但是一些用户没有开启此项功能,因此还是需要在程序里面提示用户的 方法一:在服务器接口约定对应的数 ...

  8. docker4dotnet #1 – 前世今生 & 世界你好

    作为一名.NET Developer,这几年看着docker的流行实在是有些眼馋.可惜的是,Docker是基于Linux环境的,眼瞧着那些 java, python, node.js, go 甚至连p ...

  9. Oracle:一个用户操作多个表空间中表的问题(转)

    原文地址:http://blog.csdn.net/shmiloy001/article/details/6287317 首先,授权给指定用户. 一个用户的默认表空间只能有一个,但是你可以试下用下面的 ...

  10. NOT IN 和NOT EXISTS

    今天写了一个简单的NOT IN语句,结果跟预期大相径庭,百度之发现深坑一个,遂录之. 登陆账户表logins code name status a admin N b guest N c member ...