OAuth1.0协议
概要
OAuth提供了一种client代表资源的拥有者访问server的方法,也就是在资源拥有者不向第三方提供证书(通常是指用户名和密码)的情况下,允许第三方使用用户代理重定向访问服务器上的资源。
1 介绍
OAuth协议最初是设计用于解决一个最普遍的问题,如何代理访问受保护的资源,,于是在2007年10月建立OAuth1.0的一些标准。在2009年6月重新修订和发布(http://oauth.net/core/1.0a/)。
在传统的client-server的授权模式中,client使用自己的证书去访问在server的资源,伴随着分布式web服务和云计算的增长,第三方应用也需要访问这些服务器上的资源。
OAuth把第三个角色加入到了传统的client-server的模型之中:资源拥有者。在OAuth模型中,client(不是资源拥有者,但 是却代表他做一些事情,请求访问一些资源拥有者管理的资源)也就是说,OAuth不仅能够确认证实资源拥有者得授权认证,并且还能识别出提出请求的客户端 的身份。
OAuth为客户端代表资源拥有者(另一个client或者最终用户)访问服务器资源提供了解决方案,他也提供了一种流程使得终端用户可以在不需要共享认证(账户和密码)的情况下使用用户代理重定向的方式授权第三方应用访问他们在服务器的资源。
比如,一个Web用户(资源拥有者)可以授权一个打印服务去访问他存储在照片分享服务上面的私人照片,但是却无需为打印服务提供其在照片服务上的账户和密码。他只需要直接认证授权打印服务的代理请求的认证即可。
为了让客户端去访问资源,我们首先必须得到资源拥有者的许可,这种许可事实上就是一个token和相对应的公钥,token的目的就是让资源拥有者 无需向客户端提供账户和密码。不像资源拥有者的证书(账户和密码),token在分配的时候有受限的范围以及有限的生命周期,并且可以被回收。
该规范说明包括两部分,第一部分定义了通过重定向使得终端用户授权给客户端去访问服务器资源的流程,第二部分是则定义了如何利用两个证书(一个用于识别客户端生成的请求,一个用于识别这次请求所代表的资源拥有者)来生成已认证的Http请求。
1.1 术语
client:一个能够产生OAuth 认证请求的Http Client。
Server:一个能够接受OAuth认证请求的Http Server。
受保护的资源:一个具有访问受限的资源,只有在通过了server的认证后才能访问。
资源拥有者:一个能够通过证书访问资源的实体。
证书:一对唯一的标识和相对应的公钥。OAuth定义了三类证书:客户端,临时的,以及令牌的,分别用于识别和认证客户端发起的请求,授权请求,代理访问请求。
令牌:服务器下发的一个唯一的标识,用于将客户端把资源拥有者(客户端已经向之请求权限或者已经获得权限)和已授权的请求联系起来。令牌有一个与之对应的公钥,用来建立客户端和令牌的拥有关系。
1.2 示例
Jane(资源拥有者)最近上传了一些私人的假期照片(受保护资源)到照片分享站点photos.example.net(server),她现在 想通过printer.example.com站(client)打印这些照片中间的一张,jane利用账号和密码登入了 photos.example.net。
不管怎样,jane都不想提供用户和密码给printer.example.com,为了提供更好的服务给用户,printer.example.com提前向photos.example.net注册了一组client证书。
Client标识:
dpf43f3p2l4k3l03
Client公钥:
d94hf93k423kf44
printer.example.com必须按照photos.example.net给出的API的文档中列出的协议来配置自己的应用。
临时证书请求:
https://photos.example.net/initiate
资源拥有者授权URI:
https://photos.example.net/authorize
令牌请求URI:
https://photos.example.net/token
在printer.example.com要求jane授权他访问照片之前,受限必须向photos.example.net请求一组临时证书以便server能够识别代理请求,为此,客户端必须向服务器发起如下https请求:
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"
server验证了请求,并且将一组临时证书放在http response的body中返回给客户端
HTTP/1.1 200 OK
Content-Type:application/x-www-form-urlencoded
oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03&
oauth_callback_confirmed=true
这个客户端重定向jane的请求到服务器的资源认证页面获取jane允许其访问私人照片的许可。
https://photos.example.net/authorize?oauth_token=hh5s93j4hdidpola
这个页面需要jane利用自己的账户名和密码进行登录,如果成功了,则进一步要求她允许受权给printer.example.com访问,jane允许了请求并且将他的页面重定向回客户端在之前的请求中提供的callback页面。
http://printer.example.com/ready?oauth_token=hh5s93j4hdidpola&oauth_verifier=hfdp7dh39dks9884
这个callback请求通知客户端jane已经完成了受权过程,然后客户端使用临时证书发起请求一组令牌证书(TSL):
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"
服务器验证此请求,并且将一组token放入了http response的body中返回给客户端。
HTTP/1.1 200 OK
Content-Type:application/x-www-form-urlencoded
oauth_token=nnch734d00sl2jdk&oauth_token_secret=pfkkdhi9sl3r4s00
拿到这组令牌证书以后,客户端现在可以着手请求私有照片了。
GET /photos?file=vacation.jpg&size=originalHTTP/1.1
Host: photos.example.net
Authorization: OAuthrealm="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服务器验证这个请求,并且返回照片的response,直到janse回收访问的授权之前printer.example.com都能够利用这组令牌证书继续方位jane的私人照片。
2 基于重定向的授权
OAuth利用令牌来代表资源拥有者授权给客户端的权限。典型的,令牌证书是服务器在认证了资源拥有者标识之后发放给客户端的。
服务器返还token的方式有许多种,这一节采用利用http重定向和资源拥有者的用户代理的方式,这种基于重定向的授权方法主要包括三个步骤:
1. 客户端从服务器获取了一组临时证书,这组证书主要用于在授权过程中识别访问请求。
2. 资源使用者授权服务器授予客户端的访问请求。
3. 客户端利用临时证书从服务器获取一组令牌证书,通过这组令牌证书,客户端能够访问资源访问者的受保护资源。
如果一般临时证书被用来获取令牌证书以后,服务器对临时证书必须予以回收,这里建议临时证书有一定的生命周期,服务器应该保证令牌证书在下发给客户端以后资源拥有者能够收回。
为了确保这些步骤能够被客户端执行,服务器必须提供以下三个URL的接口:
l 临时证书请求:
这个接口主要让客户端用来获取一组临时证书。
l 资源拥有者授权:
这个接口用于重定向资源拥有者,让其能够授予客户端访问其资源的权限。
l 令牌请求:
通过这个接口,客户端能够通过一组临时证书来向服务器请求一组令牌证书。
建议服务器以查询组件的形式提供这三个接口,但是要尽量避免这个接口本身包含oauth_前缀的参数,这样避免与oauth本身的参数发生冲突。
服务器和客户端应该保证这些值不被预料和猜想。
2.1 临时证书
客户端通过向服务器暴露的临时证书请求地址发出POST请求,获取一组临时证书。客户端需要构造如下参数:
oauth_callback:这个地址必须是一个绝对的URL路径,在资源拥有者经过第二部授权以后,服务器将会重新定向到这个地址。如果客户端如果收到这个重定向,或者这个地址被用作别的意图,anemia这个参数可能被设置为“oob”(out of band)。
另外,服务器可以添加额外的参数。
当构造请求的时候,客户端只需要使用客户端证书即可。客户端忽略auth_token”参数。
当客户端选择不加密的时候,那么服务器必选选择一种类似于TSL或者SSl的传输层机制。
如,客户端构造如下的HTTPS的请求:
POST /request_temp_credentials HTTP/1.1
Host: server.example.com
Authorization: OAuth realm="Example",
oauth_consumer_key="jd83jd92dhsh93js",
oauth_signature_method="PLAINTEXT",
oauth_callback="http%3A%2F%2Fclient.example.net%2Fcb%3Fx%3D1",
oauth_signature="ja893SD9%26"
服务器必须验证这个请求是否合法,如果验证成功,则返回一组临时证书,这组临时证书通过状态200的application/x-www-form-urlencoded的content-type返回。这个response中包括如下参数:
oauth-token:
临时证书标识。
oauth-token-secret:
临时证书公钥。
oauth-callback-confirmed:
必须设置并且设置true,这个参数主要用于区分前一个版本的协议。
虽然这个参数的参数名包含token,但是这个并不是令牌证书。
响应如下:
HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded
oauth_token=hdk48Djdsa&oauth_token_secret=xyz4992k83j47x0b&
oauth_callback_confirmed=true
2.2 资源拥有者授权
在客户端请求一组令牌证书之前,用户首先必须通过授权,客户端必须将利用下列参数添加到服务器的用户资源授权的URL接口中发起一次http的请求。
oauth_token:
其实就是第一步中的服务器返回的oauth_token参数,服务器声明这个参数是可选的。在这种情况,他们必须提供另外一种方式让资源拥有者能够提供这个标识。
服务器可以添加另外的参数。
客户端将资源拥有者利用页面重定向引导到构造好的URI中,这个请求必须使用GET的请求方式。
举例,客户端重定向到一切的HTTPS的请求:
GET/authorize_access?oauth_token=hdk48Djdsa HTTP/1.1
Host: server.example.com
在这个页面,服务器需要验证资源拥有者的标识ID。(通常采用账户和密码的方式)
当询问资源拥有者是否授权允许访问的时候,服务器必须呈现给资源拥有者当前客户端正在请求访问资源。
当收到来自资源拥有者得授权决定之后,服务器将资源拥有者重定向到callback的页面。
为了确保授权访问的资源拥有者和返回给客户端的资源拥有者是同一个人,callback URI中必须包含一个校验码:一个无法破解和猜测的值。在callback的URI中必须包含以下参数:
oauth_token:临时证书的标识。
oauth_verifier:校验码。
如果这个URL已经包含了查询参数,那么服务器必须将OAuth的参数追加到已经存在的查询参数之后。如:
GET/cb?x=1&oauth_token=hdk48Djdsa&oauth_verifier=473f82d3 HTTP/1.1
Host: client.example.net
如果客户端没有提供这个callback的URI,那么服务器必选显示这个效验码,然后引导资源拥有者手动的通知客户端授权过程已经完成,如果服务器知道客户端只能运行在首先得设备上,那么必须确保效验码适合手工。
2.3 令牌证书
客户端通过向服务器暴露的令牌请求的URI发起一个授权的HTTP POST请求获取一组令牌证书,客户端必须将下列参数传递给服务器:
oauth_verifier:
校验码。
举例,客户端构造如下的http请求:
POST/request_token HTTP/1.1
Host:server.example.com
Authorization:OAuth realm="Example",
oauth_consumer_key="jd83jd92dhsh93js",
oauth_token="hdk48Djdsa",
oauth_signature_method="PLAINTEXT",
oauth_verifier="473f82d3",
oauth_signature="ja893SD9%26xyz4992k83j47x0b"
而响应则包括如下返回参数:
oauth_token:
令牌标识。
oauth_token_secret:
令牌公钥。
如:
HTTP/1.1200 OK
Content-Type: application/x-www-form-urlencoded
服务器保留范围,生命周期,以及其他属性能够受资源拥有者的控制。
一旦客户端收到令牌证书并保存下来,他并能够利用这个令牌证书代表资源拥有者来访问受保护的资源。
3 授权请求
OAuth为每个请求提供了两种认证体系,一种面向客户端,一种面向资源拥有者,在客户端代表资源拥有者发起请求之前,比必须获得一个被资源所有者授权过的令牌,第二节提供了这样的一种方法。
客户端证书有一个唯一的身份标识和一个与之关联的公钥或者RSA的键值对的形式组成,在发起经过认证的请求之前,客户端必须建立一组和服务器建立一组证书。
3.1 构造请求
一个认证请求必须包含如下的易理解协议参数,每个参数都是以oauth_的前缀开头,并且这个参数的名字和值都是区分大小写的:
oauth_consumer_key:
用来区分客户端身份。
oauth_token:
这个参数用来将资源拥有者和请求联系起来。如果资源拥有者和请求没有关系,或者token无法获取,忽略。
oauth_signature_method:
客户端签名请求的时候采用的签名方法的名称。
oauth_timestamp:
时间戳,当采用PLAINTEXT的签名方法的时候,忽略该值。
oauth_nonce:
当采用PLAINTEXT的签名方法的时候,忽略该值。
oauth_version:
协议版本,可选的,当前必须设置为1.0
如下,构造一个这样的http 认证请求(下例中c2&a3=2+q字符串用来说明一个表单实体数据的影响)
POST/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1
Host:example.com
Content-Type: application/x-www-form-urlencoded
c2&a3=2+q
客户端将这些协议参数利用OAuth HTTP的Authenrization的header域添加到请求中:
Authorization:OAuth realm="Example",
oauth_consumer_key="9djdj82h48djs9d2",
oauth_token="kkk9d7dh3k39sjv7",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="137131201",
oauth_nonce="7d8f3e4a"
然后,开始计算oauth_signature的值(利用客户端证书公钥和令牌证书的公钥),然后将他添加到请求中,并且将这次http请求发送到服务器:
POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20bHTTP/1.1
Host:example.com
Content-Type: application/x-www-form-urlencoded
Authorization: OAuth realm="Example",
oauth_consumer_key="9djdj82h48djs9d2",
oauth_token="kkk9d7dh3k39sjv7",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="137131201",
oauth_nonce="7d8f3e4a",
oauth_signature="bYT5CMsGcbgUdFHObYMEfcx6bsw%3D"
c2&a3=2+q
3.2 验证请求
当服务器收到一个认证后的请求,必须验证这个请求:
l 重新计算签名并且与之收到的客户端的oauth_signature参数进行比对。
l 如果使用了“HMAC-SHA1”或者“RSA-SHA1”的签名方法,必须确保收到的nonce、timestamp,token(如果有)不是用过了的,server也许会拒绝那些已经不新鲜的timestamp的请求。
l 如果token存在,校验他的范围以及令牌所代表的客户端权限的状态。
l 如果存在oauth_version,确保是1.0
如果请求校验失败了,服务器必须返回这次http 响应的状态码,进一步的了解当前请求被拒绝的原因。
如果收到的请求中欧没有提供的参数,没有提供的签名方法,丢失的参数,或者重复的协议参数,服务器应该返回400(bad request)的状态码。如果服务器收到了不合法的客户端证书,不合法的或者过去的令牌,不合法的签名,不合法的或者使用过了的nonce则应该返回 401(未授权)的状态码。
3.3 nonce和timestamp
timestamp的值必须是一个正数,除非服务器的文档另有指定,否则timestamp就是1970 00:00:00到当前时间的秒数。
nonce是一个随机字符串,由客户端生成以允许服务器确认这个请求之前从未被访问过来,防止当请求是非加密通道发起时的重试攻击,nonce值必须保证在相同的timestamp,客户端证书,以及token的情况下,都是唯一的。