phpcurloauthyahoohmacsha1

Yahoo API and the dreaded oauth_problem="signature_invalid"


I have read various other threads on here and cannot seem to find one that actually gives a convincing answer.

I have followed the docs at https://developer.yahoo.com/oauth/guide/oauth-accesstoken.html all the way through and now have me a nice set of credentials and a user id for whoever has logged in. Up until now I have been using https and the plaintext method of signature.

Now I wish to receive some info about the user and have to use the HMACS-SHA1 method of signing my request.

Basically, like many others on here I am getting the Please provide valid credentials. OAuth oauth_problem="signature_invalid", realm="yahooapis.com" " error message.

This is my code:

function getYahooUser($userID, $oauthToken, $oauthSecret)
{
   // $url = 'https://social.yahooapis.com/v1/user/' . $userID .'/profile?';
    $ch = curl_init();

    $s =  'oauth_consumer_key='.config::yahooConsumerKey.'&';
    $s .= 'oauth_nonce='.generateRandomString().'&';
    $s .= 'oauth_signature_method=HMAC-SHA1&';
    $s .= 'oauth_timestamp='.time().'&';
    $s .= 'oauth_token='.$oauthToken.'&';
    $s .= 'oauth_version=1.0&';
    $s .= 'realm=yahooapis.com';

    $baseString ='GET&'.rawurlencode('https://social.yahooapis.com/v1/user/'.$userID.'/profile').'&'.rawurlencode($s);
    $signingKey = rawurlencode(config::yahooConsumerSecret).'&'.rawurlencode($oauthSecret);
    $signature = rawurlencode(base64_encode(hash_hmac('sha1', $baseString, $signingKey, true)));



    curl_setopt_array($ch, array(
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => TRUE,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_SSL_VERIFYHOST => 0,
        CURLOPT_HTTPGET => true,
        CURLOPT_POST       => false,
        CURLOPT_URL => 'https://social.yahooapis.com/v1/user/'.$userID.'/profile'.
            '?realm=yahooapis.com'.
            '&oauth_consumer_key='.config::yahooConsumerKey.
            '&oauth_nonce='.generateRandomString().
            '&oauth_signature_method=HMAC-SHA1'.
            '&oauth_timestamp='.time().
            '&oauth_token='.$oauthToken.
            '&oauth_version=1.0'.
            '&oauth_signature='.$signature
    ));

$output = curl_exec($ch);
    var_dump($output);

    return $output;

}

does anyone have an idea of what I am doing wrong?


