1 <?php 2 /* Copyright (c) 2013, Geert Bergman (geert@scrivo.nl) 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. Neither the name of "Scrivo" nor the names of its contributors may be 14 * used to endorse or promote products derived from this software without 15 * specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $Id$ 30 */ 31 32 namespace Scrivo\Utilities; 33 34 /** 35 * Class that implements the OAuth 1.0 protocol. Using OAuth you can send 36 * authorized requests to web services. This class implements some of these 37 * authorization rules. 38 * 39 * Also see the OAuth rfc: 40 * 1.0: (http://tools.ietf.org/html/rfc5849). 41 * 42 * Example (using bogus values): 43 * 44 * $oAuth = new OAuth( 45 * "xvz1evFS4wEEPTGEFPHBog", //< consumer key 46 * "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw", //< consumer secret 47 * "370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", //< access token 48 * "LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE" //< access token secret 49 * ); 50 * 51 * // Get the data to use for an authorized request. 52 * $oAuthData = $oAuth->getAuthorizationData($requestMethod, 53 * "https://api.twitter.com/1.1/statuses/user_timeline.json?count=2"); 54 * 55 * // This is the autorization header to use in your request: 56 * echo $oAuthData->authorisationHeader; 57 * 58 */ 59 class OAuth { 60 61 /** 62 * The OAuth version (currently we're only supporting 1.0); 63 * @var string 64 */ 65 private $version = "1.0"; 66 67 /** 68 * The identifier portion of the client credentials (equivalent to 69 * a username). The parameter name reflects a deprecated term 70 * (Consumer Key) used in previous revisions of the specification, 71 * and has been retained to maintain backward compatibility. 72 * @link http://tools.ietf.org/html/rfc5849#page-15 reference 73 * @var string 74 */ 75 private $consumerKey = null; 76 77 /** 78 * The client shared-secret, after being encoded 79 * @var string 80 */ 81 private $consumerSecret = null; 82 83 /** 84 * The token value used to associate the request with the resource 85 * owner. If the request is not associated with a resource owner 86 * (no token available), clients MAY omit the parameter. 87 * @link http://tools.ietf.org/html/rfc5849#page-15 reference 88 * @var string 89 */ 90 private $token = null; 91 92 /** 93 * The token shared-secret, after being encoded 94 * @var string 95 */ 96 private $tokenSecret = null; 97 98 /** 99 * Construct an OAuth object: an object that is able to do 100 * authenticated requests. 101 * @param string $consumerKey The identifier portion of the client 102 * credentials (equivalent to a username). 103 * @param string $consumerSecret The client shared-secret. 104 * @param string $token The token value used to associate the request 105 * with the resource owner. 106 * @param string $tokenSecret The token shared-secret. 107 */ 108 public function __construct( 109 $consumerKey, $consumerSecret, $token, $tokenSecret) { 110 $this->consumerKey = $consumerKey; 111 $this->consumerSecret = $consumerSecret; 112 $this->token = $token; 113 $this->tokenSecret = $tokenSecret; 114 } 115 116 /** 117 * A nonce is a random string, uniquely generated by the client to allow 118 * the server to verify that a request has never been made before and 119 * helps prevent replay attacks when requests are made over a non-secure 120 * channel. The nonce value MUST be unique across all requests with the 121 * same timestamp, client credentials, and token combinations. 122 * @link http://tools.ietf.org/html/rfc5849#section-3.3 reference 123 * @return string A unique token to send with the request. 124 */ 125 private function nonce() { 126 // Not really unique using an md5 hash, but good enough hopefully. 127 return hash("md5", mt_rand().time().mt_rand(), false); 128 } 129 130 /** 131 * Return a percent encoded string. 132 * @link http://tools.ietf.org/html/rfc5849#section-3.6 reference 133 * @param string $toEncode The data to percent encode. 134 * @return string The percent encoded data. 135 */ 136 private function encode($toEncode) { 137 return rawurlencode($toEncode); 138 } 139 140 /** 141 * Percent encode an array of key/value pairs. The method allow for 142 * different glues to glue the encoded sets of key/value pairs together 143 * (apmerand and comma for example). It is also possible to supply a 144 * quotation mark for the values in the result string. 145 * @link http://tools.ietf.org/html/rfc5849#section-3.6 reference 146 * @param string[] $toEncode An array with key/value pairs. 147 * @param string[] $glue The glue to bind the key/value pairs. 148 * @param string $qoute An optional quotation sign for the value. 149 * @return string A string containing the percent encoded key/value pairs 150 * seperated by an '=' sign and glued together using the $glue parameter. 151 */ 152 private function encodeKeyValuePairs(array $toEncode, $glue, $quote="") { 153 $res = array(); 154 foreach ($toEncode as $k=>$v) { 155 $res[] = $this->encode($k)."=".$quote.$this->encode($v).$quote; 156 } 157 return implode($glue, $res); 158 } 159 160 /** 161 * Normalize the collected parameters into a single string. 162 * @link http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2 reference 163 * @param string[] $oauthParam An array of OAuth key/value pairs. 164 * @param string[] $param An array of request parameter key/value pairs. 165 * @return string A string of percent encoded key/value pairs each 166 * seperated by an '=' sign and each pair seperated by an ampersand. 167 */ 168 private function normalizeParam($oauthParam, $param) { 169 $param += $oauthParam; 170 ksort($param); 171 return $this->encodeKeyValuePairs($param, "&"); 172 } 173 174 /** 175 * The signature base string is a consistent, reproducible concatenation 176 * of several of the HTTP request elements into a single string. The 177 * string is used as an input to the "HMAC-SHA1" and "RSA-SHA1" 178 * signature methods. 179 * @link http://tools.ietf.org/html/rfc5849#section-3.4.1 reference 180 * @param string $requestMethod The HTTP request method to use in the 181 * request. 182 * @param string $baseUrl The base URL of the request (= the request URL 183 * including the protocol, host and path but excluding the parameters). 184 * @param string[] $oauthParam An array of OAuth key/value pairs. 185 * @param string[] $param An array of request parameter key/value pairs. 186 * @return string The OAuth signature base string. 187 */ 188 private function signatureBaseString( 189 $requestMethod, $baseUrl, $oauthParam, $requestParam) { 190 return implode("&", array(strtoupper($requestMethod), 191 $this->encode($baseUrl), $this->encode( 192 $this->normalizeParam($oauthParam, $requestParam)))); 193 } 194 195 /** 196 * Create the OAuth signature for a request. 197 * @link http://tools.ietf.org/html/rfc5849#section-3.4.2 reference 198 * @param string $requestMethod The HTTP request method to use in this 199 * Twitter API request. 200 * @param string $baseUrl The base URL of the Twitter API request (= the 201 * request URL including the protocol, host and path but excluding the 202 * parameters). 203 * @param string[] $oauthParam An array of OAuth key/value pairs. 204 * @param string[] $requestParam An array of request parameter key/value 205 * pairs. 206 * @return string The OAuth signature. 207 */ 208 private function sign( 209 $requestMethod, $baseUrl, $oauthParam, $requestParam) { 210 return base64_encode(hash_hmac("sha1", 211 $this->signatureBaseString( 212 $requestMethod, $baseUrl, $oauthParam, $requestParam), 213 $this->consumerSecret."&".$this->tokenSecret, true)); 214 } 215 216 /** 217 * Get the authorization request data for an OAuth reqeuest. 218 * @see https://dev.twitter.com/docs/auth/authorizing-request 219 * @param string $requestMethod The HTTP request method to use in the 220 * request. 221 * @param string $baseUrl The base URL of the Twitter API request (= the 222 * request URL including the protocol, host and path but excluding the 223 * parameters). 224 * @param string[] $param An array of request parameter key/value 225 * pairs. 226 */ 227 private function authorisationHeader($requestMethod, $baseUrl, $param) { 228 $o = array( 229 "oauth_consumer_key" => $this->consumerKey, 230 "oauth_token" => $this->token, 231 "oauth_version" => $this->version, 232 "oauth_timestamp" => time(), 233 "oauth_nonce" => $this->nonce(), 234 "oauth_signature_method" => "HMAC-SHA1"); 235 $o["oauth_signature"] = 236 $this->sign($requestMethod, $baseUrl, $o, $param); 237 return "Authorization: OAuth " . 238 $this->encodeKeyValuePairs($o, ", ", "\"")."\r\n"; 239 } 240 241 /** 242 * Get the data for an OAuth 1.0 authorized request. 243 * @param string $requestMethod The HTTP request method to use in this 244 * request (GET or POST). 245 * @param string $url The URL for the request. Request parameters 246 * can be included in the URL. 247 * Note: this is an unescaped URL: ampersands should be "&" (not "&") 248 * and spaces should be " " (not "%20" or "+"), and this is not limited 249 * to ampersands and spaces. 250 * @param string[] $param Optional extra request parameters given as a set 251 * of name/value pairs. These parameters will get preceedence when 252 * name conflicts occur with parameters given in the $url parameter 253 * itself. 254 * @return object Object containting the following fields: 255 * authorisationHeader (string): The OAuth authorization header 256 * (including \r\n) 257 * parameterString (string): The request parameters in an (percent 258 * encoded) application/x-www-form-urlencoded format. 259 * baseUrl (string): The request URL without the parameters. 260 * requestMethod (string): The request method (capitalized). 261 * scheme (string): The request scheme. 262 * hostname (string): The name of the host to send the request to. 263 */ 264 public function getAuthorizationData( 265 $requestMethod, $url, array $param=array()) { 266 267 $requestMethod = strtoupper($requestMethod); 268 $ud = parse_url($url); 269 $baseUrl = $ud["scheme"]."://".$ud["host"].$ud["path"]; 270 if (isset($ud["query"])) { 271 parse_str($ud["query"], $param2); 272 $param += $param2; 273 } 274 275 return (object)array( 276 "authorisationHeader" => 277 $this->authorisationHeader($requestMethod, $baseUrl, $param), 278 "parameterString" => 279 count($param) ? $this->encodeKeyValuePairs($param, "&") : "", 280 "baseUrl" => $baseUrl, 281 "requestMethod" => $requestMethod, 282 "scheme" => $ud["scheme"], 283 "hostname" => $ud["host"] 284 ); 285 286 } 287 } 288 289 ?>
Documentation generated by phpDocumentor 2.0.0a12 and ScrivoDocumentor on August 29, 2013