Source for file OAuth.php
Documentation is available at OAuth.php
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
* Copyright (c) 2007 Andy Smith
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
$this->callback_url =
$callback_url;
return "OAuthConsumer[key=$this->key,secret=$this->secret]";
// access tokens and request tokens
* secret = the token secret
* generates the basic string serialization of a token that a server
* would respond to request_token and access_token calls with
$built =
$this->build_signature($request, $consumer, $token);
return $built ==
$signature;
$base_string =
$request->get_signature_base_string();
$request->base_string =
$base_string;
($token) ?
$token->secret :
""
$request->base_string =
$raw;
// not implemented yet, ideas are:
// (1) do a lookup in a table of trusted certs keyed off of consumer
// (2) fetch via http using a url provided by the requester
// (3) some sort of specific discovery code based on request
// either way should return a string representation of the certificate
trigger_error("fetch_public_cert not implemented", E_USER_WARNING);
// not implemented yet, ideas are:
// (1) do a lookup in a table of trusted certs keyed off of consumer
// either way should return a string representation of the certificate
trigger_error("fetch_private_cert not implemented", E_USER_WARNING);
$base_string =
$request->get_signature_base_string();
$request->base_string =
$base_string;
// Fetch the private key cert based on the request
// Pull the private key ID from the certificate
$ok =
openssl_sign($base_string, $signature, $privatekeyid);
// Release the key resource
$base_string =
$request->get_signature_base_string();
// Fetch the public key cert based on the request
// Pull the public key ID from the certificate
// Check the computed signature against the one passed in the query
// Release the key resource
function OAuthRequest($http_method, $http_url, $parameters=
NULL) {
@$parameters or $parameters =
array();
* attempt to build up a request from what was passed to the server
static function from_request($http_method=
NULL, $http_url=
NULL, $parameters=
NULL) {
$scheme =
(!isset
($_SERVER['HTTPS']) ||
$_SERVER['HTTPS'] !=
"on") ?
'http' :
'https';
@$http_url or $http_url =
$scheme .
'://' .
$_SERVER['HTTP_HOST'] .
':' .
$_SERVER['SERVER_PORT'] .
$_SERVER['REQUEST_URI'];
@$http_method or $http_method =
$_SERVER['REQUEST_METHOD'];
// let the library user override things however they'd like, if they know
// which parameters to use then go for it, for example XMLRPC might want to
$req =
new OAuthRequest($http_method, $http_url, $parameters);
// collect request parameters from query string (GET) and post-data (POST) if appropriate (note: POST vars have priority)
// NOTE: $_GET and $_POST will strip duplicate query parameters
if ($http_method ==
"POST" &&
@strstr($request_headers["Content-Type"], "application/x-www-form-urlencoded") ) {
$req_parameters =
array_merge($req_parameters, $_POST);
// next check for the auth header, we need to do some extra stuff
// if that is the case, namely suck in the parameters from GET or POST
// so that we can include them in the signature
if (@substr($request_headers['Authorization'], 0, 6) ==
"OAuth ") {
$parameters =
array_merge($req_parameters, $header_parameters);
$req =
new OAuthRequest($http_method, $http_url, $parameters);
} else $req =
new OAuthRequest($http_method, $http_url, $req_parameters);
* pretty much a helper function to set up the request
@$parameters or $parameters =
array();
$defaults =
array("oauth_version" =>
'1.0',
"oauth_consumer_key" =>
$consumer->key);
$parameters['oauth_token'] =
$token->key;
return new OAuthRequest($http_method, $http_url, $parameters);
* Returns the normalized parameters of the request
* This will be all (except oauth_signature) parameters,
* sorted first by key, and if duplicate keys, then by
* The returned string will be all the key=value pairs
// Include query parameters
$parsed_query =
$php_parsed_query;
foreach($parsed_query as $key =>
$value) {
// Remove oauth_signature if present
if (isset
($params['oauth_signature'])) {
unset
($params['oauth_signature']);
* Returns the base string of this request
* The base string defined as the method, the url
* and the parameters (normalized), each urlencoded
* and the concated with &.
* just uppercases the http method
* parses the url and rebuilds it to be
$scheme =
$parts['scheme'];
$port or $port =
($scheme ==
'https') ?
'443' :
'80';
if (($scheme ==
'https' &&
$port !=
'443')
||
($scheme ==
'http' &&
$port !=
'80')) {
return "$scheme://$host$path";
* builds a url usable for a GET request
* builds the data one would send in a POST request
* builds the Authorization: header
$out =
'Authorization: OAuth realm="yahooapis.com"';
if (substr($k, 0, 5) !=
"oauth")
trigger_error('Arrays not supported in headers', E_USER_WARNING);
function sign_request($signature_method, $consumer, $token) {
$this->set_parameter("oauth_signature_method", $signature_method->get_name());
$signature =
$signature_method->build_signature($this, $consumer, $token);
* util function: current timestamp
* util function: current nonce
return md5($mt .
$rand); // md5s look nicer than numbers
* util function for turning the Authorization: header into
* parameters, has to do some unescaping
$pattern =
'/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) >
0) {
$header_name =
$matches[2][0];
$header_content =
(isset
($matches[5])) ?
$matches[5][0] :
$matches[4][0];
$offset =
$match[1] +
strlen($match[0]);
if (isset
($params['realm'])) {
* helper to try to sort out headers for people who aren't running apache
// we need this to get the actual Authorization: header
// because apache tends to tell us it doesn't exist
return apache_request_headers();
// otherwise we don't have apache and are just going to have to hope
// that $_SERVER actually contains what we need
foreach ($_SERVER as $key =>
$value) {
if (substr($key, 0, 5) ==
"HTTP_") {
// this is chaos, basically it is just there to capitalize the first
// letter of every word that is not an initial HTTP and strip HTTP
if(!isset
($out['Content-Type'])) {
$out['Content-Type'] =
@$_SERVER['CONTENT_TYPE'];
* process a request_token request
* returns the request token on success
// no token required for the initial token request
$new_token =
$this->data_store->new_request_token($consumer);
* process an access_token request
* returns the access token on success
// requires authorized request token
$token =
$this->get_token($request, $consumer, "request");
$new_token =
$this->data_store->new_access_token($token, $consumer);
* verify an api call, checks all the parameters
$token =
$this->get_token($request, $consumer, "access");
return array($consumer, $token);
$version =
$request->get_parameter("oauth_version");
if ($version &&
$version !=
$this->version) {
trigger_error("OAuth version '$version' not supported", E_USER_WARNING);
* figure out the signature with some defaults
@$request->get_parameter("oauth_signature_method");
if (!$signature_method) {
$signature_method =
"PLAINTEXT";
* try to find the consumer for the provided request's consumer key
$consumer_key =
@$request->get_parameter("oauth_consumer_key");
$consumer =
$this->data_store->lookup_consumer($consumer_key);
* try to find the token for the provided request's token key
function get_token(&$request, $consumer, $token_type=
"access") {
$token_field =
@$request->get_parameter('oauth_token');
$consumer, $token_type, $token_field
trigger_error("Invalid $token_type token: $token_field", E_USER_WARNING);
* all-in-one function to check the signature on a request
* should guess the signature method appropriately
// this should probably be in a different method
$timestamp =
@$request->get_parameter('oauth_timestamp');
$nonce =
@$request->get_parameter('oauth_nonce');
$this->check_nonce($consumer, $token, $nonce, $timestamp);
$signature =
$request->get_parameter('oauth_signature');
$valid_sig =
$signature_method->check_signature(
* check that the timestamp is new enough
// verify that timestamp is recentish
trigger_error("Expired timestamp, yours $timestamp, ours $now", E_USER_WARNING);
* check that the nonce is not repeated
function check_nonce($consumer, $token, $nonce, $timestamp) {
// verify that the nonce is uniqueish
$found =
$this->data_store->lookup_nonce($consumer, $token, $nonce, $timestamp);
function lookup_nonce($consumer, $token, $nonce, $timestamp) {
// return a new token attached to this consumer
// return a new access token attached to this consumer
// for the user associated with this token if the request token
// should also invalidate the request token
/* A very naive dbm-based oauth storage
function lookup_nonce($consumer, $token, $nonce, $timestamp) {
function new_token($consumer, $type=
"request") {
return $this->new_token($consumer, "request");
$token =
$this->new_token($consumer, 'access');
// This function takes a input like a=b&a=c&d=e and returns the parsed
// array('a' => array('b','c'), 'd' => 'e')
if (!isset
($input) ||
!$input) return array();
$parsed_parameters =
array();
foreach ($pairs as $pair) {
if (isset
($parsed_parameters[$parameter])) {
// We have already recieved parameter(s) with this name, so add to the list
// of parameters with this name
if (is_scalar($parsed_parameters[$parameter])) {
// This is the first duplicate, so transform scalar (string) into an array
// so we can add the duplicates
$parsed_parameters[$parameter] =
array($parsed_parameters[$parameter]);
$parsed_parameters[$parameter][] =
$value;
$parsed_parameters[$parameter] =
$value;
return $parsed_parameters;
// Utility function for turning the Authorization: header into
// parameters, has to do some unescaping
// Can filter out any non-oauth parameters if needed (default behaviour)
static function split_header($header, $only_allow_oauth_parameters =
true) {
$pattern =
'/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) >
0) {
$header_name =
$matches[2][0];
$header_content =
(isset
($matches[5])) ?
$matches[5][0] :
$matches[4][0];
if (preg_match('/^oauth_/', $header_name) ||
!$only_allow_oauth_parameters) {
$offset =
$match[1] +
strlen($match[0]);
if (isset
($params['realm'])) {
// helper to try to sort out headers for people who aren't running apache
// we need this to get the actual Authorization: header
// because apache tends to tell us it doesn't exist
return apache_request_headers();
// otherwise we don't have apache and are just going to have to hope
// that $_SERVER actually contains what we need
foreach ($_SERVER as $key =>
$value) {
if (substr($key, 0, 5) ==
"HTTP_") {
// this is chaos, basically it is just there to capitalize the first
// letter of every word that is not an initial HTTP and strip HTTP
// Urlencode both keys and values
// Parameters are sorted by name, using lexicographical byte value ordering.
foreach ($params as $parameter =>
$value) {
// If two or more parameters share the same name, they are sorted by their value
foreach ($value as $duplicate_value) {
$pairs[] =
$parameter .
'=' .
$duplicate_value;
$pairs[] =
$parameter .
'=' .
$value;
// For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
// Each name-value pair is separated by an '&' character (ASCII code 38)
if(!isset
($query_string)) {
$pairs =
explode('&', $query_string);
foreach($pairs as $pair) {
list
($k, $v) =
explode('=', $pair, 2);
// Handle duplicate query params
if(isset
($query_arr[$k])) {
// Transform scalar to array
$query_arr[$k] =
array($query_arr[$k]);
// Urlencode both keys and values
// Sort by keys (natsort)
foreach ($params as $k =>
$v) {
foreach ($v as $duplicate_value) {
$pairs[] =
$k .
'=' .
$duplicate_value;
$pairs[] =
$k .
'=' .
$v;
return array_map(array('OAuthUtil','urlencode_rfc3986'), $input);
// This decode function isn't taking into consideration the above
// modifications to the encoding process. However, this method doesn't
// seem to be used anywhere so leaving it as is.
return OAuthUtil::urldecode_rfcC3986($input);
* Crib'd native implementation of hash_hmac() for SHA1 from the
* http://fireeagle.yahoo.net/developer/code/php
// Earlier versions of PHP5 are missing hash_hmac(). Here's a
// pure-PHP version in case you're using one of them.
// Thanks, Kellan: http://laughingmeme.org/code/hmacsha1.php.txt
trigger_error("Internal hash_hmac() can only do sha1, sorry", E_USER_WARNING);
$key =
pack('H*', $hashfunc($key));
Documentation generated on Thu, 22 Oct 2009 12:54:45 -0700 by phpDocumentor 1.4.3