在开放受权中,第三方应用(Client)多是一个Web站点,也多是在浏览器中运行的一段JavaScript代码,还多是安装在本地的一个应用程序。这些第三方应用都有各自的安全特性。对于Web站点来讲,它与RO浏览器是分离的,它能够本身保存协议中的敏感数据,这些密钥能够不暴露给RO;对于JavaScript代码和本地安全的应用程序来讲,它原本就运行在RO的浏览器中,RO是能够访问到Client在协议中的敏感数据。web
OAuth2.0为了支持这些不一样类型的第三方应用,提出了下面四种受权类型:浏览器
流程 | Response Type(第一次请求) | Grant Type(第二次请求) | 可带Refresh Token | 说明 |
---|---|---|---|---|
受权码 | code | authorization_code | 是 | 常规流程 |
Implicit | token | - | 否 | 适用于纯JS程序 |
用户认证 | - | password | 是 | 客户端高度可信,且受权码流程不方便实施 |
客户端 | - | client_credentials | 否 | 客户端高度可信,拥有被操做资源(自用型),或操做非敏感资源 |
Authorization code 受权适用于PC,无线客户端等须要和第三方server进行交互的应用场景。使用Authorization code受权,第三方可以集中处理用户的受权请求和受权结果,适用于有server端的应用。安全
Authorization code受权模式分为两步,首先获取authorization code,而后用code获取acces token。服务器
示意图:app
+----------+ | Resource | | Owner | | | +----------+ ^ | (B) +----|-----+ Client Identifier +---------------+ | -+----(A)-- & Redirection URI ---->| | | User- | | Authorization | | Agent -+----(B)-- User authenticates --->| Server | | | | | | -+----(C)-- Authorization Code ---<| | +-|----|---+ +---------------+ | | ^ v (A) (C) | | | | | | ^ v | | +---------+ | | | |>---(D)-- Authorization Code ---------' | | Client | & Redirection URI | | | | | |<---(E)----- Access Token -------------------' +---------+ (w/ Optional Refresh Token)
交互图:性能
+--------+ +---------------+ | |--(A)------- Authorization Grant --------->| | | | | | | |<-(B)----------- Access Token -------------| | | | & Refresh Token | | | | | | | | +----------+ | | | |--(C)---- Access Token ---->| | | | | | | | | | | |<-(D)- Protected Resource --| Resource | | Authorization | | Client | | Server | | Server | | |--(E)---- Access Token ---->| | | | | | | | | | | |<-(F)- Invalid Token Error -| | | | | | +----------+ | | | | | | | |--(G)----------- Refresh Token ----------->| | | | | | | |<-(H)----------- Access Token -------------| | +--------+ & Optional Refresh Token +---------------+
一、获取Authorization Code网站
请求参数加密
若是用户成功受权,则会跳转到指定的回调地址,并在redirect_uri地址后带上Authorization Code和原始的state值spa
二、经过Authorization Code获取Access Token设计
请求参数
返回参数
三、[可选] 权限自动续期,获取access_token
Access_token通常须要根据应用特性设定有效期,过时后须要用户从新受权或采用自动续期的方式。
请求参数
若是受权成功,则会返回和步骤二一样的结果。
Implicit受权通常适用于没有server端的客户端应用,由客户端发起受权请求,保存和处理access_token,但有些应用(如web应用等)为了提升用户体验,简化受权过程,也会常采用Implicit受权方式(注意,这种受权方式没有返回refresh_token。)
示意图:
+----------+ | Resource | | Owner | | | +----------+ ^ | (B) +----|-----+ Client Identifier +---------------+ | -+----(A)-- & Redirection URI --->| | | User- | | Authorization | | Agent -|----(B)-- User authenticates -->| Server | | | | | | |<---(C)--- Redirection URI ----<| | | | with Access Token +---------------+ | | in Fragment | | +---------------+ | |----(D)--- Redirection URI ---->| Web-Hosted | | | without Fragment | Client | | | | Resource | | (F) |<---(E)------- Script ---------<| | | | +---------------+ +-|--------+ | | (A) (G) Access Token | | ^ v +---------+ | | | Client | | | +---------+ Note: The lines illustrating steps (A) and (B) are broken into two parts as they pass through the user-agent.
交互图:
+----------+ | Resource | | Owner | | | +----------+ v | Resource Owner (A) Password Credentials | v +---------+ +---------------+ | |>--(B)---- Resource Owner ------->| | | | Password Credentials | Authorization | | Client | | Server | | |<--(C)---- Access Token ---------<| | | | (w/ Optional Refresh Token) | | +---------+ +---------------+
交互图:
请求参数
若是成功受权,则会跳转到redirect_uri指定的回调地址,并带上access_token、expires_in、state以及与应用相关的参数。注意,这种受权方式没有返回refresh_token。
该受权方式获取access token通常只有一步,相似以下GET/POST请求:https://open.xxx.com/oauth2/access_token?client_id=xxxx&client_secret=xxxxx&grant_type=password&username=xxx&password=xxx
协议设计中,为何要使用authorization_code来交换access_token?这是读者容易想到的一个问题。也就是说,在协议的第3步,为何不直接将access_token经过重定向方式返回给Client呢?好比:
HTTP/1.1 302 Location: https://www.facebook.com/?access_token=ya29.AHES6ZSXVKYTW2VAGZtnMjD&token_type=Bearer&expires_in=3600
若是直接返回access_token,协议将变得更加简洁,并且少一次Client与AS之间的交互,性能也更优。那为什么不这么设计呢?协议文档[1]中并无给出这样设计的理由,但也不难分析:(1) 浏览器的redirect_uri是一个不安全信道,此方式不适合于传递敏感数据(如access_token)。由于uri可能经过HTTP referrer被传递给其它恶意站点,也可能存在于浏览器cacher或log文件中,这就给攻击者盗取access_token带来了不少机会。另外,此协议也不该该假设RO用户代理的行为是可信赖的,由于RO的浏览器可能早已被攻击者植入了跨站脚本用来监听access_token。所以,access_token经过RO的用户代理传递给Client,会显著扩大access_token被泄露的风险。 但authorization_code能够经过redirect_uri方式来传递,是由于authorization_code并不像access_token同样敏感。即便authorization_code被泄露,攻击者也没法直接拿到access_token,由于拿authorization_code去交换access_token是须要验证Client的真实身份。也就是说,除了Client以外,其余人拿authorization_code是没有用的。 此外,access_token应该只颁发给Client使用,其余任何主体(包括RO)都不该该获取access_token。协议的设计应能保证Client是惟一有能力获取access_token的主体。引入authorization_code以后,即可以保证Client是access_token的惟一持有人。固然,Client也是惟一的有义务须要保护access_token不被泄露。 (2) 引入authorization_code还会带来以下的好处。因为协议须要验证Client的身份,若是不引入authorization_code,这个Client的身份认证只能经过第1步的redirect_uri来传递。一样因为redirect_uri是一个不安全信道,这就额外要求Client必须使用数字签名技术来进行身份认证,而不能用简单的密码或口令认证方式。引入authorization_code以后,AS能够直接对Client进行身份认证(见步骤4和5),并且能够支持任意的Client认证方式(好比,简单地直接将Client端密钥发送给AS)。 在咱们理解了上述安全性考虑以后,读者也许会有豁然开朗的感受,懂得了引入authorization_code的妙处。那么,是否是必定要引入authorization_code才能解决这些安全问题呢?固然不是。笔者将会在另外一篇博文给出一个直接返回access_token的扩展受权类型解决方案,它在知足相同安全性的条件下,使协议更简洁,交互次数更少。