This page describes how to use a two legged OAuth with Services 3.x and OAuth 7.x.

Why would you want to do this?

  • You may want a mobile application access to resources, but not require a login from the user
WARNINGS:
  • At the time of this posting there is no beta release for OAuth 7.x. I am currently using a version from Git.
  • This is a very basic tutorial - it does not currently cover authorization levels / specific permissions.
SETTING UP:

    Create a context

  • Create a context at http://yoursite/admin/config/services/oauth/contexts
  • Ensure that HMAC-SHA1 is a selected Signature method

    Create a user

  • Create a dummy Drupal user at admin/people/create (This is so you can create a consumer. There may be a better way to do this without the need to create a new Drupal user)
  • Create a consumer for your user, at user/<uid>/oauth/consumer/add. I used the same name as my context name, I'm not sure it matters in this instance.
  • Grab the key and secret for your newly created consumer
RUNNING THE SCRIPT

Alot of this code is taken and modified from the oauth.test unit tests found inside the OAuth.module itself.
Note that the call in accessTokenRequest returns nothing if the token validation was successful. If there was a problem, the header would return a 401 (access denied).

NOTE

: You will need to replace the $url_prefix to match your website.

<?php
$url_prefix = 'http://127.0.0.1/yoursite/';


function print_nice($print) {
  print("<pre>");
  print_r($print);
  print("</pre>");
}

function requestTokenRequest($consumer, $method = 'HMAC-SHA1') {
  //$url = $this->getAbsoluteUrl('oauth/request_token');
  global $url_prefix;
  $url = $url_prefix . 'oauth/request_token';
  
  if (substr($method, 0, 4) == 'HMAC') {
    $signature_method = new OAuthSignatureMethod_HMAC(strtolower(substr($method, 5)));
  }

  $request = DrupalOAuthRequest::from_consumer_and_token($consumer, NULL, 'POST', $url, NULL);
  $request->sign_request($signature_method, $consumer, NULL);

  $fields['signature'] = $request->get_parameter('oauth_signature');
  $data = $request->to_postdata();

  print_nice($request->get_parameters());
  
  $options = array(
    CURLOPT_POST => TRUE, 
    CURLOPT_RETURNTRANSFER => TRUE,
    CURLOPT_URL => $url,
    CURLOPT_POSTFIELDS => $data,
    // CURLOPT_HEADER => true,
    CURLOPT_VERBOSE => true,
  );

  $ch = curl_init();
	curl_setopt_array($ch, $options);
  $result = curl_exec($ch);
  
  echo 'curlinfo request_token:::';
  print (curl_getinfo($ch, CURLINFO_HTTP_CODE));

  return $result;
}
  
  
function accessTokenRequest($consumer, $tokens, $type = 'access') {
  //$url = $this->getAbsoluteUrl('oauth/access_token');
  $method = 'HMAC-SHA1';
  $url = '';
  global $url_prefix;
  
  if ($type == 'verify') {
    $url = $url_prefix . 'oauth/test/valid-access-token';
  }
  else {
    $url = $url_prefix . 'oauth/access_token';
  }
  
  if (substr($method, 0, 4) == 'HMAC') {
    $signature_method = new OAuthSignatureMethod_HMAC(strtolower(substr($method, 5)));
  }

  $token = new DrupalOAuthToken($tokens['oauth_token'], $tokens['oauth_token_secret'], $consumer);

  $request = DrupalOAuthRequest::from_consumer_and_token($consumer, $token, 'POST', $url, NULL);
  $request->sign_request($signature_method, $consumer, $token);

  $fields['signature'] = $request->get_parameter('oauth_signature');
  $data = $request->to_postdata();
  print_nice($request->get_parameters());
  
  $options = array(
    CURLOPT_POST => TRUE, 
    CURLOPT_RETURNTRANSFER => TRUE,
    CURLOPT_URL => $url,
    CURLOPT_POSTFIELDS => $data,
    // CURLOPT_HEADER => true,
    CURLOPT_VERBOSE => true,
  );

  $ch = curl_init();
	curl_setopt_array($ch, $options);
  $result = curl_exec($ch);
  
  echo 'curlinfo access_token:::';
  echo curl_getinfo($ch, CURLINFO_HTTP_CODE);

  return $result;
}
  
  
  
  
  
$_SERVER['HTTP_HOST'] = 'localhost';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
define('DRUPAL_ROOT', dirname(realpath(__FILE__)));
include_once 'includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

// MATCH YOUR CONSUMER KEY/SECRET
$consumer = new DrupalOAuthConsumer('sws4hKQZSfCuP3ShfymCkWMH2FmCsWTW', '4BQyhys5UakETENY5k6vUg3n7MtRqoe9', array());

$result = requestTokenRequest($consumer);
print_nice($result);

