• 认证: 完成对用户身份的确认,验证当前自称为某种身份的用户,确实是所声称的用户
  • 授权: 一般指对特定身份的用户授予访问数字化资源的权限

OAuth 2.0 是一个关于授权的公开协议。支持用户让第三方应用访问该用户在某一网站上存储的私密资源,而无需将用户名和密码提供给第三方应用。但生产实践中,“OAuth 认证” 的表述无处不在,迫于困惑,专门整理了下 OAuth 与 OpenID 的异同。

OAuth

在互联网的“石器时代”,两个服务间共享资源的方案非常简单。用户为服务 B 提供他在服务 A 的账号密码,服务 B 通过模拟认证完成登录并抓取它想要的任何信息。但是,“任何时候都不要泄露你的账号密码”,你无法确保服务 B 会妥善保管你的账号密码,无法确保它不会抓取远超它实际所需的信息… 幸好,OAuth 让我们无需主动泄露账号密码。

无需账号密码,但总需要一个凭证。OAuth 提供了两种模式来解决凭证的生成与转交。

授权码模式

     +----------+
     | 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)
  • 资源所有者(Resource Owner): 指用户,参与完成主动授权的操作
  • 客户端(Client): 实指第三方服务 B ,想要在用户允许后抓取在服务 A 的信息
  • 授权服务器(Authorization Server): 授权服务器,即服务提供商 A 专门用来处理授权的服务器
  • 用户代理(User Agent): 指浏览器

A. 第三方服务 B 通过重定向到服务 A 的授权服务器,要求用户授权服务 B 抓取服务 A 的相关信息(通常,这个重定向还将包含服务 B 申请的授权范围、用户确认后的重定向地址)。 B. 授权服务器确认用户同意授权。 C. 授权服务器将携带授权码(Authorization Code) 重定向到预设的服务 B 的地址。 D. 服务 B 接收到授权码并基于授权码向服务 A 的授权服务器兑换访问令牌(Access Token) E. 服务 B 收到访问令牌

此后服务 B 就像是完成认证的用户一样,可以进行授权范围内的所有动作。

隐式授权(Implicit Grant)

     +----------+
     | 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 |
     |         |
     +---------+

隐式授权为没有 Server 端的服务授权提供了可能。在用户确认授权后,授权服务器直接提供加密后的访问令牌,用户代理通过请求资源服务器下载一段解码的 JavaScript 脚本在执行后得到最终的访问令牌。相对于授权码模式服务器之间的访问令牌交互,隐式授权会存在更大的安全隐患,但也是一种业务上的妥协。

OpenID

OpenID 是一个去中心化的身份认证系统。对于支持 OpenID 的网站,用户不需要记住像用户名和密码这样的传统验证标记。取而代之的是,他们只需要预先在一个作为 OpenID 身份提供者(identity provider, IdP) 的网站上注册。通过将登录导向这个 IdP 服务,完成认证流程。从流程上,OpenID 与 OAuth 有大量相似的地方,区别在于 OpenID 只是在证明自己是 IdP 上某身份的拥有者,而 OAuth 却赋予了网站随意操作自己私有数据的权利。

OIDC

OAuth 提供的机制,始终只是在确保不提供账号密码的前提下,为服务 B 提供访问用户存储在服务 A 上信息的许可;以及服务 B 代理用户抓取其在服务 A 上的信息。至于如何完成用户在服务 B 上的认证,这是 OAuth 所不包含的。

认证的核心是完成对用户身份的确认。通过 OAuth 从服务 A 获取的用户信息能够确认用户身份吗?并不一定。用户名存在修改的可能性;手机号可能有重复或随意填写;邮箱也许没有被校验过…从 OAuth 抓取过来的用户信息,仅仅只能作为这个用户在网络社区上的一些标签,随时可能更改,也存在伪造的可能。但是,换句话说,如果服务 A 能提供一个用户无法伪造,具有唯一识别意义的标志,其实认证也能架构在 OAuth 上进行实现。

OIDC (OpenId Connect) 就是一个基于 OAuth 2.0 协议的身份认证标准协议。用授权的方式证明自己身份。拿 OAuth 2.0 的授权码模式举例,OIDC 在首次请求中增加了对 OPENID 资源的授权申请,在利用授权码换取访问令牌时,额外地得到了 ID Token (其中就包含 Open ID 信息)

     +----------+
     | Resource |
     |   Owner  |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier                    +---------------+
     |         -+----(A)-- & Redirection URI (scope=OPENID)---> |               |
     |  User-   |                                               | Authorization |
     |  Agent  -+----(B)------- User authenticates -----------> |     Server    |
     |          |                                               |               |
     |         -+----(C)------- Authorization Code -----------< |               |
     +-|----|---+                                               +---------------+
       |    |                                                       ^      v
      (A)  (C)                                                      |      |
       |    |                                                       |      |
       ^    v                                                       |      |
     +---------+                                                    |      |
     |         |>---(D)-- Authorization Code -----------------------'      |
     |  Client |          & Redirection URI                                |
     |         |                                                           |
     |         |<---(E)----- Access Token ---------------------------------'
     +---------+              & ID Token

总结

OAuth 作为授权协议,本不包含认证。但通过授权访问能够证明身份的信息(Like OpenID),也能够实现认证。从这个角度看,“OAuth 认证” 的口语化表述并无不妥。

不过,从实践的角度看,使用 OAuth 来实现认证,和非基于授权的认证,还是有所不同的。就好比,收件时为了证明收件人的身份,本来只要给快递小哥看一眼身份证,结果你却授权快递小哥随时随地查验你的身份证(不管是否在取件)。