又是工作相关日志。最近收到升级客户端VK SDK的ticket,要求升级SDK以适配OAuth 2.1,阅读接入文档以后遇到了PKCE这个概念。
概念
全称 (Proof Key for Code Exchange),根据gemini解答是OAuth 2.0模式的一个扩展。过去在Web端auth code通过重定向返回,而在移动互联网时代变成了通过custom scheme返回。问题是手机系统支持存在多个相同custom scheme。因此恶意应用可以监听相同scheme进而对auth code截胡。
而PKCE做的就是客户端获取auth code时请求参数携带一串哈希过的数据(challenge)作为令牌,平台服务器会缓存,随后客户端用auth code兑换token时携带哈希前的数据(verifier),然后由平台服务器进行相同加密计算并与之前的缓存数据进行比较。
所以也就解释了为什么在实现服务器侧token-exchange时要先在服务器中生成PKCE。以及为什么这个叫”proof” key。
都到这了索性往前再看看。
OAuth历史
OAuth 1.0 ( 2007 )
OAtuh的最初设计目的,是在不给第三方平台用户账号密码的情况下,让它可以访问用户的数据。这和现在人们直接将他当作某种“登录”方式的理解有很大出入。
过去如果想允许某个应用读取用户数据,我们必须提供账号密码,并且没有专门的途径”撤回“这个行为: 只能是修改密码。 OAuth 1.0引入了Token的概念,类似一张临时房卡,第三方平台可以拿着用户交给他的临时房卡来访问用户房间(数据)。
授权流程
授权流程分为三步
- 获取临时token
- 第三方应用(Consumer) 向服务提供者(Service Provider)表明身份,发送一个经过签名的请求到服务器的Request Token URL。携带参数consumer_key、oauth_nonce、oauth_timestamp 和 oauth_callback
- 服务器验证签名后,返回一个Request Token 和 Token Secret
- 用户授权
- 第三方应用拿着Request Token将用户引导到授权页面。 按我们现在的理解,就是展示“授权登录”这个页面。
- 用户在页面上点击确认授权,服务器返回一个 Verification Code
- 服务器生成code的同时会在本地将他和传过来的Request Token进行关联记录。另外这个code并不是返回给用户(Consumer),而是转发Callback URL。这个Callback URL是Consumer在请求Request Token时会携带过去的。
- 换取正式access token
- 第三方应用拿着 Verification Code 和 Request Token 请求服务器接口
- 服务器检查这个Code和 Request Token是否匹配
签名生成
生成签名用到signing key,它由两部分组成: “${consumerSecret}&${tokenSecret}”。
有个细节是,在用户完成授权拿到Access Token这一步之前,我们只有提前在服务器配置好的consumer-secret。所以在完成授权之前的步骤中我们仍然会携带签名,只是这时我们用的key只有consumer-secret,还没有使用token secret。因此,可以说整个流程中,前一段是用了半截key进行签名直到完成授权。
签名信息传输
OAuth 1.0 对签名信息的传输没有严格的要求,他可以在 Header里(比如Authorization: xxx),或者在GET请求中拼接在url后面作为请求参数,又或者是POST请求时直接在body里传递。所以他主要关注的是两端接收后如何验证,而不在于传输方式。
callback问题
1.0时期,callback还不需要提前在provider服务器中进行配置。毕竟经过了签名校验,所以provider service也认为传递过来的callback URL是可靠的。实践中,平台可能会有自己的额外实现逻辑,比如Google/Twitter会要求Callback URL至少要与注册的应用域名一致。
与SSL的渊源
根据考征,OpenSSL诞生比OAuth1.0还要早,但因为时代的局限吧,浏览器这个场景还没有大量投入使用这个技术。如今我们将采用HTTP2或者HTTPS视为很自然,但在OAuth1.0那个时期浏览器仍在大量使用HTTP。因为这个客观条件,OAuth1.0非常关注安全性问题。
OAuth 2.0 (2012)
OAuth 2.0经过了重新设计,因此与1.0完全不兼容。
这个时期HTTPS已经足够普及,因此OAuth 2.0的协议设计也建立在OpenSSL的基础上。他明确说明要求配合TLS/SSL环境使用,所以不再需要在应用层面进行签名与校验,而是在请求头中携带Bearer Token明文传输(安全性交给SSL)。
新特性
Bearer Token
- 这个时期HTTPS已经足够普及,因此OAuth 2.0的协议设计也建立在OpenSSL的基础上,因此他明确说明要求配合TLS/SSL环境使用
- 不需要计算签名,但需要在请求头中携带Bearer Token进行鉴权。
多种Grant Type
Authorization code
最经典的模式,和1.0相似,服务器拿到code以后再交换access token
Implicit
曾用于没有后端的纯前端应用(PKCE就是解决这个场景的问题)
2.1中被废弃
Resource owner password credentials
允许直接输入用户名密码
2.1中被废弃
client credentials
用于机器对机器(M2M)的通信
Refresh Token
- 1.0时期,下发的Token有效期可以很长,有的长达1年
- 2.0中引入了refresh token概念。现在access token只会有很短的有效期,但可以通过refresh token重新获取新的access token,已避免要求用户反复授权的问题。
Scope
- 引入了Scope得概念,用于展示consumer具体将有哪些数据权限
授权流程
获取授权码
第三方应用(Client)引导用户跳转到服务商的授权页面。
此时携带 client_id、redirect_uri、response_type=code 以及 scope(权限范围)。
兑换访问令牌
客户端后端拿到 code 后,在后台(Server to Server)向服务商发起请求
callback URL变化
1.0时期,规范中callback URL不需要提前注册至服务器,而2.0开始,redirect_uri就变成必须提前注册了。没错,这个uri仍然是由consumer发送的。
OAuth 2.1
OAuth 2.1 并不是对2.0的重写,而是整合了多年来的相关扩展和实践,以提供一个统一&安全的规范。
目前为止OAuth2.1仍然在制订中,它整个规范并没有完全定稿。除了上述PKCE以外还有以下已经确定的改进:
- Redirect URIs 必须完全匹配
- 移除隐式模式 (Implicit)
- 移除密码模式
- 禁止在 URL 查询字符串中传输 Bearer Token
- 加强刷新令牌 (Refresh Tokens) 的安全性
grant type变化
移除一些grant type以后,2.1中还支持的授权方式分别是:
- auth code
- client credentials
实践与变化
除了协议细节的区别以外,用户使用情景也有了变化:
- 不管是callback URL还是叫 redirect uri,原本设计目的都是在授权以后让浏览器302转发到这个地址,从而让consumer server处理auth code并在服务器侧完成相关code exchange,然后再返回结果给浏览器。
- 经历移动互联网爆发以后,现在的授权流程变成通过移动端App完成,所以有了两点变化:
- 不管是consumer还是service provider,在这个场景下都变成了mobile App,service provider的App通过API与服务器进行通信,完成一些鉴权交互。
- redirect uri并不是转发到服务器地址,而是配置了custom scheme,从而在授权完以后跳转本地自定义scheme地址,一般是consumer对应的App来接收并自己处理code的交换。
- 正是因为现在这种通过custom scheme变成了常态,所以PKCE的引入才显得更有意义。否则任何App都可以声明相同scheme来接收授权结果。
Server端实践
根据客户端是否需要直接向平台发起请求,在登录时现在有两种实现,一种是授权后直接返回对应platform的access token,另一种是服务器实现自己的access token,也就是说,客户端不持有平台access token。
- 如果是服务器自己实现token可能就会有更多考量:应对用户量过大可能会引入雪花ID的概念,或者通过uuid来避免暴露基本的用户id索引给外部,防止别人通过递增id来获取额外信息。
- 自己实现token时,另外就是控制权限和有效期。另外在集群环境还有数据一致性问题。常规做法可能是将token存入一个redis中,从而在集群间保证一致性。
- 也有更极端的做法,就是服务器只负责生成,但不进行保存。token可能是通过base64进行编码,自己携带了过期时间,这种的就没有了服务器侧的维护压力,但同时也失去了对token进行分别控制/注销的能力。并对办法就是缩短access token的有效期。