├── CHANGELOG ├── LICENSE ├── README ├── getacctok.php ├── getreqtok.php ├── globals.php ├── oauth_helper.php └── tweet.php /CHANGELOG: -------------------------------------------------------------------------------- 1 | OAuth Examples using Twitter 2 | --- 3 | 4 | 1.0.1 2016-12-29 Joe Chung 5 | * Change Twitter API endpoint scheme from http to https. 6 | 7 | 1.0.0 2010-04-14 Joe Chung 8 | * Initial Release 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License for OAuth Examples 2 | 3 | Copyright (c) 2010 Joe Chung - joechung at yahoo dot com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | ------------------------------------------------------------------------------ 24 | 25 | This code also contains software derived from the PHP OAuth Library 26 | Copyright 2007 Andy Smith: 27 | 28 | 29 | MIT License - for PHP OAuth Library 30 | 31 | Copyright © 2007 Andy Smith 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy 34 | of this software and associated documentation files (the "Software"), to deal 35 | in the Software without restriction, including without limitation the rights 36 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 | copies of the Software, and to permit persons to whom the Software is 38 | furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 46 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 49 | SOFTWARE. 50 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | OAuth Examples using Twitter 2 | 3 | These standalone scripts are meant to be run from the command line, 4 | for learning the OAuth requests and responses. You are expectd to 5 | edit these scripts (very minimally) to fill in the various tokens and 6 | secrets required as input for each step. 7 | 8 | The accompanying tutorial can be found here: 9 | http://nullinfo.wordpress.com/oauth-twitter/ 10 | 11 | globals.php - edit to fill in the consumer key and secret 12 | oauth_helper.php - helper functions; no editing required 13 | getreqtok.php - no editing required 14 | getacctok.php - edit to fill in the required token, secret, and verifier 15 | tweet.php - edit to fill in the required token and secret 16 | 17 | Though the concepts are generic to OAuth, these scripts have been 18 | tweaked to work with Twitter. 19 | -------------------------------------------------------------------------------- /getacctok.php: -------------------------------------------------------------------------------- 1 | 98 | -------------------------------------------------------------------------------- /getreqtok.php: -------------------------------------------------------------------------------- 1 | 94 | -------------------------------------------------------------------------------- /globals.php: -------------------------------------------------------------------------------- 1 | $url, 34 | CURLOPT_PORT => $port, 35 | CURLOPT_POST => false, 36 | CURLOPT_SSL_VERIFYHOST => false, 37 | CURLOPT_SSL_VERIFYPEER => false, 38 | CURLOPT_RETURNTRANSFER => true); 39 | 40 | if ($headers) { $curl_opts[CURLOPT_HTTPHEADER] = $headers; } 41 | 42 | $response = do_curl($curl_opts); 43 | 44 | if (! empty($response)) { $retarr = $response; } 45 | 46 | return $retarr; 47 | } 48 | 49 | /** 50 | * Do an HTTP POST 51 | * @param string $url 52 | * @param int $port (optional) 53 | * @param array $headers an array of HTTP headers (optional) 54 | * @return array ($info, $header, $response) on success or empty array on error. 55 | */ 56 | function do_post($url, $postbody, $port=80, $headers=NULL) 57 | { 58 | $retarr = array(); // Return value 59 | 60 | $curl_opts = array(CURLOPT_URL => $url, 61 | CURLOPT_PORT => $port, 62 | CURLOPT_POST => true, 63 | CURLOPT_SSL_VERIFYHOST => false, 64 | CURLOPT_SSL_VERIFYPEER => false, 65 | CURLOPT_POSTFIELDS => $postbody, 66 | CURLOPT_RETURNTRANSFER => true); 67 | 68 | if ($headers) { $curl_opts[CURLOPT_HTTPHEADER] = $headers; } 69 | 70 | $response = do_curl($curl_opts); 71 | 72 | if (! empty($response)) { $retarr = $response; } 73 | 74 | return $retarr; 75 | } 76 | 77 | /** 78 | * Make a curl call with given options. 79 | * @param array $curl_opts an array of options to curl 80 | * @return array ($info, $header, $response) on success or empty array on error. 81 | */ 82 | function do_curl($curl_opts) 83 | { 84 | global $debug; 85 | 86 | $retarr = array(); // Return value 87 | 88 | if (! $curl_opts) { 89 | if ($debug) { logit("do_curl:ERR:curl_opts is empty"); } 90 | return $retarr; 91 | } 92 | 93 | // Open curl session 94 | $ch = curl_init(); 95 | if (! $ch) { 96 | if ($debug) { logit("do_curl:ERR:curl_init failed"); } 97 | return $retarr; 98 | } 99 | 100 | // Set curl options that were passed in 101 | curl_setopt_array($ch, $curl_opts); 102 | 103 | // Ensure that we receive full header 104 | curl_setopt($ch, CURLOPT_HEADER, true); 105 | 106 | if ($debug) { 107 | curl_setopt($ch, CURLINFO_HEADER_OUT, true); 108 | curl_setopt($ch, CURLOPT_VERBOSE, true); 109 | } 110 | 111 | // Send the request and get the response 112 | ob_start(); 113 | $response = curl_exec($ch); 114 | $curl_spew = ob_get_contents(); 115 | ob_end_clean(); 116 | if ($debug && $curl_spew) { 117 | logit("do_curl:INFO:curl_spew begin"); 118 | logit($curl_spew, false); 119 | logit("do_curl:INFO:curl_spew end"); 120 | } 121 | 122 | // Check for errors 123 | if (curl_errno($ch)) { 124 | $errno = curl_errno($ch); 125 | $errmsg = curl_error($ch); 126 | if ($debug) { logit("do_curl:ERR:$errno:$errmsg"); } 127 | curl_close($ch); 128 | unset($ch); 129 | return $retarr; 130 | } 131 | 132 | if ($debug) { 133 | logit("do_curl:DBG:header sent begin"); 134 | $header_sent = curl_getinfo($ch, CURLINFO_HEADER_OUT); 135 | logit($header_sent, false); 136 | logit("do_curl:DBG:header sent end"); 137 | } 138 | 139 | // Get information about the transfer 140 | $info = curl_getinfo($ch); 141 | 142 | // Parse out header and body 143 | $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); 144 | $header = substr($response, 0, $header_size); 145 | $body = substr($response, $header_size ); 146 | 147 | // Close curl session 148 | curl_close($ch); 149 | unset($ch); 150 | 151 | if ($debug) { 152 | logit("do_curl:DBG:response received begin"); 153 | if (!empty($response)) { logit($response, false); } 154 | logit("do_curl:DBG:response received end"); 155 | } 156 | 157 | // Set return value 158 | array_push($retarr, $info, $header, $body); 159 | 160 | return $retarr; 161 | } 162 | 163 | /** 164 | * Pretty print some JSON 165 | * @param string $json The packed JSON as a string 166 | * @param bool $html_output true if the output should be escaped 167 | * (for use in HTML) 168 | * @link http://us2.php.net/manual/en/function.json-encode.php#80339 169 | */ 170 | function json_pretty_print($json, $html_output=false) 171 | { 172 | $spacer = ' '; 173 | $level = 1; 174 | $indent = 0; // current indentation level 175 | $pretty_json = ''; 176 | $in_string = false; 177 | 178 | $len = strlen($json); 179 | 180 | for ($c = 0; $c < $len; $c++) { 181 | $char = $json[$c]; 182 | switch ($char) { 183 | case '{': 184 | case '[': 185 | if (!$in_string) { 186 | $indent += $level; 187 | $pretty_json .= $char . "\n" . str_repeat($spacer, $indent); 188 | } else { 189 | $pretty_json .= $char; 190 | } 191 | break; 192 | case '}': 193 | case ']': 194 | if (!$in_string) { 195 | $indent -= $level; 196 | $pretty_json .= "\n" . str_repeat($spacer, $indent) . $char; 197 | } else { 198 | $pretty_json .= $char; 199 | } 200 | break; 201 | case ',': 202 | if (!$in_string) { 203 | $pretty_json .= ",\n" . str_repeat($spacer, $indent); 204 | } else { 205 | $pretty_json .= $char; 206 | } 207 | break; 208 | case ':': 209 | if (!$in_string) { 210 | $pretty_json .= ": "; 211 | } else { 212 | $pretty_json .= $char; 213 | } 214 | break; 215 | case '"': 216 | if ($c > 0 && $json[$c-1] != '\\') { 217 | $in_string = !$in_string; 218 | } 219 | default: 220 | $pretty_json .= $char; 221 | break; 222 | } 223 | } 224 | 225 | return ($html_output) ? 226 | '
' . htmlentities($pretty_json) . '
' : 227 | $pretty_json . "\n"; 228 | } 229 | ?> 230 | -------------------------------------------------------------------------------- /oauth_helper.php: -------------------------------------------------------------------------------- 1 | $v) { 30 | if ($excludeOauthParams && substr($k, 0, 5) == 'oauth') { 31 | continue; 32 | } 33 | if (is_array($v)) { 34 | // If two or more parameters share the same name, 35 | // they are sorted by their value. OAuth Spec: 9.1.1 (1) 36 | natsort($v); 37 | foreach ($v as $value_for_same_key) { 38 | array_push($kvpairs, ($k . '=' . $value_for_same_key)); 39 | } 40 | } else { 41 | // For each parameter, the name is separated from the corresponding 42 | // value by an '=' character (ASCII code 61). OAuth Spec: 9.1.1 (2) 43 | array_push($kvpairs, ($k . '=' . $v)); 44 | } 45 | } 46 | 47 | // Each name-value pair is separated by an '&' character, ASCII code 38. 48 | // OAuth Spec: 9.1.1 (2) 49 | $query_string = implode('&', $kvpairs); 50 | } 51 | 52 | return $query_string; 53 | } 54 | 55 | /** 56 | * Parse a query string into an array. 57 | * @param string $query_string an OAuth query parameter string 58 | * @return array an array of query parameters 59 | * @link http://oauth.net/core/1.0/#rfc.section.9.1.1 60 | */ 61 | function oauth_parse_str($query_string) 62 | { 63 | $query_array = array(); 64 | 65 | if (isset($query_string)) { 66 | 67 | // Separate single string into an array of "key=value" strings 68 | $kvpairs = explode('&', $query_string); 69 | 70 | // Separate each "key=value" string into an array[key] = value 71 | foreach ($kvpairs as $pair) { 72 | list($k, $v) = explode('=', $pair, 2); 73 | 74 | // Handle the case where multiple values map to the same key 75 | // by pulling those values into an array themselves 76 | if (isset($query_array[$k])) { 77 | // If the existing value is a scalar, turn it into an array 78 | if (is_scalar($query_array[$k])) { 79 | $query_array[$k] = array($query_array[$k]); 80 | } 81 | array_push($query_array[$k], $v); 82 | } else { 83 | $query_array[$k] = $v; 84 | } 85 | } 86 | } 87 | 88 | return $query_array; 89 | } 90 | 91 | /** 92 | * Build an OAuth header for API calls 93 | * @param array $params an array of query parameters 94 | * @return string encoded for insertion into HTTP header of API call 95 | */ 96 | function build_oauth_header($params, $realm='') 97 | { 98 | $header = 'Authorization: OAuth realm="' . $realm . '"'; 99 | foreach ($params as $k => $v) { 100 | if (substr($k, 0, 5) == 'oauth') { 101 | $header .= ',' . rfc3986_encode($k) . '="' . rfc3986_encode($v) . '"'; 102 | } 103 | } 104 | return $header; 105 | } 106 | 107 | /** 108 | * Compute an OAuth PLAINTEXT signature 109 | * @param string $consumer_secret 110 | * @param string $token_secret 111 | */ 112 | function oauth_compute_plaintext_sig($consumer_secret, $token_secret) 113 | { 114 | return ($consumer_secret . '&' . $token_secret); 115 | } 116 | 117 | /** 118 | * Compute an OAuth HMAC-SHA1 signature 119 | * @param string $http_method GET, POST, etc. 120 | * @param string $url 121 | * @param array $params an array of query parameters for the request 122 | * @param string $consumer_secret 123 | * @param string $token_secret 124 | * @return string a base64_encoded hmac-sha1 signature 125 | * @see http://oauth.net/core/1.0/#rfc.section.A.5.1 126 | */ 127 | function oauth_compute_hmac_sig($http_method, $url, $params, $consumer_secret, $token_secret) 128 | { 129 | global $debug; 130 | 131 | $base_string = signature_base_string($http_method, $url, $params); 132 | $signature_key = rfc3986_encode($consumer_secret) . '&' . rfc3986_encode($token_secret); 133 | $sig = base64_encode(hash_hmac('sha1', $base_string, $signature_key, true)); 134 | if ($debug) { 135 | logit("oauth_compute_hmac_sig:DBG:sig:$sig"); 136 | } 137 | return $sig; 138 | } 139 | 140 | /** 141 | * Make the URL conform to the format scheme://host/path 142 | * @param string $url 143 | * @return string the url in the form of scheme://host/path 144 | */ 145 | function normalize_url($url) 146 | { 147 | $parts = parse_url($url); 148 | 149 | $scheme = $parts['scheme']; 150 | $host = $parts['host']; 151 | $port = $parts['port']; 152 | $path = $parts['path']; 153 | 154 | if (! $port) { 155 | $port = ($scheme == 'https') ? '443' : '80'; 156 | } 157 | if (($scheme == 'https' && $port != '443') 158 | || ($scheme == 'http' && $port != '80')) { 159 | $host = "$host:$port"; 160 | } 161 | 162 | return "$scheme://$host$path"; 163 | } 164 | 165 | /** 166 | * Returns the normalized signature base string of this request 167 | * @param string $http_method 168 | * @param string $url 169 | * @param array $params 170 | * The base string is defined as the method, the url and the 171 | * parameters (normalized), each urlencoded and the concated with &. 172 | * @see http://oauth.net/core/1.0/#rfc.section.A.5.1 173 | */ 174 | function signature_base_string($http_method, $url, $params) 175 | { 176 | // Decompose and pull query params out of the url 177 | $query_str = parse_url($url, PHP_URL_QUERY); 178 | if ($query_str) { 179 | $parsed_query = oauth_parse_str($query_str); 180 | // merge params from the url with params array from caller 181 | $params = array_merge($params, $parsed_query); 182 | } 183 | 184 | // Remove oauth_signature from params array if present 185 | if (isset($params['oauth_signature'])) { 186 | unset($params['oauth_signature']); 187 | } 188 | 189 | // Create the signature base string. Yes, the $params are double encoded. 190 | $base_string = rfc3986_encode(strtoupper($http_method)) . '&' . 191 | rfc3986_encode(normalize_url($url)) . '&' . 192 | rfc3986_encode(oauth_http_build_query($params)); 193 | 194 | logit("signature_base_string:INFO:normalized_base_string:$base_string"); 195 | 196 | return $base_string; 197 | } 198 | 199 | /** 200 | * Encode input per RFC 3986 201 | * @param string|array $raw_input 202 | * @return string|array properly rfc3986 encoded raw_input 203 | * If an array is passed in, rfc3896 encode all elements of the array. 204 | * @link http://oauth.net/core/1.0/#encoding_parameters 205 | */ 206 | function rfc3986_encode($raw_input) 207 | { 208 | if (is_array($raw_input)) { 209 | return array_map('rfc3986_encode', $raw_input); 210 | } else if (is_scalar($raw_input)) { 211 | return str_replace('%7E', '~', rawurlencode($raw_input)); 212 | } else { 213 | return ''; 214 | } 215 | } 216 | 217 | function rfc3986_decode($raw_input) 218 | { 219 | return rawurldecode($raw_input); 220 | } 221 | ?> 222 | -------------------------------------------------------------------------------- /tweet.php: -------------------------------------------------------------------------------- 1 | 82 | --------------------------------------------------------------------------------