本文是对OAuth标准的浅析。 OAuth标准定义了一种Web服务的授权框架,可以让Web服务的用户把自己的权限委托给第三方,让其帮自己执行某些操作。OAuth在RFC6749中定义,跟HTTP、TCP一样,是IETF Standard Track的RFC。

一个引子

我们来讲一个故事帮你理解OAuth到底是什么。

假设你的钱存在国内某个银行,然后你要出国,但是出国不能携带太多现金。于是你想委托一个亲朋好友,当你在国外需要用钱的时候,帮你从国内你自己的银行账户取钱,然后汇给你。最简单的做法,是你将银行卡以及密码都交给你委托的人。可是你有点担心,因为银行有说明,持有银行卡和密码的人对账户的操作,视为账户持有者本人的操作。你的账户里面有非常多钱,你无法完全相信自己所委托的那个人。这完全情有可原,毕竟一个人不可能相信别人超过相信自己。正好该银行提供有一项服务,可以让你签订一个委托取款协议。持有该协议的人,可以去银行从你的账户取协议里面约定的金额。于是你拉上你的亲朋好友,到银行签订了一个委托取款协议,然后你就安心出国了。

必须申明,上面这个例子是臆造的,因为你完全可以通过网上银行来进行跨境汇款操作。

银行提供了这项服务,它就必须保证用户能够安全地使用这项服务。所以银行制定了一些规定

  • 签订委托取款协议的时候,必须本人和受委托人一同前来,并且必须携带各自的身份证件
  • 委托协议的取款金额不能超过100万,并且委托协议的有效期不能超过6个月
  • 受委托人依据委托协议取款的时候,必须携带本人身份证件

可是,安全的对立面是易用性,上面的规定保证了一些安全性,同时也牺牲了一些易用性,比如:

  • 委托取款协议不能在ATM上签订,必须得跑到银行柜台排长长的号办理
  • 如果计划有变,想要的金额或者期限超过了协议的规定,就没有变通的办法了

云相册的例子

跟上面的例子同理,假设你把照片存在一个网盘应用里面(假设这个应用叫做云相册),然后有个第三方应用(假设叫做美颜APP),这个美颜APP可以帮你读取云相册里面的照片,并将你的自拍照片都用美颜滤镜处理一把。你不想把自己云相册的用户名和密码交给美颜APP,于是通过OAuth,你授权美颜APP,让它可以读取你自拍目录下的所有照片,帮你美颜处理。

更具体的过程是这样的,美颜APP给你一个它的Client ID(用来在云相册服务中标识它自己),然后你用自己的用户名密码登录云相册,并把美颜APP的Client ID,告诉云相册你愿意这个Client ID访问你的自拍目录下的照片,于是云相册给你颁布一个Token ID(也称为令牌,通常带有有效期),然后你把Token ID给到美颜APP,这样美颜APP就可以使用自己的Client ID和云相册颁布的Token ID来访问你云相册中自拍目录下的照片了。

所以,云相册颁布的Token ID实际上是一个委托,它包含以下信息:

  • 你在云相册中的ID
  • 美颜APP的Client ID
  • 访问范围,即自拍目录
  • 有效期限

总的来说,OAuth所定义的,就是如何安全的颁布一个Token ID给第三方应用,使这个应用可以访问你愿意它访问的内容。然而,有一点不包含在OAuth的职责里面,就是验证你与第三方APP的信任关系。也就是说,OAuth无法阻止你把令牌颁布给一个恶意的第三方APP。但是你永远拥有撤销第三方APP的访问权限的权利。

上面看似简单的过程中,其实包含大量会带来安全隐患的细节,OAuth需要对这些细节作出规定,从而避免这些安全隐患。

授权(Grant)过程

交互方式

OAuth是给Web服务做安全认证的,所以它是基于HTTPS的协议,也就是用HTTP消息来进行交互,用TLS来执行传输层加密。HTTP消息是Client到Server的非对称消息,所以当Client访问Server的URL的时候,会把请求的参数编码到URL中;然后返回的参数会以JSON各式编码在Server的响应消息中。

相关名词

下面的名词都是抽象的概念:

  • Third-party application:第三方应用,也即是上面例子中的美颜APP。
  • Resource Owner:资源所有者,也就是用户。
  • User Agent:浏览器的泛称。
  • Authorization server:认证服务器,即上面例子中云相册提供委托认证的服务器。
  • Resource server:资源服务器,即上面例子中云相册保存相片的服务器。

