OAuth2授权标准
引自: [认证 & 授权] 1. OAuth2授权.。
OAuth2简介
OAuth2是一个开放授权标准,它允许让第三方应用访问该用户在某服务的特定私有服务
OAuth2的四个重要角色
- Resource Owner ,资源拥有者;
- Resource Server ,资源服务器;
- Client ,第三方客户端,指代任何可以消费资源服务器的第三方应用;
- Authorization Server ,授权服务器,管理Resource Owner,Client和Resource Server的三角关系的中间层。
OAuth2解决问题的关键在于使用Authorization server提供一个访问平局给Client,使得Client可以在不知道Resource owner在Resource server上的用户名和密码的情况下消费Resource owner的受保护资源。
部署OAuth2需要的完成的工作
由于OAuth2引入了Authorization server来管理Resource Owner,Client和Resource Server的三角关系,那么想要用上OAuth2,是实现以下功能的。
- 增加一个Authorization server,提供授权的实现,一般由Resource server来提供。
- Resource server为第三方应用程序提供注册接口。
- Resource server开放相应的受保护资源的API。
- Client注册成为Resource server的第三方应用。
- Client消费这些API。
作为资源服务提供商来说,1,2,3这三件事情是需要完成的。
作为第三方应用程序,要完成的工作是在4和5这两个步骤中。
其中作为Resource owner来说,是不用做什么的,是OAuth2收益的千千万万的最终人类用户。
作为Client
在Client取得client_id和client_secret之后。使用这些信息来发起授权请求、获取access_token请求和消费受包含的资源。
OAuth2的授权流程
在上述OAuth完整流程中,(A) -> (B) -> © -> (D) 是授权的过程;(E) -> (F)是消费资源的过程。
- (A) 发起授权请求;
- (B) 返回授权许可;
- © 使用授权许可向Authorization server发起请求;
- (D) Authorization server验证身份和授权许可,发送访问令牌给Client;
- (E) 用访问令牌请求资源服务器(Resource Server);
- (F) 根据访问令牌返回客户端资源;
这其中比较重要的一个该店是访问令牌它代表的信息是整个OAuth2的核心,也是ABCD这些步骤最终要得到的信息。
OAuth2的4种授权许可
授权许可是一个代表资源所有者授权(访问收保护资源)的凭据,客户端用它来获取访问令牌。即,授权许可是资源拥有者授予客户端获取资源访问令牌的一个凭据。
OAuth2定义了四种许可类型以及用于定义其他类型的可扩展性机制:
- Authorization Code: 授权码;
- Implicit: 隐式许可;
- Resource Owner Password Credentials: 资源所有者密码凭据;
- Client Credentials:客户端凭据;
Authorization Code
这是OAuth2最常用的一种授权许可类型。要求Client具体可公开访问的Server服务器来接受Authoriation Code,具体的流程如下:
- (A) Client使用浏览器(用户代理)访问Authorization server。也就是用浏览器访问一个URL,这个URL是Authorization server提供的,访问的时候Client需要提供(客户端标识,请求范围,本地状态和重定向URL)这些参数。
- (B) Authorization server验证Client在(A)中传递的参数信息,如果无误则提供一个页面供Resource owner登陆,登陆成功后选择Client可以访问Resource server的哪些资源以及读写权限。
- © 在(B)无误后返回一个授权码(Authorization Code)给Client。
- (D) Client拿着©中获得的授权码(Authorization Code)和(客户端表示、重定向URL等信息)作为参数,请求Authorization server提供的获取访问令牌的URL。
- (E) Authorization server返回访问令牌和可选的刷新令牌已经令牌有效时间等信息给Client。
Authorization Request
对应步骤(A),客户端土工以下参数请求Authorization Server:
- response_type:必选。值固定为"code"。
- client_id:必选。第三方应用的标识ID。
- state:推荐。Client提供的一个字符串,服务器会原样返回给Client。
- redirect_uri:必选。授权成功后的重定向地址。
- scope:可选。表示授权范围。
示例如下:
GET /authorize?response_type=code&client_id=1&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Foauth2&scope=user,photo HTTP/1.1
Host: server.example.com
Authorization Response
对应步骤©,Authorization Server会返回如下信息:
- code:授权码。
- state:步骤(A)中客户端提供的参数原样返回。
比如示例如下:
HTTP/1.1 302 Found
Location: https://client.example.com/oauth2code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
Location头部指向步骤(A)提供的redirect_uri地址,同时携带code信息和state信息给client,这样浏览器在重定向的时候就会以GET的方式访问Client提供的redirect_uri,同时Client接收到code信息和state信息。下一步就可以请求access_token了。
Access Token Request
对应步骤(D),客户端提供以下参数请求Authorization Server:
- grant_type:必选。固定值"authorization_code"。
- code:必选。Authorization Response中响应的code。
- redirect_uri:必选。必须和Authorization Request中提供的redirect_uri相同。
- client_id:必选。毕珣和Authorization Request中提供的client_id相同。
示例如下:
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=123&client_id=1&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Foauth2
Access Token Response
对应步骤(E),Authorization Server会返回如下典型的信息:
- access_token:访问令牌。
- refresh_token:刷新令牌。
- expires_in:过期时间。
示例如下:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
Implicit
这个是Authorization Code的简化版本。其中省略了拌饭授权码(Authorization Code)给客户端的过程,而是之间返回访问令牌和可选的刷新令牌。其适用于没有Server服务器来接受处理Authorization Code的第三方应用,其流程如下:
和Authorzation Code类型下重要的区分就是省略了Authorzation Respongse和Access Token Request。而是直接由Authorzation Request返回Access Token Response信息,具体如下。
Authorization Request
客户端提供以下参数请求Authorization Server:
- response_type:必选。值固定为“token”。
- client_id:必选。第三方应用的标识ID。
- state:推荐。Client提供的一个字符串,服务器会原样返回给Client。
- redirect_uri:可选。授权成功后的重定向地址。
- scope:可选。表示授权范围。
重点区别在于response_type为“token”,而不再是“code”,redirect_uri也变为了可选参数。
示例如下:
GET /authorize?response_type=token&client_id=1&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Foauth2&scope=user,photo HTTP/1.1
Host: server.example.com
Access Token Response
Authorization Server会返回如下典型的信息:
- access_token:访问令牌。
- refresh_token:刷新令牌。
- expires_in:过期时间。
示例如下:
HTTP/1.1 302 Found
Location: http://client.example.com/oauth2#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&expires_in=3600
注意其和Authorization Code的最大区别在于它是把token信息放在了url的hash部分(#后面),而不是作为参数(?后面)。这样浏览器在访问重定向的Location指定的url时,就不会把这些数据发送到服务器。而Client可以通过读取Location头信息中获取到access_token信息。
Resource Owner Password Credentials Grant
这种模式再一步简化,和Authorzation Code类型下重要的区分就是省略了Authorization Request和Authorization Response。而是Client直接使用Resource owner提供的username和password来直接请求access_token(直接发起Access Token Request然后返回Access Token Response信息)。这种模式一般适用于Resource server高度信任第三方Client的情况下。
客户端提供以下参数请求Authorization Server:
- grant_type:必选。值固定为“password”。
- username:必选。用户登陆名。
- passward:必选。用户登陆密码。
- scope:可选。表示授权范围。
示例如下:
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=blackheart&password=1234
Access Token Response和Authorization Code一致,就不列出来了。
Client Credentials Grant
这种类型就更简化了,Client直接以自己的名义而不是Resource owner的名义去要求访问Resource server的一些受保护资源。
客户端提供以下参数请求Authorization Server:
- grant_type:必选。值固定为“client_credentials”。
- scope:可选。表示授权范围。
示例如下:
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
OAuth2刷新令牌
在上述得到访问令牌(access_token)时,一般会提供一个过期时间和刷新令牌。以便在访问令牌过期失效的时候可以由客户端自动获取新的访问令牌,而不是让用户再次登陆授权。那么问题来了,是否可以把过期时间设置的无限大呢,答案是可以的。如下是刷新令牌的收客户端需要提供给Authorization Server的参数:
- grant_type:必选。固定值“refresh_token”。
- refresh_token:必选。客户端得到access_token的同时拿到的刷新令牌。
示例如下:
POST /token HTTP/1.1
Host: server.example.com
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
响应信息和 Access Token Response保持一致。
Token传递方式
在第三方Client拿到access_token后,如何发送给Resouce Server这个问题并没有在RFC6729种定义,而是作为一个单独的RFC6750来独立定义了。这里做以下简单的介绍,主要有三种方式如下:
- URI Query Parameter.
- Authorization Request Header Field.
- Form-Encoded Body Parameter.
URI Query Parameter
这种使用途径应该是最常见的一种方式,非常简单,比如:
GET /resource?access_token=mF_9.B5f-4.1JqM HTTP/1.1
Host: server.example.com
在我们请求受保护的资源的Url后面追加一个access_token的参数即可。另外还有一点要求,就是Client需要设置以下Request Header的Cache-Control:no-store,用来阻止access_token不会被Web中间件给log下来,属于安全防护方面的一个考虑。
Authorization Request Header Field
因为在HTTP应用层协议中,专门有定义一个授权使用的Request Header,所以也可以使用这种方式:
GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM
其中"Bearer "是固定的在access_token前面的头部信息。
Form-Encoded Body Parameter
使用Request Body这种方式,有一个额外的要求,就是Request Header的"Content-Type"必须是固定的“application/x-www-form-urlencoded”,此外还有一个限制就是不可以使用GET访问,这个好理解,毕竟GET请求是不能携带Request Body的。
POST /resource HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
access_token=mF_9.B5f-4.1JqM
OAuth2的安全问题
在OAuth2早期的时候爆发过不少相关的安全方面的漏洞,其实仔细分析后会发现大都都是没有严格遵循OAuth2的安全相关的指导造成的,相关的漏洞事件百度以下就有了。
其实OAuth2在设计之初是已经做了很多安全方面的考虑,并且在RFC6749中加入了一些安全方面的规范指导。比如
要求Authorization server进行有效的Client验证;
client_serect,access_token,refresh_token,code等敏感信息的安全存储(不得泄露给第三方)、传输通道的安全性(TSL的要求);
维持refresh_token和第三方应用的绑定,刷新失效机制;
维持Authorization Code和第三方应用的绑定,这也是state参数为什么是推荐的一点,以防止CSRF;
保证上述各种令牌信息的不可猜测行,以防止被猜测得到;
安全无小事,这方面是要靠各方面(开放平台,第三方开发者)共同防范的。如QQ互联的OAuth2 API中,state参数是强制必选的参数,授权接口是基于HTTPS的加密通道等;同时作为第三方开发者在使用消费这些服务的时候也应该遵循其相关的安全规范。