简体   繁体   中英

Can not authenticate user with Twitter V1.1 API

I've implemented everything up through Creating a Signature . I created a function to collect the required parameters (I've added some comments here for clarity):

function collect_parameters(){
    global $credentials; // This is an Object with my App's credentials and stuff

    $oAuth = get_user_oauth();  // This returns an object with the the personal user OAuth tokens retrieved from the earlier docs.
    $encoded_collection = array();

    $collection = array(
        'status'                 => rawurlencode( $_GET['tweet'] ),
        'include_entities'       => 'true',
        'oauth_consumer_key'     => $credentials->key,
        'oauth_nonce'            => $credentials->nonce, // md5( str_shuffle( uniqid() . mt_rand(0,9999999999) ) )
        'oauth_signature_method' => 'HMAC-SHA1',
        'oauth_timestamp'        => $credentials->time, // current timestamp
        'oauth_token'            => $oAuth->oauth_token,
        'oauth_version'          => '1.0',
    );

    // Percent encode every key and value that will be signed.
    foreach( $collection as $key => $value ){
        $encoded_collection[rawurlencode($key)] = rawurlencode($value); 
    }

    // Sort the list of parameters alphabetically by encoded key.
    ksort( $encoded_collection );

    return http_build_query( $encoded_collection );
}

I use this function to build the Signature Base String

function create_signature_base_string( $parameter_string, $url = 'https://api.twitter.com/1.1/statuses/update.json', $method = 'POST' ){
    return strtoupper( $method ) .'&'. rawurlencode( $url ) .'&'. rawurlencode( $parameter_string );
}

I use this function to calculate the signature

function calculate_signature( $signature_base_string, $signing_key ){
    return base64_encode( hash_hmac('sha1', $signature_base_string, $signing_key, true) );
}

Now to building the OAuth Header. here's a function for it, that uses the helper functions from above (plus some others that return the required information):

function get_oauth_headers(){
    global $credentials;
    $oAuth = get_user_oauth();
    
    $parameters = collect_parameters();
    $signature_base_string = create_signature_base_string( $parameters );
    $signing_key = get_signing_key();
    $signature = calculate_signature( $signature_base_string, $signing_key );

    $auth_array = array(
        'oauth_consumer_key'     => $credentials->key,
        'oauth_nonce'            => $credentials->nonce,
        'oauth_signature'        => $signature,
        'oauth_signature_method' => 'HMAC-SHA1',
        'oauth_timestamp'        => $credentials->time,
        'oauth_token'            => $oAuth->oauth_token,
        'oauth_version'          => '1.0'
    );

    ksort( $auth_array );

    return $auth_array;
}

Now I've got everything in a nice & neat little array, it's time to actually try and send this to Twitter.

function create_tweet( $build_query = true ){
    global $credentials;

    $ch = curl_init();

    $url    = 'https://api.twitter.com/1.1/statuses/update.json';
    $fields = array(
        'status' => rawurlencode( $_GET['tweet'] ) // I've just been using "Test" or "WhyNoWork" style text in this $_GET param
    );

    $oAuth_headers = get_oauth_headers(); // This uses that function above that returns all of the specific parameters for OAuth, sorted, and ready to go.
    $oAuth_array   = array();

    // Loop through the oauth headers, and encode them
    foreach( $oAuth_headers as $key => $value ){
        $oAuth_array[] = rawurlencode($key) .'="'. rawurlencode($value) .'"';
    }

    // Implode it into a single line
    $oAuth_string = implode(', ', $oAuth_array );

    $headers = array(
        'Content-Type: application/x-www-form-rawurlencoded',
        'Authorization: OAuth '. $oAuth_string,
    );

    curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
    curl_setopt( $ch, CURLOPT_ENCODING, 'gzip' );
    curl_setopt( $ch, CURLOPT_POST, true );
    
    // It seems to prefer this as a query string instead of postfields?
    if( $build_query == true ){
        curl_setopt( $ch, CURLOPT_URL, $url.'?'.http_build_query($fields) );
    } else {
        curl_setopt( $ch, CURLOPT_URL, $url );
        curl_setopt( $ch, CURLOPT_POSTFIELDS, $fields );
    }

    $result = curl_exec( $ch );
    $info   = curl_getinfo( $ch );

    curl_close( $ch );

    if( isset($_GET['debug']) ){
        echo $result;
        var_dump( $info );
    } else {
        echo $result;
    }
}

For example, here's the order of everything in the OAuth header. I've run through each of my little helper functions a dozen times making sure they take in the right arguments and output the appropriate values. I've even replaced my own OAuth credentials with the ones from the docs, and end up with the same results they do for the signing key, signature, etc.:

OAuth 标头

Yet, everytime I try and run the create_tweet() function, I get a 401 status code with error 32: {"errors":[{"code":32,"message":"Could not authenticate you."}]} . What the heck am I missing? Is it possible to see why they couldn't authenticate the request?


Here's the output from collect_parameters() ; include_entities=true&oauth_consumer_key=APP_API_KEY&oauth_nonce=ABC123&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1597456781&oauth_token=USER_AUTH_TOKEN&oauth_version=1.0&status=TESTING

That is passed to the Signature Base String function, which returns the following:

POST&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fstatuses%2Fupdate.json&include_entities%3Dtrue%26oauth_consumer_key%3DAPP_API_KEY%26oauth_nonce%3DABC123%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1597457034%26oauth_token%3DUSER_AUTH_TOKEN%26oauth_version%3D1.0%26status%3DTESTING

That looks good, and now I take the signing key: APP_SECRET&USER_AUTH_SECRET and pass those to calculate the signature gives me a value just like the one in the Docs (and using the params in the docs gives me the same signature that they show): thIsiSeEmSUnNecEssArYPOs3OxQdSNpI=

I don't understand how I can replace my data with the test data and get the same result, but I still can't get an API Request authenticated?

You are performing a couple of extra encodings.

First, in collect_parameters you are encoding keys and values for the $encoded_collection array and then passing that to http_build_query which will further encode them. You can completely remove the loop to encode items and instead pass them directly to http_build_query . The trick there is that it defaults to + encoding, so you need to tell it to switch to % encoding using the fourth parameter:

function collect_parameters()
{
    global $credentials; // This is an Object with my App's credentials and stuff

    $oAuth = get_user_oauth();  // This returns an object with the the personal user OAuth tokens retrieved from the earlier docs.
    
    $collection = [
        'status' => 'Hello Ladies + Gentlemen, a signed OAuth request!',
        'include_entities' => 'true',
        'oauth_consumer_key' => $credentials->key,
        'oauth_nonce' => $credentials->nonce, // md5( str_shuffle( uniqid() . mt_rand(0,9999999999) ) )
        'oauth_signature_method' => 'HMAC-SHA1',
        'oauth_timestamp' => $credentials->time, // current timestamp
        'oauth_token' => $oAuth->oauth_token,
        'oauth_version' => '1.0',
    ];

    // Sort the list of parameters alphabetically by encoded key.
    ksort($collection);

    return http_build_query($collection, '', '&', PHP_QUERY_RFC3986);
}

Next, in your create_tweet function, in the first loop, you are encoding both keys and values again which isn't needed and can be removed:

    foreach ($oAuth_headers as $key => $value) {
        $oAuth_array[] = $key . '="' . $value . '"';
    }

I unfortunately don't have a Twitter account to test all of this, but their docs have sample keys that I was able to use and a sample output, and using that these changes and the docs produced the same output.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM