OAuth2.0
OAuth1.0虽然在安全性上经过修补已经没有问题了,但还存在其它的缺点,其中最主要的莫过于以下两点:其一,签名逻辑过于复杂,对开发者不够友好;其二,授权流程太过单一,除了Web应用以外,对桌面、移动应用来说不够友好。
为了弥补这些短板,OAuth2.0做了以下改变:
首先,去掉签名,改用SSL(HTTPS)确保安全性,所有的token不再有对应的secret存在,这也直接导致OAuth2.0不兼容老版本。
其次,针对不同的情况使用不同的授权流程,和老版本只有一种授权流程相比,新版本提供了四种授权流程,可依据客观情况选择。
在详细说明授权流程之前,我们需要先了解一下OAuth2.0中的角色:
OAuth1.0定义了三种角色:User、Service Provider、Consumer。而OAuth2.0则定义了四种角色:Resource Owner、Resource Server、Client、Authorization Server:
- Resource Owner:User
- Resource Server:Service Provider
- Client:Consumer
- Authorization Server:Service Provider
也就是说,OAuth2.0把原本OAuth1.0里的Service Provider角色分拆成Resource Server和Authorization Server两个角色,在授权时交互的是Authorization Server,在请求资源时交互的是Resource Server,当然,有时候他们是合二为一的。
下面我们具体介绍一下OAuth2.0提供的四种授权流程:
Authorization Code
可用范围:此类型可用于有服务端的应用,是最贴近老版本的方式。
+----------+ | 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)
Client向Authorization Server发出申请(/oauth/2.0/authorize):
response_type = code client_id redirect_uri scope state
Authorization Server在Resource Owner授权后给Client返回Authorization Code:
code state
Client向Authorization Server发出申请(/oauth/2.0/token):
grant_type = authorization_code code client_id client_secret redirect_uri
Authorization Server在Resource Owner授权后给Client返回Access Token:
access_token token_type expires_in refresh_token
说明:基本流程就是拿Authorization Code换Access Token。
Implicit Grant
可用范围:此类型可用于没有服务端的应用,比如Javascript应用。
采用Implicit Grant方式获取Access Token的授权验证流程又被称为User-Agent Flow,适用于所有无Server端配合的应用(由于应用往往位于一个User Agent里,如浏览器里面,因此这类应用在某些平台下又被称为Client-Side Application),如手机/桌面客户端程序、浏览器插件等,以及基于JavaScript等脚本客户端脚本语言实现的应用,他们的一个共同特点是,应用无法妥善保管其应用密钥(App Secret Key),如果采取Authorization Code模式,则会存在泄漏其应用密钥的可能性。其流程示意图如下:

