Merge pull request #28 from jrconlin/dev

Merge Dev to master
This commit is contained in:
JR Conlin
2015-03-20 15:56:28 -07:00
3 changed files with 220 additions and 89 deletions
+56
View File
@@ -0,0 +1,56 @@
#OAuthSimple for Javascript
## A Word of Caution:
It might be useful to understand what OAuth is. OAuth is a way for two
(or more) parties to confirm the fact that they are who they say they
are. This is done by a set of secret handshakes that only both sides
know.
In short, the very first thing that happens is that both sides
exchange some secret value. This exchange happens outside of OAuth and
the secret is NEVER displayed to anyone other than the two parties.
This... gets a bit tricky when you're talking about javascript.
The example code in index.html doesn't even try to hide the secret,
and frankly, it's considerably difficult to do that even in the best
of situations because users can stop and examine code with most recent
browsers.
So, while your secret may be disclosed, in a properly functioning
system, the user's data is only disclosed after the user concents to
it.
If you're building this for your own use on a device you control where
your code can't be inspected by malicious parties. Go for it.
Otherwise, you probably want to do the OAuth exchange on the server
and pass back the final access token to the javascript app. (See the
OAuth documentation for whatever site you're working with for details
about that.)
## Webcalls without a Web
There are lots of environments that let you run javascript outside of
a browser. I'm not going to detail all of them (because there are a
lot of them), but I'll note that OAuth was designed around using HTTP
as the transport mechanism.
Fortunately, that doesn't mean that you have to fire up your browser
of choice in order to make these calls. You do need to have something
that talks HTTP, however. Again, there are lots of choices, for
instance libcURL is available for darn near everything out there.
There are also plenty of systems that provide native calls.
For the most part, these calls will either want the arguments in-line
with the call (use ```OAuthSimple().sign(...).signed_url;```), as a
request header ( ```OAuthSimple().sign(...).header```), or as the POST
request body (you may want to use _normalizedParameters() for that,
but understand that it's not a public function and is subject to
change.) Again, see the documentation for the service you wish to
connect to for how best to provide this info.
## How to use it.
Honestly? Read the source. I commented the heck out of it and there's
example code inside there. Plus, unlike this, it is more likely to be
documented and included with the distribution.
+93 -89
View File
@@ -1,9 +1,9 @@
<?php
/**
/**
* OAuthSimple - A simpler version of OAuth
*
* https://github.com/jrconlin/oauthsimple
*
*
* @author jr conlin <src@jrconlin.com>
* @copyright unitedHeroes.net 2011
* @version 1.3
@@ -17,7 +17,7 @@ class OAuthSimple {
private $_action;
private $_nonce_chars;
/**
/**
* Constructor
*
* @access public
@@ -26,47 +26,47 @@ class OAuthSimple {
* @return OAuthSimple (Object)
*/
function __construct ($APIKey = "", $sharedSecret=""){
if (!empty($APIKey))
{
$this->_secrets['consumer_key'] = $APIKey;
}
if (!empty($sharedSecret))
{
$this->_secrets['shared_secret'] = $sharedSecret;
}
$this->_default_signature_method = "HMAC-SHA1";
$this->_action = "GET";
$this->_nonce_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
return $this;
}
/**
* Reset the parameters and URL
*
/**
* Reset the parameters and URL
*
* @access public
* @return OAuthSimple (Object)
*/
*/
public function reset() {
$this->_parameters = Array();
$this->path = NULL;
$this->sbs = NULL;
return $this;
return $this;
}
/**
* Set the parameters either from a hash or a string
*
/**
* Set the parameters either from a hash or a string
*
* @access public
* @param(string, object) List of parameters for the call, this can either be a URI string (e.g. "foo=bar&gorp=banana" or an object/hash)
* @return OAuthSimple (Object)
*/
*/
public function setParameters ($parameters=Array()) {
if (is_string($parameters))
{
$parameters = $this->_parseParameterString($parameters);
@@ -103,24 +103,24 @@ class OAuthSimple {
{
$this->_parameters['oauth_version']="1.0";
}
return $this;
}
/**
/**
* Convenience method for setParameters
*
*
* @access public
* @see setParameters
*/
public function setQueryString ($parameters)
*/
public function setQueryString ($parameters)
{
return $this->setParameters($parameters);
}
/**
/**
* Set the target URL (does not include the parameters)
*
*
* @param path (String) the fully qualified URI (excluding query arguments) (e.g "http://example.org/foo")
* @return OAuthSimple (Object)
*/
@@ -131,28 +131,28 @@ class OAuthSimple {
throw new OAuthSimpleException('No path specified for OAuthSimple.setURL');
}
$this->_path=$path;
return $this;
}
/**
/**
* Convenience method for setURL
*
* @param path (String)
* @param path (String)
* @see setURL
*/
public function setPath ($path)
public function setPath ($path)
{
return $this->_path=$path;
}
/**
/**
* Set the "action" for the url, (e.g. GET,POST, DELETE, etc.)
*
* @param action (String) HTTP Action word.
* @return OAuthSimple (Object)
*/
public function setAction ($action)
public function setAction ($action)
{
if (empty($action))
{
@@ -164,17 +164,17 @@ class OAuthSimple {
throw new OAuthSimpleException('Invalid action specified for OAuthSimple.setAction');
}
$this->_action = $action;
return $this;
}
/**
/**
* Set the signatures (as well as validate the ones you have)
*
* @param signatures (object) object/hash of the token/signature pairs {api_key:, shared_secret:, oauth_token: oauth_secret:}
* @return OAuthSimple (Object)
*/
public function signatures ($signatures)
public function signatures ($signatures)
{
if (!empty($signatures) && !is_array($signatures))
{
@@ -182,7 +182,7 @@ class OAuthSimple {
}
if (!empty($signatures))
{
if (empty($this->_secrets))
if (empty($this->_secrets))
{
$this->_secrets=Array();
}
@@ -216,16 +216,16 @@ class OAuthSimple {
{
throw new OAuthSimpleException('Missing oauth_secret for supplied oauth_token in OAuthSimple.signatures');
}
return $this;
}
public function setTokensAndSecrets($signatures)
public function setTokensAndSecrets($signatures)
{
return $this->signatures($signatures);
}
/**
/**
* Set the signature method (currently only Plaintext or SHA-MAC1)
*
* @param method (String) Method of signing the transaction (only PLAINTEXT and SHA-MAC1 allowed for now)
@@ -248,7 +248,7 @@ class OAuthSimple {
throw new OAuthSimpleException ("Unknown signing method $method specified for OAuthSimple.setSignatureMethod");
break;
}
return $this;
}
@@ -259,8 +259,8 @@ class OAuthSimple {
*
* @param args (Array) hash of arguments for the call {action, path, parameters (array), method, signatures (array)} all arguments are optional.
* @return (Array) signed values
*/
public function sign($args=array())
*/
public function sign($args=array())
{
if (!empty($args['action']))
{
@@ -280,12 +280,11 @@ class OAuthSimple {
}
if (empty($args['parameters']))
{
$args['parameters']=array();
$args['parameters']=array();
}
$this->setParameters($args['parameters']);
$normParams = $this->_normalizedParameters();
$this->_parameters['oauth_signature'] = $this->_generateSignature($normParams);
return Array (
'parameters' => $this->_parameters,
'signature' => self::_oauthEscape($this->_parameters['oauth_signature']),
@@ -295,7 +294,7 @@ class OAuthSimple {
);
}
/**
/**
* Return a formatted "header" string
*
* NOTE: This doesn't set the "Authorization: " prefix, which is required.
@@ -303,9 +302,9 @@ class OAuthSimple {
* ways to do that.
*
* @param args (Array)
* @return $result (String)
* @return $result (String)
*/
public function getHeaderString ($args=array())
public function getHeaderString ($args=array())
{
if (empty($this->_parameters['oauth_signature']))
{
@@ -335,7 +334,7 @@ class OAuthSimple {
return preg_replace('/, $/','',$result);
}
private function _parseParameterString ($paramString)
private function _parseParameterString ($paramString)
{
$elements = explode('&',$paramString);
$result = array();
@@ -364,7 +363,7 @@ class OAuthSimple {
}
private static function _oauthEscape($string)
private static function _oauthEscape($string)
{
if ($string === 0) { return 0; }
if ($string == '0') { return '0'; }
@@ -372,21 +371,18 @@ class OAuthSimple {
if (is_array($string)) {
throw new OAuthSimpleException('Array passed to _oauthEscape');
}
$string = rawurlencode($string);
$string = urlencode($string);
//FIX: rawurlencode of ~
$string = str_replace('%7E','~', $string);
$string = str_replace('+','%20',$string);
$string = str_replace('!','%21',$string);
$string = str_replace('*','%2A',$string);
$string = str_replace('\'','%27',$string);
$string = str_replace('(','%28',$string);
$string = str_replace(')','%29',$string);
//FIX: urlencode of ~ and '+'
$string = str_replace(
Array('%7E','+' ), // Replace these
Array('~', '%20'), // with these
$string);
return $string;
}
private function _getNonce($length=5)
private function _getNonce($length=5)
{
$result = '';
$cLength = strlen($this->_nonce_chars);
@@ -396,22 +392,22 @@ class OAuthSimple {
$result .= substr($this->_nonce_chars,$rnum,1);
}
$this->_parameters['oauth_nonce'] = $result;
return $result;
}
private function _getApiKey()
private function _getApiKey()
{
if (empty($this->_secrets['consumer_key']))
{
throw new OAuthSimpleException('No consumer_key set for OAuthSimple');
}
$this->_parameters['oauth_consumer_key']=$this->_secrets['consumer_key'];
return $this->_parameters['oauth_consumer_key'];
}
private function _getAccessToken()
private function _getAccessToken()
{
if (!isset($this->_secrets['oauth_secret']))
{
@@ -422,69 +418,77 @@ class OAuthSimple {
throw new OAuthSimpleException('No access token (oauth_token) set for OAuthSimple.');
}
$this->_parameters['oauth_token'] = $this->_secrets['oauth_token'];
return $this->_parameters['oauth_token'];
}
private function _getTimeStamp()
private function _getTimeStamp()
{
return $this->_parameters['oauth_timestamp'] = time();
}
private function _normalizedParameters()
private function _normalizedParameters()
{
$normalized_keys = array();
$return_array = array();
foreach ( $this->_parameters as $paramName=>$paramValue) {
if (!preg_match('/\w+_secret/',$paramName) OR (strpos($paramValue, '@') !== 0 && !file_exists(substr($paramValue, 1))) )
foreach ( $this->_parameters as $paramName=>$paramValue) {
if (preg_match('/w+_secret/', $paramName) OR
$paramName == "oauth_signature") {
continue;
}
// Read parameters from a file. Hope you're practicing safe PHP.
if (strpos($paramValue, '@') !== 0 && !file_exists(substr($paramValue, 1)))
{
if (is_array($paramValue))
if (is_array($paramValue))
{
$normalized_keys[self::_oauthEscape($paramName)] = array();
foreach($paramValue as $item)
foreach($paramValue as $item)
{
array_push($normalized_keys[self::_oauthEscape($paramName)], self::_oauthEscape($item));
}
}
else
else
{
$normalized_keys[self::_oauthEscape($paramName)] = self::_oauthEscape($paramValue);
$normalized_keys[self::_oauthEscape($paramName)] = self::_oauthEscape($paramValue);
}
}
}
ksort($normalized_keys);
foreach($normalized_keys as $key=>$val)
foreach($normalized_keys as $key=>$val)
{
if (is_array($val))
if (is_array($val))
{
sort($val);
foreach($val as $element)
foreach($val as $element)
{
array_push($return_array, $key . "=" . $element);
}
}
else
else
{
array_push($return_array, $key .'='. $val);
}
}
}
$presig = join("&", $return_array);
$sig = $this->_generateSignature($presig);
$this->_parameters['oauth_signature']=$sig;
array_push($return_array, "oauth_signature=$sig");
return join("&", $return_array);
}
private function _generateSignature ($parameters="")
private function _generateSignature ($parameters="")
{
$secretKey = '';
if(isset($this->_secrets['shared_secret']))
{
$secretKey = self::_oauthEscape($this->_secrets['shared_secret']);
}
$secretKey .= '&';
if(isset($this->_secrets['oauth_secret']))
{
@@ -509,8 +513,8 @@ class OAuthSimple {
}
class OAuthSimpleException extends Exception {
public function __construct($err, $isDebug = FALSE)
public function __construct($err, $isDebug = FALSE)
{
self::log_error($err);
if ($isDebug)
@@ -518,12 +522,12 @@ class OAuthSimpleException extends Exception {
self::display_error($err, TRUE);
}
}
public static function log_error($err)
{
error_log($err, 0);
error_log($err, 0);
}
public static function display_error($err, $kill = FALSE)
{
print_r($err);
+71
View File
@@ -0,0 +1,71 @@
<?php
// Craptastic UNIT test for PHP OAuthSimple
require 'OAuthSimple.php';
$path = 'http://example.com/test';
$static_nonce = 'abcd123';
$static_time = 1234567890;
$signatures = array('consumer_key' => 'test_key',
'shared_secret' => 'test_secret',
'oauth_token' => 'access_key',
'oauth_secret' => 'access_secret');
$parameters = array(
'fruit'=>'bananas are <Awe+some!>',
'number'=>42,
// defining these here overrides the auto-generator.
'oauth_nonce'=>$static_nonce,
'oauth_timestamp'=>$static_time);
$oauth = new OAuthSimple();
$results = $oauth->sign(array('path'=>$path,
'parameters'=>$parameters,
'signatures'=>$signatures));
// ====
$expected = array(
'fruit'=>'bananas are <Awe+some!>',
'number'=>42,
'oauth_nonce'=>$static_nonce,
'oauth_timestamp'=>$static_time,
'oauth_consumer_key'=>$signatures['consumer_key'],
'oauth_token'=>$signatures['oauth_token'],
'oauth_signature_method'=>'HMAC-SHA1',
'oauth_version'=>1.0,
'oauth_signature'=>'IkTXsl3d/FV7uOY0p9CFFCxpdyQ=');
if ($results['parameters'] != $expected) {
print_r($results['parameters']);
throw new OAuthSimpleException("Failure: incorrect parameters returned");
}
// ====
$expected="IkTXsl3d%2FFV7uOY0p9CFFCxpdyQ%3D";
if ($results['signature'] != $expected) {
print $results['signature']."\n$expected\n";
throw new OAuthSimpleException("Failure: incorrect signature returned");
}
// ====
$expected="http://example.com/test?fruit=bananas%20are%20%3CAwe%2Bsome%21%3E&number=42&oauth_consumer_key=test_key&oauth_nonce=abcd123&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1234567890&oauth_token=access_key&oauth_version=1.0&oauth_signature=IkTXsl3d/FV7uOY0p9CFFCxpdyQ=";
if ($results['signed_url'] != $expected){
print $results['signed_url']."\n$expected\n";
throw new OAuthSimpleException("Failure: Invalid signed URL returned");
}
// ====
$expected='OAuth oauth_nonce="abcd123", oauth_timestamp="1234567890", oauth_consumer_key="test_key", oauth_token="access_key", oauth_signature_method="HMAC-SHA1", oauth_version="1.0", oauth_signature="IkTXsl3d%2FFV7uOY0p9CFFCxpdyQ%3D"';
if ($results['header'] != $expected) {
print $results['header']."\n$expected\n";
throw new OAuthSimpleException("Failure: Invalid Header returned");
}
// ====
$expected='GET&http%3A%2F%2Fexample.com%2Ftest&fruit%3Dbananas%2520are%2520%253CAwe%252Bsome%2521%253E%26number%3D42%26oauth_consumer_key%3Dtest_key%26oauth_nonce%3Dabcd123%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1234567890%26oauth_token%3Daccess_key%26oauth_version%3D1.0';
if ($results['sbs'] != $expected) {
print $results['sbs']."\n$expected\n";
throw new OAuthSimpleException("Failure: Invalid Base String returned");
}
print("ok\n");