OAuth验证方法

前些日子试用twittering-mode,希望可以和twip一起搭配工作,可惜自己胡乱折腾发觉不行,无奈把twitter的oauth验证和API相关的东西捋了一遍,终于能够工作了,这里做个小结。

由于越来越多的第三方应用涌现,出于对用户账户的安全考虑,今年8月31日twitter宣布关闭basic验证,仅支持oauth验证。目前主流的oauth版本为1.0a,并且产生了rfc5849文档,现在已经成为了各大公司向第三方应用授权的标准方法。

oauth的使用场景很简单,它涉及到三个对象——用户C、服务S和第三方应用A,以及受保护的数据D:

用户C有受保护的数据D保存在服务S上,现在第三方应用A需要访问服务S上的数据D。

是不是想起以前遇到这样的情况是咋样的?比如,你注册了人人网(应用A),然后人人网要你的邮箱(服务S)联系人数据(受保护的数据D),居然会要求你输入该邮箱的用户名和密码,此时你心里是不是会咯噔一下,觉得有些不妥呢?如果服务S上部署了oauth验证方法,那么就可以很容易的解决这种授权访问问题。

各种介绍oauth使用的文档很多,那么它有什么优势呢?

  • 用户可以授权给第三方应用B访问服务A,且不用暴露用户名密码
  • 授权可以随时被收回
  • 可以抵御信息窃听
  • 可以抵御中间人攻击

目前常用的oauth流程、API以及相关字段可以用下图表示:

OAUTH验证流程图

下面使用rfc5849上的一个例子来实战一下oauth的流程:

Jane(用户C)最近上传了一些她的私人假期照片(受保护的数据D)到她的照片分享网站 ‘photos.example.net’(服务S)。她希望用 ‘printer.example.com’ 网站(第三方应用)来打印其中一张照片。和通常一样,Jane用她的用户名和密码登录到 ‘photos.example.net’。

然而,Jane不想让 ‘printer.example.com’ 知道她的用户名和密码,但该应用又必须访问它的照片才能打印 -_-!!。为了给用户提供更好的服务,’printer.example.com’ 已经事先取得了一组 ‘photos.example.net’ 的客户端凭证(credentials):

Client Identifier(第三方应用标识):
    dpf43f3p2l4k3l03

Client Shared-Secret(第三方应用共享密钥):
    kd94hf93k423kf4

同时,’printer.example.com’ 网站遵循 ‘photos.example.net’ 的API文档所规定协议接口通讯,这些接口使用”HMAC-SHA1”签名加密。一般有如下接口,都是URL地址:

Temporary Credential Request(临时凭证请求):
    https://photos.example.net/initiate

Resource Owner Authorization URI(资源所有者的验证地址):
    https://photos.example.net/authorize

Token Request URI(Token请求地址):
    https://photos.example.net/token

在 ‘printer.example.com’ 能请求Jane授权它访问照片之前,它必须先跟 ‘photos.example.net’ 建立一组临时凭证以用来识别接下来的请求。因此,第三方应用会发送如下的HTTPS请求到服务端的Temporary Credential Request地址:

POST /initiate HTTP/1.1
Host: photos.example.net
Authorization: OAuth realm="Photos",
   oauth_consumer_key="dpf43f3p2l4k3l03",
   oauth_signature_method="HMAC-SHA1",
   oauth_timestamp="137131200",
   oauth_nonce="wIjqoS",
   oauth_callback="http%3A%2F%2Fprinter.example.com%2Fready",
   oauth_signature="74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D"

服务端验证这个请求并在HTTP应答的正文里面回复一组临时凭证:

HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded

oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03&oauth_callback_confirmed=true

第三方应用转发Jane的user-agent到服务端的Resource Owner Authorization URI,以便Jane对其访问照片请求的批准:

https://photos.example.net/authorize?oauth_token=hh5s93j4hdidpola

服务端会要求Jane用她在 ‘photos.example.net’ 的用户名和密码登录。如果登录成功,服务器再要求她批准授权 ‘printer.example.com’ 访问她的私有照片。Jane批准该请求后,她的user-agent会跳转到第三方应用在之前请求里提供的callback地址:

http://printer.example.com/ready?oauth_token=hh5s93j4hdidpola&oauth_verifier=hfdp7dh39dks988

该callback请求通知第三方应用 Jane 已经完成了授权过程。然后,第三方应用使用它的临时凭证来请求另一组token凭证(由于token凭证拥有最终的访问用户受保护数据的权限,此时的请求必须是加密通道):

POST /token HTTP/1.1
Host: photos.example.net
Authorization: OAuth realm="Photos",
   oauth_consumer_key="dpf43f3p2l4k3l03",
   oauth_token="hh5s93j4hdidpola",
   oauth_signature_method="HMAC-SHA1",
   oauth_timestamp="137131201",
   oauth_nonce="walatlh",
   oauth_verifier="hfdp7dh39dks9884",
   oauth_signature="gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D"

服务器验证这个请求并在HTTP应答的正文里面回复一组token凭证:

HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded

oauth_token=nnch734d00sl2jdk&oauth_token_secret=pfkkdhi9sl3r4s00

有了这组token凭证,第三方应用现在就可以访问用户的受保护数据了:

GET /photos?file=vacation.jpg&size=original HTTP/1.1
Host: photos.example.net
Authorization: OAuth realm="Photos",
   oauth_consumer_key="dpf43f3p2l4k3l03",
   oauth_token="nnch734d00sl2jdk",
   oauth_signature_method="HMAC-SHA1",
   oauth_timestamp="137131202",
   oauth_nonce="chapoH",
   oauth_signature="MdpQcU8iPSUjWoN%2FUDMsK2sui9I%3D"

‘photos.example.net’ 服务端验证这个请求,并回复要求访问的照片。在Jane的授权生效期间,’printer.example.com’都可以使用这组token凭证来访问Jane的私密照片,除非Jane取消该授权。