四种授权方式

OAuth共有四种授权方式: authorization code、implicit、resource owner password credentials、client credentials。

真正推荐使用的只有authorization code方式,所以下面主要这种授权方式进行讲解。

authorization code授权方式

采用authorization code方式的授权,在颁布Token ID之前,有一个中间步骤,用来获取authorization code。以上面云相册的例子做说明,用户在云相册中授权完美颜APP的时候,会获得一个authorization code。然后用户把authorization code转给美颜APP,接着美颜APP通过authorization code自己向云相册申请获得最终的Token ID。

来自RFC6749中的图:

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

首先解释下上图中的User Agent,也就是浏览器。当美颜APP需要获得用户在云相册的授权的时候,它需要跳转到云相册的授权页面,让用户登录。作为负责任的第三方APP,美颜APP必须启动系统自带的浏览器,而不是自己内嵌的浏览器。因为如果使用自己内嵌的浏览器,第三方APP有可能偷窃到用户的密码。

可能是出于类似的安全原因。苹果在iOS上禁止任何APP内嵌浏览器,而只能调用iOS提供的Safari嵌入版。所以不管iOS上的Chrome、Firefox还是Edge,内嵌的都是Safari。

当用户在系统浏览器中完成授权的时候,必须有办法能够跳转到美颜APP,这是通过一个Redirect URL实现的。对于Web App来说,这个URL可以是http开头的地址;对于本地的APP来说,这个URL可以系统支持的一个Custom URL Schema,比如my-app://

当美颜APP调用系统浏览器来获取授权的时候(也就是上图中的(A)步骤),它必须提供以下信息:

  • Client ID,前面介绍过了,美颜APP在云相册中注册的ID
  • Redirect URL,当授权完成之后,能够通过这个URL返回美颜APP,通常也要求在云相册中注册
  • Scope,授权范围。这是一个可选参数,云相册这个例子里面,这个scope可以用来指定把授权范围限定在自拍相册
  • State,美颜APP生成的nounce,会在授权结束完原样返回给美颜APP。一个负责任的第三方APP应该使用随机的一次性的值作为state,防止自己的身份被盗用。

一个来自OAuth 2 Simplified的例子,征求facebook的授权:

https://facebook.com/dialog/oauth?response_type=code&client_id=CLIENT_ID
  &redirect_uri=REDIRECT_URI&scope=email&state=1234zyx

上面的例子中参数都编码到了URL中了:

  • response_type=code,表示使用authorization code授权方式
  • client_id=CLIENT_ID,CLIENT_ID代表请求的APP的Client ID
  • redirect_uri=REDIRECT_URI,redirect_uri即是redirect_url,假设是fb00000000://authorize
  • scope=email,授权范围是用户的email
  • state=1234zyx,随机的一次性的nounce

在上图流程图中的(B)步骤中用户输入用户名和密码登录;在上面的(C)步骤中,资源服务器返回一个Authorization Code和之前的state参数,然后调用REDIRECT_URI,大概是下面这样子:

fb00000000://authorize?code=AUTHORIZATION_CODE&state=1234zyx

系统能够识别fb00000000://这个Custom URL Scheme,转入第三方APP,并且把Authorization Code和state传给第三方APP。一个负责任的第三方APP必须验证state是自己之前发送的state。然后这个获得的Authorization Code是有时效性的,第三方APP必须尽快去换取最后的Token ID。也就是上面流程图中的(D)步骤和(E)步骤。(D)步骤所需的参数:

  • grant_type=authorization_code,固定格式
  • code=AUTH_CODE_HERE,获取到的Authorization Code
  • redirect_uri=REDIRECT_URI,跟之前发送的redirect_uri一致
  • client_id=CLIENT_ID,跟之前发送Client ID一致

至此,授权流程就结束了。

其他授权方式

  • implicit授权方式,跟authorization code类似,不过省了获取authorization code的步骤,现在差不多完全被authorization code方式替代了。
  • resource owner password credentials授权方式,需要完全信任第三方APP,并提供用户名密码给第三方APP,使用场景较少。
  • client credentials授权方式,其实没有用户什么事。假设美颜APP和云相册是同一家公司的产品,云相册本来就信任美颜APP,所以美颜APP只要给云相册自己的Client ID就可以获得授权了,不需要征得用户的授权。

参考资源