Solution

  • Seriously, the yahoo api is shocking....

    Ok solved it using a combination of Joe Chungs oauth examples available at https://github.com/joechung/oauth_yahoo and this post from the yahoo developers network https://developer.yahoo.com/forum/OAuth-General-Discussion-YDN-SDKs/token-rejected/1259915145000-8648ab55-f852-38ed-91c9-bf7e37f7d76c

    The trick is to url decode the auth token you get from the gettoken request before adding it in your signature creation.

    This is my complete yahoo function, if you are having the same problem then just include your variables where I have my config::params

    main function:

    function doYahoo() //returns a url for the user to click to start auth
    {
    
        require('Yahoo/YahooCurl.php');
        new YahooCurl;
        $yahooReply = getYahooRequestToken();
        $yahooReplyToArray = explode("&", $yahooReply);
        $yahooOauthToken = $yahooReplyToArray[0];
        $yahooOauthToken = substr($yahooOauthToken, 12);
    
        $yahooOauthTokenSecret = $yahooReplyToArray[1];
        $yahooOauthTokenSecret = substr($yahooOauthTokenSecret,19);
    
    
        $expire = time() + 60 * 60 * 24 * 7;
        setcookie("yahooSecret", $yahooOauthTokenSecret, $expire, "/", null);
    
        $YahooURL = 'https://api.login.yahoo.com/oauth/v2/request_auth?oauth_token=' . $yahooOauthToken;
        return $YahooURL;
    
    }
    
    if(isset($_GET['yahoo']))
    {
    
        $yahoooAuthToken = $_GET['oauth_token'];
        $yahoooAuthVerifier = $_GET['oauth_verifier'];
    
        require('Yahoo/YahooCurl.php');
        new YahooCurl;
    
        $yahooReply = getYahooAccessToken($yahoooAuthVerifier, $yahoooAuthToken, $_COOKIE['yahooSecret']);
        $yahooReplyToArray = explode("&", $yahooReply);
        $yahoooAuthToken = $yahooReplyToArray[0];
        $yahoooAuthToken = substr($yahoooAuthToken,12);
        $yahoooAuthToken = urldecode($yahoooAuthToken);
        $yahoooAuthTokenSecret = $yahooReplyToArray[1];
        $yahoooAuthTokenSecret = substr($yahoooAuthTokenSecret,19);
        $YahooUserID = $yahooReplyToArray[5];
        $YahooUserID = substr($YahooUserID,18);
        $YahooRefreshToken = $yahooReplyToArray[3];
        $YahooRefreshToken = substr($YahooRefreshToken,21);
    
        $expire = time() + 60 * 60 * 24 * 7;
        setcookie("yahooRefresh", $YahooRefreshToken, $expire, "/", null);
    
        $yahooUserData = getYahooUser($YahooUserID,$yahoooAuthToken,$yahoooAuthTokenSecret);
    
        //do what you want with $yahooUserData
    }
    

    and YahooCurl.php

    class YahooCurl
    {
        public $YahooRequestToken;
    }
    
    function getYahooRequestToken()
    {
    
        $params = array(
            'oauth_nonce' => urlencode(generateRandomString()),
            'oauth_timestamp' => time(),
            'oauth_consumer_key' => config::yahooConsumerKey,
            'oauth_signature_method' => 'plaintext',
            'oauth_signature' => config::yahooConsumerSecret . '%26',
            'oauth_version' => '1.0',
            'oauth_callback' => config::yahooCallBackDomain);
    
        $url = 'https://api.login.yahoo.com/oauth/v2/get_request_token';
        $postData = '';
    
    
        foreach ($params as $k => $v) {
            $postData .= $k . '=' . $v . '&';
        }
        rtrim($postData, '&');
    
        //var_dump($postData);
    
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_POST, count($postData));
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
    
        $output = curl_exec($ch);
        curl_close($ch);
        return $output;
    }
    
    function generateRandomString($length = 6)
    {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[rand(0, strlen($characters) - 1)];
        }
        return $randomString;
    }
    
    function getYahooAccessToken($oauthVerifier, $oauthToken, $oauthSecret)
    {
    
        $url = 'https://api.login.yahoo.com/oauth/v2/get_token?oauth_consumer_key=' .urlencode(config::yahooConsumerKey) .
            '&oauth_signature_method=' . urlencode('plaintext') .
            '&oauth_version=' . urlencode('1.0') .
            '&oauth_verifier=' . urlencode($oauthVerifier) .
            '&oauth_token=' . urlencode($oauthToken) .
            '&oauth_timestamp=' . urlencode(time()) .
            '&oauth_nonce=' .urlencode(generateRandomString()) .
            '&oauth_signature=' . config::yahooConsumerSecret . '%26' . $oauthSecret;
    
        $ch = curl_init();
    
        curl_setopt($ch,CURLOPT_URL,$url);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
    //  curl_setopt($ch,CURLOPT_HEADER, false);
    
        $output=curl_exec($ch);
    
        curl_close($ch);
        return $output;
    }
    
    function getYahooUser($userID, $oauthToken, $oauthSecret)
    {
    
        $ch = curl_init();
    
        $url = 'https://social.yahooapis.com/v1/user/'. $userID . '/profile';
        $params['oauth_consumer_key'] = config::yahooConsumerKey;
        $params['oauth_nonce'] = generateRandomString();
        $params['oauth_signature_method'] = 'HMAC-SHA1';
        $params['oauth_timestamp'] = time();
        $params['oauth_token'] = $oauthToken;
        $params['oauth_version'] = '1.0';
        $params['oauth_signature'] =
            oauth_compute_hmac_sig('GET', $url, $params,
                config::yahooConsumerSecret, $oauthSecret);
    
        $query_parameter_string = oauth_http_build_query($params);
        $request_url = $url . ($query_parameter_string ?
                ('?' . $query_parameter_string) : '' );
    
    
        curl_setopt($ch, CURLOPT_URL, $request_url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    
        $output = curl_exec($ch);
        var_dump($output);
    
        return $output;
    
    }
    
        function oauth_compute_hmac_sig($http_method, $url, $params, $consumer_secret, $token_secret)
        {
    
            $base_string = signature_base_string($http_method, $url, $params);
            $signature_key = rfc3986_encode($consumer_secret) . '&' . rfc3986_encode($token_secret);
            $sig = base64_encode(hash_hmac('sha1', $base_string, $signature_key, true));
            return $sig;
        }
    
    function oauth_http_build_query($params, $excludeOauthParams=false)
    {
        $query_string = '';
        if (! empty($params)) {
    
            // rfc3986 encode both keys and values
            $keys = rfc3986_encode(array_keys($params));
            $values = rfc3986_encode(array_values($params));
            $params = array_combine($keys, $values);
    
            // Parameters are sorted by name, using lexicographical byte value ordering.
            // http://oauth.net/core/1.0/#rfc.section.9.1.1
            uksort($params, 'strcmp');
    
            // Turn params array into an array of "key=value" strings
            $kvpairs = array();
            foreach ($params as $k => $v) {
                if ($excludeOauthParams && substr($k, 0, 5) == 'oauth') {
                    continue;
                }
                if (is_array($v)) {
                    // If two or more parameters share the same name,
                    // they are sorted by their value. OAuth Spec: 9.1.1 (1)
                    natsort($v);
                    foreach ($v as $value_for_same_key) {
                        array_push($kvpairs, ($k . '=' . $value_for_same_key));
                    }
                } else {
                    // For each parameter, the name is separated from the corresponding
                    // value by an '=' character (ASCII code 61). OAuth Spec: 9.1.1 (2)
                    array_push($kvpairs, ($k . '=' . $v));
                }
            }
    
            // Each name-value pair is separated by an '&' character, ASCII code 38.
            // OAuth Spec: 9.1.1 (2)
            $query_string = implode('&', $kvpairs);
        }
    
        return $query_string;
    }
    
    function rfc3986_encode($raw_input)
    {
        if (is_array($raw_input)) {
            return array_map('rfc3986_encode', $raw_input);
        } else if (is_scalar($raw_input)) {
            return str_replace('%7E', '~', rawurlencode($raw_input));
        } else {
            return '';
        }
    }
    
    function signature_base_string($http_method, $url, $params)
    {
        // Decompose and pull query params out of the url
        $query_str = parse_url($url, PHP_URL_QUERY);
        if ($query_str) {
            $parsed_query = oauth_parse_str($query_str);
            // merge params from the url with params array from caller
            $params = array_merge($params, $parsed_query);
        }
    
        // Remove oauth_signature from params array if present
        if (isset($params['oauth_signature'])) {
            unset($params['oauth_signature']);
        }
    
        // Create the signature base string. Yes, the $params are double encoded.
        $base_string = rfc3986_encode(strtoupper($http_method)) . '&' .
            rfc3986_encode(normalize_url($url)) . '&' .
            rfc3986_encode(oauth_http_build_query($params));
    
    
    
        return $base_string;
    }
    
    function normalize_url($url)
    {
        $parts = parse_url($url);
    
        $scheme = $parts['scheme'];
        $host = $parts['host'];
        $port = '443';
        $path = $parts['path'];
    
        if (! $port) {
            $port = ($scheme == 'https') ? '443' : '80';
        }
        if (($scheme == 'https' && $port != '443')
            || ($scheme == 'http' && $port != '80')) {
            $host = "$host:$port";
        }
    
        return "$scheme://$host$path";
    }
    
    
    function oauth_parse_str($query_string)
    {
        $query_array = array();
    
        if (isset($query_string)) {
    
            // Separate single string into an array of "key=value" strings
            $kvpairs = explode('&', $query_string);
    
            // Separate each "key=value" string into an array[key] = value
            foreach ($kvpairs as $pair) {
                list($k, $v) = explode('=', $pair, 2);
    
                // Handle the case where multiple values map to the same key
                // by pulling those values into an array themselves
                if (isset($query_array[$k])) {
                    // If the existing value is a scalar, turn it into an array
                    if (is_scalar($query_array[$k])) {
                        $query_array[$k] = array($query_array[$k]);
                    }
                    array_push($query_array[$k], $v);
                } else {
                    $query_array[$k] = $v;
                }
            }
        }
    
        return $query_array;
    }
    

    Hope this helps someone.