对于应用而言,其流程只有一步,即直接获取Access 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 | | | +---------+
Client向Authorization Server发出申请(/oauth/2.0/authorize):
response_type = token client_id redirect_uri scope state
Authorization Server在Resource Owner授权后给Client返回Access Token:
access_token token_type expires_in scope state
说明:没有服务端的应用,其信息只能保存在客户端,如果使用Authorization Code授权方式的话,无法保证client_secret的安全。BTW:不返回Refresh Token。
请求数据包格式
为了获取Access Token,应用需要将用户浏览器(或手机/桌面应用中的浏览器组件)到百度OAuth2.0授权服务的“https://openapi.baidu.com/oauth/2.0/authorize”地址上,并带上以下参数:
-
- client_id:必须参数。注册应用时获得的API Key。
- response_type:必须参数。此值固定为“token”。
- redirect_uri:授权后要回调的URI,即接受Access Token的URI。如果用户在授权过程中取消授权,会回调该URI,并在URI末尾附上error=access_denied参数。对于无Web Server的应用,其值可以是“oob”,否则redirect_uri所在域名必须与开发者注册应用时所提供的网站根域名列表或应用站点地址(如果根域名列表没填写)的域名相匹配。
-
- scope:非必须参数。以空格分隔的权限列表,若不传递此参数,代表请求用户的默认权限。关于权限的具体信息请参考“权限列表”。
- state:非必须参数。用于保持请求和回调的状态,授权服务器在回调时(重定向用户浏览器到“redirect_uri”时),会在Query Parameter中原样回传该参数。
- display:非必须参数。登录和授权页面的展现样式,默认为“page”,具体参数定义可以参考“自定义授权页面样式”一节。
例如:“client_id”为“Va5yQRHlA4Fq4eR3LT0vuXV4”的应用要请求某个用户的默认权限和email访问权限,并在授权后需跳转到“http://www.example.com/oauth_redirect”,同时希望在弹出窗口中展现用户登录授权界面,则应用需要重定向用户浏览器到如下URL:
https://openapi.baidu.com/oauth/2.0/authorize? response_type=token& client_id=Va5yQRHlA4Fq4eR3LT0vuXV4& redirect_uri=http%3A%2F%2Fwww.example.com%2Foauth_redirect& scope=email& display=popup& state=xxx
响应数据包格式
若用户登录并接受授权,授权服务将重定向用户浏览器到“redirect_uri”,并在Fragment中追加如下参数:
-
- access_token:要获取的Access Token;
- expires_in:Access Token的有效期,以秒为单位;请参考“Access Token生命周期方案”
- scope:Access Token最终的访问范围,即用户实际授予的权限列表(用户在授权页面时,有可能会取消掉某些请求的权限),关于权限的具体信息参考“权限列表”一节;
- state:如果请求获取Access Token时带有state参数,则将该参数原样返回。
- session_key:基于http调用Open API时所需要的Session Key,其有效期与Access Token一致;
- session_secret:基于http调用Open API时计算参数签名用的签名密钥。
例如:
HTTP/1.1 302 Found Location: http://www.example.com/oauth_redirect#access_token=1.a6b7dbd428f731035f771b8d15063f61.86400.1292922000-2346678-124328&expires_in=86400&scope=basic%20email&session_key=ANXxSNjwQDugf8615OnqeikRMu2bKaXCdlLxn&session_secret=248APxvxjCZ0VEC43EYrvxqaK4oZExMB&state=xxx
若请求错误或用户拒绝授权,授权服务将重定向用户浏览器到“redirect_uri”,并在Fragment中追加如下参数:
-
- error:错误码;关于错误码的详细信息请参考“百度OAuth2.0错误响应”一节。
- error_description:错误描述信息,用来帮助理解和解决发生的错误;
- state:如果请求获取Access Token时带有state参数,则将该参数原样返回。
例如:
HTTP/1.1 302 Found Location: http://www.example.com/oauth_redirect#error=access_denied&error_description=user%20didnot%20allow%20your%20authorization%20request&state=xxx
开发者需要注意的事项
-
- 默认情况下,Access Token的有效期为永久(一个月的Access Token + 10年的Refresh Token)。关于Token方案的详细信息,请参考“Access Token生命周期方案”一节。
- 采用Implicit Grant方式获取Access Token时,不会返回Refresh Token,即使该应用申请了1个月Access Token + 10年的Refresh Token这种token生命周期方案。
- 获取Access Token时所返回的session_key和session_secret参数不是OAuth2.0协议标准规定的返回参数,而是百度OAuth2.0服务扩展加入的,目的是使得开发者可以基于http调用百度的Open API,因为基于https调用Open API虽然更为简单,但毕竟响应速度更差(比基于http的要差一倍时间左右)。
Resource Owner Password Credentials
可用范围:不管有无服务端,此类型都可用。
+----------+ | Resource | | Owner | | | +----------+ v | Resource Owner (A) Password Credentials | v +---------+ +---------------+ | |>--(B)---- Resource Owner ------->| | | | Password Credentials | Authorization | | Client | | Server | | |<--(C)---- Access Token ---------<| | | | (w/ Optional Refresh Token) | | +---------+ +---------------+
Clien向Authorization Server发出申请(/oauth/2.0/token):
grant_type = password username password scope
AuthorizationServer给Client返回AccessToken:
access_token token_type expires_in refresh_token
说明:因为涉及用户名和密码,所以此授权类型仅适用于可信赖的应用。
Client Credentials
可用范围:不管有无服务端,此类型都可用。
+---------+ +---------------+ | | | | | |>--(A)- Client Authentication --->| Authorization | | Client | | Server | | |<--(B)---- Access Token ---------<| | | | | | +---------+ +---------------+
Client向Authorization Server发出申请(/oauth/2.0/token):
grant_type = client_credentials client_id client_secret scope
Authorization Server给Client返回Access Token:
access_token token_type expires_in
说明:此授权类型仅适用于获取与用户无关的公共信息。BTW:不返回Refresh Token。
…
流程中涉及两种Token,分别是Access Token和Refresh Token。通常,Access Token的有效期比较短,而Refresh Token的有效期比较长,如此一来,当Access Token失效的时候,就需要用Refresh Token刷新出有效的Access 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 +---------------+
Client向Authorization Server发出申请(/oauth/2.0/token):
grant_type = refresh_token refresh_token client_id client_secret scope
Authorization Server给Client返回Access Token:
access_token expires_in refresh_token scope
…
不过并不是所有人都对OAuth2.0投赞成票,有空可以看看:OAuth 2.0对Web有害吗?