设计理念
根据 wikipedia:
开放授权(OAuth)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
OAuth 2.0 is a protocol that allows distinct parties to share information and resources in a secure and reliable manner.
解决的问题
OAuth 2.0 解决两个问题:
- 联合身份(federated identity):允许用户使用另外一个服务的帐号登录此服务。比如用微信登录京东,用 Google 登录 Medium。这使得用户只需要维护一个帐号
- 授权(delegated authority):授权一个服务以用户的身份去访问它在另外一个服务中的信息。比如你可以授权 LinkedIn 帐户访问你的 Google 联系人这种非公开的信息
假如在有 OAuth 2 之前,应用 GoodApp 想访问你的 Facebook 联系人列表,那它不得不向你询问你的 Facebook 帐户的用户名密码。这样引起的问题是:
- 你给了 GoodApp 对你的 Facebook 帐户做任何事情的能力
- GoodApp 会储存你的密码,并且它可能由于被黑导致你的密码泄露
- 除非你修改你的 Facebook 密码,否则你没有办法收回这份授权
有了 OAuth 2 之后:
- 你不再需要给 GoodApp 你的 Facebook 密码,而是通过 Facebook 授权给一个 token 到 GoodApp
- 你可以限定 GoodApp 能访问你的哪些 Facebook 数据,比如只能读取好友列表
- 你可以随时在 Facebook 这边收回对 GoodApp 的授权
更通俗的理解是:用户的密码是全权限的 token,token 是只有部分权限的密码。
Components
理解 OAuth 的具体流程之前,先理解下它的几个组成部分:
- Resource Owner:资源的所有者,一般指用户;比如某个用户它拥有它的 Facebook 好友列表(资源)
- User Agent:用户的设备;比如浏览器、手机 native app
- Client:想要使用用户的资源的客户端或服务;比如上述例子中的 GoodApp,想获得用户的 Facebook 好友列表
- Authorization Server (AS):向 client 发放 access token,使得 client 可以访问具体资源服务器
- Resource Server (RS):即资源所在的服务。client 通过 AS 发放的 access token,在此处可以使用实际的资源
Authorization server 及 resource server 可能实现在同个域名也可能分开,但是它们背后的服务商一般是一样的,比如同样属于 Facebook。
Flow
OAuth 2 提供了 4 种授权类型(authorization grant type):
- Authorization code grant
- Implicit grant
- Resource owner password credentials grant
- Client credentials grant
第一种 authorization code grant 是最常见的,其次是 implicit grant。剩余的两种不做讨论。对于前两种方式,核心的点是:
- Client 预先在 AS 上注册了它的服务;AS 会给 client 一个 client ID 来标识它
- Client 最终要通过 AS 获得一个 access token,才能 RS;而 AS 需要用户允许才给 access token
Authorization code grant
流程如下:
绝大多数互联网产品都采用这种流程,比如 Google、Twitter、GitHub。看这个 GitHub 的 例子 来深入理解这套流程。
这里演示一个基于 GitHub 的 OAuth 应用,在获取用户授权后显示用户所有仓库信息的例子。GitHub 的 官方文档 对这个过程有详细地说明。我做了一个 YouTube 视频来描述这个过程:
代码及预编译好的多平台可执行文件在 这里。
仅当存在 client 且它可以安全地保存信息时(包含 client secret 及 access token),才应该使用 authorization code grant 方式。假如它无法安全保存信息:
- client secret 被盗会导致黑客可以以此应用身份欺骗用户
- access token 被盗则黑客可以以用户身份做任何操作
怎样界定有无安全存放信息的条件?
- 如果你的 client 是纯 JS 应用,没有后台服务器,那它被认定为不满足。因为 JS 的 Storage API 和 cookie 都是不安全的,可以被外部读取的
- 如果你的 client 包含一个后台服务,一般被认为是满足的。后台可以通过保护数据库和使用各种加密方式来保证数据安全
- 如果你是在使用某个手机客户端(比如 GitHub 第三方客户端来访问 GitHub),这个客户端如果用了系统提供的安全能力(比如 Android),则可以认定为是满足的
如果不满足的话,需要走 implicit grant type 流程。
Implicit grant type
流程如下:
对比于 authorization code grant,这个流程的特点在于少了 client application,因此也没有哪一环可以安全地存放用户的 access token,所以 access token 直接给到了 user agent,并且一般会设置一个比较短的(如 1 小时)的有效时间。User agent 拿到 access token 后直接使用。
这种流程在互联网服务中比较少见,用户体验也会相对较差。