OAuth 2.0 协议
OAuth 是一个开发标准, 允许用户授权第三方网站或应用访问他们存储在另外的服务提供者上的信息, 而不需要将用户名和密码提供给第三方网站或分享他们数据的内容.
OAuth 2.0 不兼容 1.0.
协议的参与者
RO (resource owner): 资源所有者, 对资源具有授权能力的人.
RS (resource server): 资源服务器, 它存储资源, 并处理对资源的访问请求.
Client: 第三方应用, 它获得 RO 的授权后便可以去访问 RO 的资源.
AS (authorization server): 授权服务器, 它认证 RO 的身份, 为 RO 提供授权审批流程, 并最终颁发授权令牌(Access Token).
授权方式
在开放授权中, 第三方应用 (Client) 可能是一个 web 站点, 也可能是在浏览器中运行的一段 JavaScript 代码, 还可能是安装在本地的一个应用程序. 这些第三方应用都有各自的安全特性. 对于 Web 站点来说, 它与 RO 浏览器是分离的, 它可以自己保存协议中的敏感数据, 这些密钥可以不暴露给 RO; 对于 JavaScript 代码和本地安全的应用程序来说, 它本来就运行在 RO 的浏览器中, RO 是可以访问到 Client 在协议中的敏感数据.
OAuth2.0 为了支持这些不同类型的第三方应用, 提出了下面四种授权类型:
授权码 (Authorization Code Grant), 适用于有 server 端的应用授权.
隐式授权 (Implicit Grant), 适用于通过客户端访问的应用授权.
资源所有者密码凭证许可 (Resource Owner Password Credentials Grant),OAuth 简化版, 常用于移动应用认证, 称为 xAuth.
受保护资源的客户端授权 (Client Credentials Grant).
流程 | Response Type(第一次请求) | Grant Type(第二次请求) | 可带 Refresh Token | 说明 |
---|---|---|---|---|
授权码 | code | authorization_code | 是 | 常规流程 |
Implicit | token | - | 否 | 适用于纯 JS 程序 |
用户认证 | - | password | 是 | 客户端高度可信,且授权码流程不方便实施 |
客户端 | - | client_credentials | 否 | 客户端高度可信,拥有被操作资源(自用型),或操作非敏感资源 |
Authorization Code 授权
1. 授权场景
Authorization code 授权适用于 PC, 无线客户端等需要和第三方 server 进行交互的应用场景. 使用 Authorization code 授权, 第三方能够集中处理用户的授权请求和授权结果, 适用于有 server 端的应用.
2. 授权流程
Authorization code 授权模式分为两步, 首先获取 authorization code, 然后用 code 获取 acces token.
示意图:
- +----------+
- | 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 +---------------+
3. 过程详解
1, 获取 Authorization Code
请求参数
client_id 必须 分配给应用的 appid
redirect_uri 必须 授权回调地址, 必须和应用注册的地址一致
response_type 必须 授权类型, 此值固定为 "code"
state 必须 client 端的状态值. 用于第三方应用防止 CSRF 攻击, 成功授权后回调时会原样带回. 请务必严格按照流程检查用户与 state 参数状态的绑定.
scope 可选 授予权限范围
其他参数
如果用户成功授权, 则会跳转到指定的回调地址, 并在 redirect_uri 地址后带上 Authorization Code 和原始的 state 值
2, 通过 Authorization Code 获取 Access Token
请求参数
client_id 必须 分配给应用的 appid
grant_type 必须 授权类型, 此值为: authorization_code
client_secret 必须 分配给应用的 secret
state 必须 client 端的状态值. 用于第三方应用防止 CSRF 攻击, 成功授权后回调时会原样带回. 请务必严格按照流程检查用户与 state 参数状态的绑定.
redirect_uri 必须 与上面一步中传入的 redirect_uri 保持一致
code 必须 上一步返回的 Authorization Code 值(必须设定此 code 的有效时间)
返回参数
access_token 必须 授权令牌
expires_in 必须 该 access_token 的有效期
refresh_token 可选 在授权自动续期步骤中, 获取新的 Access_Token 时需要提供的参数
其他参数 可选
3,[可选] 权限自动续期, 获取 access_token
Access_token 一般需要根据应用特性设定有效期, 过期后需要用户重新授权或采用自动续期的方式.
请求参数
grant_type 必须 授权类型, 在本步骤中, 此值为 "refresh_token"
client_id 必须 分配给应用的 appid
refresh_token 必须 第二步返回的 refresh_token
client_secret 必须 分配给应用的 secret
如果授权成功, 则会返回和步骤二同样的结果.
Implicit 授权
1. 授权场景
Implicit 授权一般适用于没有 server 端的客户端应用, 由客户端发起授权请求, 保存和处理 access_token, 但有些应用 (如 Web 应用等) 为了提高用户体验, 简化授权过程, 也会常采用 Implicit 授权方式(注意, 这种授权方式没有返回 refresh_token.)
2. 授权流程
示意图:
- +----------+
- | 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) | |
- +---------+ +---------------+
交互图:
3. 过程详解
请求参数
response_type 必须 授权类型, 在本步骤中, 此值为 "token"
client_id 必须 分配给应用的 appid
redirect_uri 必须 授权回调地址, 必须和应用注册的地址一致
scope 可选 授予权限范围
state 必须 client 端的状态值. 用于第三方应用防止 CSRF 攻击, 成功授权后回调时会原样带回. 请务必严格按照流程检查用户与 state 参数状态的绑定.
其他参数 可选
如果成功授权, 则会跳转到 redirect_uri 指定的回调地址, 并带上 access_token,expires_in,state 以及与应用相关的参数. 注意, 这种授权方式没有返回 refresh_token.
Resource Owner Password Credentials
该授权方式获取 access token 一般只有一步, 类似如下 GET/POST 请求: https://open.xxx.com/oauth2/access_token?client_id=xxxx&client_secret=xxxxx&grant_type=password&username=xxx&password=xxx
OAuth2.0 新特性
服务器角色区分: 授权服务器和资源服务器
区别不同的用户类型, 授权场景和授权流程
将 token 分为频繁传输使用但是有效时长较短的 Access Token 和用于更新 Access Token 的 Refresh Token
定义多种 token, 降低资源请求的构造难度
授权过程不签名, 可根据需要采用 HTTPS 加密传输, 验证客户端密钥(通过签名), 客户端注册时预先指定 callback 地址等手段.
明确引入客户端注册流程: 确定 Client Type,callback URL 以及其它信息
引入可选的 state 参数, 帮助客户端防范 CSRF 和管理状态
引入 scope 参数, 可以进行授权范围控制
token type 等可被扩展: bearer(HTTPS), Mac(HTTP+sign), etc.
淘宝的 OAuth2.0 安全控制
只支持 HTTPS(bears), 不支持签名(签名是走以前老的授权方式)
授权过程指定的 callback URL 必须与注册的 callback URL 在同一个域名, 或者为 um:ietf:wg:OAuth:2.0:oob(表示只显示授权码, 不回调 callback URL)
接口分不同级别(R1, R2, W1, W2), 同一 Token 对不同级别接口有不同的有效期
接口权限划分(item, promotion, user, etc.)
说明 为何引入 authorization_code?
协议设计中, 为什么要使用 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 的扩展授权类型解决方案, 它在满足相同安全性的条件下, 使协议更简洁, 交互次数更少.
来源: http://www.bubuko.com/infodetail-2869727.html