# OAuth
:一个实现 SSO 的协议。
- 允许用户通过平台 A 的账号登录第三方应用,也允许第三方应用获取用户在平台 A 上的某些资源。
- 常用的版本是 v2.0 ,它不兼容 v1.0 。
# 架构
角色划分:
- 用户:要通过平台 A 的身份认证登录 app 。
- 第三方应用:简称为 app 。
- 资源服务器:存储着用户在平台 A 上的资源。
- 授权服务器:完成身份认证之后,返回一个 token(访问令牌)给 app ,允许 app 拿着这个 token 发出 HTTP 请求到资源服务器,获取用户在平台 A 上的某些资源。
- 平台 A 的资源服务器、授权服务器可能是同一台服务器。
# 工作模式
- 授权码模式
- :app 先向授权服务器申请 code(授权码),拼接出申请 token 的登录 URL 。
- 由平台 A 完成统一的身份认证,避免了用户在每个网站上创建账号的麻烦。
- 由平台 A 负责实际的身份认证,第三方应用不会知道用户的真实密码,因此这种模式最安全。
- 简化模式
- :app 直接向授权服务器申请 token 。当用户从授权服务器跳转回来时,token 会被放在 URL 中,容易泄露。
- 适用于 app 只有网页,没有后端服务器的情况,比如手机应用。
- 密码模式
- :用户将自己的账号密码告诉 app ,让 app 拿着账号密码去获取 token 。这样 app 有权访问用户的所有资源。
- 客户端模式
- :授权服务器收到 app 的请求时直接返回 token ,不需要身份认证。
# 授权码模式
工作流程示例:
- 用户在 app 的网页上点击 “以其它方式登录” 的链接。
- app 构造出平台 A 的 OAuth 登录 URL ,让用户 302 重定向到它(采用 GET 请求)。
- app 要在登录 URL 的请求字符串中加入以下参数:
client_id # app 在平台 A 上注册的 id redirect_uri # app 的回调 URL response_type # 表示希望服务器返回的值类型。必须设置为 "code" scope # 表示 app 请求授权的范围 state # 一个针对该请求的随机数,用于防止 CSRF 攻击。
- 例如:
…….com/oauth/authorize?client_id=……&redirect_uri=……&response_type=code
- app 需要事先平台 A 上注册,设置自己的 redirect_uri ,被分配 client_id、client_secret 。
- app 要在登录 URL 的请求字符串中加入以下参数:
- 用户在平台 A 的页面上,同意授权给 app 。
- 平台 A 要验证用户的身份(可能要让用户通过账号密码或其它方式完成登录),然后验证登录 URL 中是否包含了必须参数、这些参数是否有效,最后循环用户是否同意授权给 app 。
- 如果授权失败或出错,平台 A 应该在自己的网页上告诉用户。
- 平台 A 构造出 app 的 redirect_uri ,让用户 302 重定向到它(采用 GET 请求)。
- 平台 A 要在 redirect_uri 的请求字符串中加入以下参数:
code # 一个根据 client_id 和 redirect_uri 生成的随机值 state # 原样返回 app 的 state
- code 应该在几分钟之内过期,减少泄漏的风险。
- code 是一次性的,如果有 app 拿着用过的 code 来请求平台 A 发放 token ,平台 A 应该拒绝该请求,并撤销基于该 code 发放的 token 。
- app 应该检查该 state 参数是否与自己发送的相同(可以事先保存在用户的 session 中)。
- 平台 A 要在 redirect_uri 的请求字符串中加入以下参数:
- 当用户重定向回到 app 时,app 从 redirect_uri 中解析出 code ,向平台 A 请求 token 。
- app 的后端服务器发出 POST 报文到平台 A ,请求获取 token 。报文 body 中应该采用 application/x-www-form-urlencoded 格式,包含以下参数:
client_id client_secret redirect_uri code # 填入 app 从 redirect_uri 中解析出的 code grant_type # 表示授权模式。必须设置为 "authorization_code"
- 平台 A 的响应报文 body 中包含以下参数:
access_token # 访问令牌 expires_in # token 的过期时间 refresh_token # 当 access_token 过期之后,用 refresh_token 来请求新的 access_token token_type # token 的类型,通常为 “bearer” scope # token 的授权范围
- 如果 refresh_token 也过期了,就需要用户重新授权。
- 有的平台只有 access_token ,没有 refresh_token 。
- app 的后端服务器发出 POST 报文到平台 A ,请求获取 token 。报文 body 中应该采用 application/x-www-form-urlencoded 格式,包含以下参数:
- app 收到 token ,拿着它向平台 A 请求用户的身份信息(比如用户名)。
- app 拥有 token 之后,就有权限调用平台 A 的一些 API 了。
- app 同意用户登录,将用户重定向到自己的业务页面。