$split = explode('&', $result);
foreach ($split as $line) {
  $token_split = explode('=', $line);
  $tokens[$token_split[0]] = $token_split[1];
}
print_nice('```` requested tokens ````');
print_nice($tokens);

$result = accessTokenRequest($consumer, $tokens, 'verify');
$results = explode('&', $result);
foreach ($results as $line) {
  $split = explode('=', $line);
  $access_token[$split[0]] = $split[1];
}
print_nice('```` token result ````');
print_nice($result);


// NOW, ANY SUBSEQUENT REQUESTS CAN USE THESE TOKENS (the ```` requested tokens `````)



Comments

mykmallett’s picture

COMMENTS IN THE CODE! This is supposed to be documentation!

batje’s picture

This seems a nice start for a very interesting usecase. Questions:

- Where does this code go? In what file?
- Why is this code not in a module?

spoco2’s picture

This is a start, gets me to a point of having a php page that shows some oauth information, but stops short of doing anything useful.

Does not say where to put this php file (I just dropped it in the drupal root to see what it did, and it produces), or how to use this php page, or anything really.

bonn’s picture

I put the file on my drupal 7 root,

modified lines into these:

$url_prefix = 'http://core.zeus.lan/';

$_SERVER['HTTP_HOST'] = 'client1.zeus.lan';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';

BUT I ALWAYS GET curlinfo access_token:::401... How to fix that???

Array
(
    [oauth_version] => 1.0
    [oauth_nonce] => b370b8a6937f8f3ae45ff8620e004da3
    [oauth_timestamp] => 1352650692
    [oauth_consumer_key] => yeoQt7L4ZoPcvwkQQfy6LPYE7kxVxkcm
    [oauth_signature_method] => HMAC-SHA1
    [oauth_signature] => y7Dn3DjYmHlYARnxTh02OqMBexI=
)

curlinfo request_token:::200

oauth_token=bzNfEuqc8emLHymBFUUtDgpRVm4CBwMz&oauth_token_secret=2XfTE8B73CMuepPDSdFQCnrdCMCickya

```` requested tokens ````

Array
(
    [oauth_token] => bzNfEuqc8emLHymBFUUtDgpRVm4CBwMz
    [oauth_token_secret] => 2XfTE8B73CMuepPDSdFQCnrdCMCickya
)

Array
(
    [oauth_version] => 1.0
    [oauth_nonce] => 97518b4f302f97f49f617cf4f1b0393e
    [oauth_timestamp] => 1352650692
    [oauth_consumer_key] => yeoQt7L4ZoPcvwkQQfy6LPYE7kxVxkcm
    [oauth_token] => bzNfEuqc8emLHymBFUUtDgpRVm4CBwMz
    [oauth_signature_method] => HMAC-SHA1
    [oauth_signature] => CMPN/6xo7AZFNPbj6qB8zjXilpc=
)

curlinfo access_token:::401

```` token result ````
murrayw’s picture

I too was having that problem and it was very frustrating. I would suggest making sure that your authentication has been set up properly. In particular check that you have a context set and that default required auth is set as well.

admin/structure/services/list/MY_ENDPOINT/authentication

bonn’s picture

I've solved it using oauth php extension. Here's the documentation that I wrote: http://drupal.org/node/1839550

murrayw’s picture

I've been going through this in some more detail to work out what has been going wrong. I'll put my notes down here as they may be of help to some of you out there.

Firstly, the 401 is a strange beast. I tracked it down to a problem with creating a new access token, once the request token had been created. See line 127 in DrupalOAuthDataStore.inc.

if ($token_old && $token_old->authorized) {

A check is done for $token_old->authorized. I think this is always going to be false from a quick look at the code. I'm not sure how this conditional will ever be satisfied. The result is that the request token will never be upgraded to an access token. The method then throws an error resulting in the 401. If you comment out the authorized bit then it works. I'm not condoning this :) of course, just identifying where I was having an issue.

Secondly, once you get the access token coming back you must then use that to sign future requests. The example code above is a little misleading, as developers may well be submitting the old request tokens. This results in a "Token not found" error. So, to fill in some of the gaps above try:

$result = accessTokenRequest($consumer, $tokens, 'access');
$results = explode('&', $result);
foreach ($results as $line) {
  $split = explode('=', $line);
  $access_token[$split[0]] = $split[1];
}
print_nice('```` token result ````');
print_nice($access_token);

And then use $access_token to sign the next request.

Also, I found $request->to_url() helpful for getting a signed URL for a subsequent GET request.

Hopefully, this is helpful to someone. I still need to investigate the seriousness of the $token_old->authorized bit above. Maybe I am missing something obvious LOL.

aparnakondala1’s picture

foreach ($split as $line) {
$token_split = explode('=', $line);
$token_split[0]= preg_replace('/\s+/', '', $token_split[0]);
$tokens[$token_split[0]] = $token_split[1];
}