Drupal Services, REST Server and 3-legged OAuth

Step-by-Step Guide

In this tutorial I am going to show you how to create and retrieve nodes, upload files and access Views with Drupal Services module, REST server and 3-legged OAuth authentication.

1. To begin, you will need to download the following modules: 

CTools (7.x-1.2)
Devel (7.x-1.3)
OAuth (7.x-3.1)
Services (7.x-3.3)
Services Views (7.x-1.0-beta2+10-dev)
Views (7.x-3.5)

2. Extract the downloaded files into sites/all/modules directory, go to admin/modules and enable the following:

Chaos tools
OAuth
OAuth Provider UI
Services
Services Views
OAuth Authentication
REST Server
Views
Views UI

3. Go to Configuration->Web Services->OAuth (admin/config/services/oauth), make sure 'Enable the oauth provider' is checked, enter 7200 seconds for request token lifetime, and 'user/login' for login page.

4. Click on 'Add context' tab. Give this new context a title, for example 'Authentication'. Leave the default signature methods. Under 'Authorization Options' enter 'Login to @appname' as page title, and the following text as message:

Hi @user!

Click on the Login button to log in to @appname. This will give @appname access to your account (without disclosing your password).

'Deny access title' can be 'Cancel' and 'Grant access title' can be 'Log in'.

Access token lifetime can be left at 0.

Under 'Authorization Levels' you can for now set the name to 'default' and the title to 'Default Authorization Level'. Save these settings.

Several steps presented here, including this one, are based on an excellent tutorial available at http://www.electriceye.org.uk/node/98.

5. Now go to Structure->Services (admin/structure/services) and click on 'Add' to add a new endpoint.

Enter a machine-readable name, for example 'rest-service', select REST server and as path to endpoint enter 'rest'. Enable debug mode, but remember to disable it later, for production environment. Chose 'OAuth authentication' and save.

6. Click on 'Edit resources' for the endpoint you have just created. First go to the 'Authentication' tab. Under 'OAuth context' select the context you enabled in step 3 (Authentication). Under 'Default required OAuth Authorization level' select the 'Default Authorization Level' and under 'Default required authentication' select 'Consumer key and access token, also known as 3-legged OAuth'.

7. Now click on 'Resources' tab. Select 'node'. You can select the following CRUD operations: retrieve, create, update, delete, index. For this tutorial we need 'retrieve' and 'create'.

For each action you can select 'Required authentication' and 'Required authorization'. In this case, for the sake of simplicity, you can leave 'Default' for both values. Please note that authorization levels can be granular. In Step 3 you could have entered additional authorization levels. For example, you could have entered 'Create nodes on your behalf' authorization level, and then you could say that this is the required authorization for 'create' operation, while 'Default Authorization Level' would be required for ‘retrieve’ operation.

8. Click on the 'Server' tab. You can check all options and click on 'Save'. It is important to make sure that 'application/x-www-form-urlencoded' is checked since it is disabled by default.

9. Create a ‘developer’ user role. Assign the following permissions to this role:

Access own OAuth authorizations
Access own OAuth consumers

Add the following permissions to all authenticated users:

Register OAuth consumers in Authentication
Authorize OAuth consumers in Authentication

(Remember, 'Authentication' is the context you enabled in step 3.)

Also, under 'Services' check 'Save file information' for authenticated users. This last permission is important for step 13 below.

10. Login as a new user with the developer role. Go to 'My Account' and click on 'OAuth Consumers' tab. (Alternatively you can stay logged in as admin, go to new user’s account page and then to this tab.)

Click on 'Add Consumer'. This is similar to 'registering your application' with several non-Drupal sites, where you enter application name and callback URL and get a key and a secret value that you can use in your code.

Enter consumer name, callback URL and select the application context (there is only one, 'Authentication', which you created in step 3.)

For consumer name you can enter 'Test Application'.

Let’s say that your Drupal server is at http://mydrupalserver.com, that an application that will use this API is at http://myapp.com, and that the page you will use to test the application is at http://myapp.com/test.php. In this case under 'Callback URL' you should enter 'http://myapp.com/test.php'.

After you save the consumer, you will see the key and the secret value. Copy these values as you will need them in the code that uses the API.

Retrieving Nodes

11. Now let’s try to retrieve a node. For the sake of simplicity for now let’s presume you have only one content type called 'page' with only the title, body and URL path settings.

Use the following code in your test.php file:

<?php

//API urls

$req_url = 'http://mydrupalserver.com/oauth/request_token';

$authurl = 'http://mydrupalserver.com/oauth/authorize';

$acc_url = 'http://mydrupalserver.com/oauth/access_token';

 

//Your Application key and secret

$conskey = 'FASMSeZqsNJwDLzhPBNgV46vdxcCV8BT';

$conssec = 'nVsqAzqBTtWhwYsQzguZmd5Mw4bWWTnA';

 

//Your Application URL

$api_url = 'http://myapp.com/rest';

 

//Enable session.  We will store token information here later

session_start();

 

// state will determine the point in the authorization request our user is in

// In state=1 the next request should include an oauth_token.

// If it doesn't go back to 0

if(!isset($_GET['oauth_token']) && $_SESSION['state']==1) $_SESSION['state'] = 0;

 

try {

  //create a new Oauth request

  $oauth = new OAuth($conskey, $conssec, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);

  $oauth->enableDebug();

  //If this is a new request, request a new token with callback and direct user to allow/deny page

  if(!isset($_GET['oauth_token']) && !$_SESSION['state']) {

    $request_token_info = $oauth->getRequestToken($req_url);

    $_SESSION['secret'] = $request_token_info['oauth_token_secret'];

    $_SESSION['state'] = 1;

    header('Location: '.$authurl.'?oauth_token='.$request_token_info['oauth_token']);

    exit;

  //If this is a callback from allow/deny page, request the auth token and auth token secret codes

//and save them in session

  } else if($_SESSION['state']==1) {

    $oauth->setToken($_GET['oauth_token'], $_SESSION['secret']);

    $access_token_info = $oauth->getAccessToken($acc_url);

    $_SESSION['state'] = 2;

    $_SESSION['token'] = $access_token_info['oauth_token'];

    $_SESSION['secret'] = $access_token_info['oauth_token_secret'];

  } 

  $oauth->setToken($_SESSION['token'], $_SESSION['secret']);

 

   // URL should have the format "$api_url/node/[nid]".

  $oauth->fetch("$api_url/node/1");

 

  $json = json_decode($oauth->getLastResponse());

  print_r($json);

} catch(OAuthException $E) {

  print_r($E);

}

?>

 

This code is mostly based on http://www.php.net/manual/en/oauth.examples.fireeagle.php.

To test your application, open http://myapp.com/test.php in your Web browser. You should be redirected to your Drupal server, and if you are not logged in there, you will be asked for your user name and login information.

In this example, after we login as user demo-developer, we should see the following:

Please note that the title and the message text above is what you configured in step 4.

After you click on 'Log in', you will be redirected to your application page and it will display the requested node in Drupal object format, similar to this:

stdClass Object

(

    [vid] => 1

    [uid] => 1

    [title] => Test

    [log] => 

    [status] => 1

    [comment] => 0

    [promote] => 0

    [sticky] => 0

    [nid] => 1

    [type] => page

    [language] => und

    [created] => 1358864278

    [changed] => 1361195558

    [tnid] => 0

    [translate] => 0

    [revision_timestamp] => 1361195558

    [revision_uid] => 1

    [body] => stdClass Object

        (

            [und] => Array

                (

                    [0] => stdClass Object

                        (

                            [value] => This is a test page. 

                            [summary] => 

                            [format] => plain_text

                            [safe_value] => <p>This is a test page.</p>

 

                            [safe_summary] => 

                        )

 

                )

 

        )

 

    [name] => demo-user

    [picture] => 0

    [data] => a:2:{s:7:"contact";i:0;s:9:"timestamp";i:1361111428;}}

    [path] => http://mydrupalserver.com/test

)

 

Creating Nodes

12. Let’s try to create a node. Use the following code in your test.php file:

<?php

//API urls

$req_url = 'http://mydrupalserver.com/oauth/request_token';

$authurl = 'http://mydrupalserver.com/oauth/authorize';

$acc_url = 'http://mydrupalserver.com/oauth/access_token';

 

//Your Application key and secret

$conskey = 'FASMSeZqsNJwDLzhPBNgV46vdxcCV8BT';

$conssec = 'nVsqAzqBTtWhwYsQzguZmd5Mw4bWWTnA';

 

//Your Application URL

$api_url = 'http://myapp.com/rest';

 

//Enable session.  We will store token information here later

session_start();

 

// state will determine the point in the authorization request our user is in

// In state=1 the next request should include an oauth_token.

// If it doesn't go back to 0

if(!isset($_GET['oauth_token']) && $_SESSION['state']==1) $_SESSION['state'] = 0;

 

try {

  //create a new Oauth request

  $oauth = new OAuth($conskey, $conssec, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_AUTHORIZATION);

  $oauth->enableDebug();

  //If this is a new request, request a new token with callback and direct user to allow/deny page

  if(!isset($_GET['oauth_token']) && !$_SESSION['state']) {

    $request_token_info = $oauth->getRequestToken($req_url);

    $_SESSION['secret'] = $request_token_info['oauth_token_secret'];

    $_SESSION['state'] = 1;

    header('Location: '.$authurl.'?oauth_token='.$request_token_info['oauth_token']);

    exit;

  //If this is a callback from allow/deny page, request the auth token and auth token secret codes

//and save them in session

  } else if($_SESSION['state']==1) {

    $oauth->setToken($_GET['oauth_token'], $_SESSION['secret']);

    $access_token_info = $oauth->getAccessToken($acc_url);

    $_SESSION['state'] = 2;

    $_SESSION['token'] = $access_token_info['oauth_token'];

    $_SESSION['secret'] = $access_token_info['oauth_token_secret'];

  } 

  $oauth->setToken($_SESSION['token'], $_SESSION['secret']);
  

  $node_data = array(

    'type' => 'page',

    'title' => 'Testing',

    'language' => 'und',

    'body[und][0][value]' => 'This is another test',

    'body[und][0][format]' => 'plain_text',

  );
  

  $oauth->fetch("$api_url/node", $node_data, OAUTH_HTTP_METHOD_POST);

 

  $json = json_decode($oauth->getLastResponse());

  print_r($json);

} catch(OAuthException $E) {

  print_r($E);

}

?>

 

Login and authorization page should be the same as in the example above (however, please note that if you have already authenticated in step 11, you will not be required do it again in this step). Once you are redirected to your application it will return the following:

stdClass Object

(

    [nid] => 7

    [uri] => http://myapp.com/rest/node/7

)

 

with [nid] being the Node ID of the newly created node.

Please note the differences with the earlier example. Instead of OAUTH_AUTH_TYPE_URI we have OAUTH_AUTH_TYPE_AUTHORIZATION and in $oauth->fetch we add OAUTH_HTTP_METHOD_POST as the last parameter. 

Uploading Files

13. First add a new field to your 'page' content type. It should be a file field with allowed extension '.pdf'. For this example you can give it the machine name 'field_file'. Create a new pdf file with some sample text and - in order to keep it simple for now - save it in the same directory where your application is.

Use the following code in your test.php file:

<?php

//API urls

$req_url = 'http://mydrupalserver.com/oauth/request_token';

$authurl = 'http://mydrupalserver.com/oauth/authorize';

$acc_url = 'http://mydrupalserver.com/oauth/access_token';

 

//Your Application key and secret

$conskey = 'FASMSeZqsNJwDLzhPBNgV46vdxcCV8BT';

$conssec = 'nVsqAzqBTtWhwYsQzguZmd5Mw4bWWTnA';

 

//Your Application URL

$api_url = 'http://myapp.com/rest';

 

//Enable session.  We will store token information here later

session_start();

 

// state will determine the point in the authorization request our user is in

// In state=1 the next request should include an oauth_token.

// If it doesn't go back to 0

if(!isset($_GET['oauth_token']) && $_SESSION['state']==1) $_SESSION['state'] = 0;

 

try {

  //create a new Oauth request

  $oauth = new OAuth($conskey, $conssec, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_AUTHORIZATION);

  $oauth->enableDebug();

  //If this is a new request, request a new token with callback and direct user to allow/deny page

  if(!isset($_GET['oauth_token']) && !$_SESSION['state']) {

    $request_token_info = $oauth->getRequestToken($req_url);

    $_SESSION['secret'] = $request_token_info['oauth_token_secret'];

    $_SESSION['state'] = 1;

    header('Location: '.$authurl.'?oauth_token='.$request_token_info['oauth_token']);

    exit;

  //If this is a callback from allow/deny page, request the auth token and auth token secret codes

//and save them in session

  } else if($_SESSION['state']==1) {

    $oauth->setToken($_GET['oauth_token'], $_SESSION['secret']);

    $access_token_info = $oauth->getAccessToken($acc_url);

    $_SESSION['state'] = 2;

    $_SESSION['token'] = $access_token_info['oauth_token'];

    $_SESSION['secret'] = $access_token_info['oauth_token_secret'];

  } 

  $oauth->setToken($_SESSION['token'], $_SESSION['secret']);
  

  $filename = 'test.pdf';

  $file_data = array(

    'filesize' => filesize($filename),

    'filename' => basename($filename),

    'file' => base64_encode(file_get_contents($filename)),

    'filepath' => 'public://' . basename($filename) ,

  );
  

  $oauth->fetch("$api_url/file", $file_data, OAUTH_HTTP_METHOD_POST);

 

  $json = json_decode($oauth->getLastResponse());

  print_r($json);

} catch(OAuthException $E) {

  print_r($E);

}

?>

 

Again, login and authorization page should be the same as in the examples above, and once you are redirected to your application it should return the following:

stdClass Object

(

    [fid] => 6

    [uri] => http://myapp.com/rest/node/6

)

 

With [fid] being the Drupal file ID of the uploaded file.

14. How can we add this file to a node? The easiest way is to first open an existing page that has a file attachment, and click on the 'devel' tab. There you can expand object and see array 'field_file'.

You will see that it contains field_file['und'][0]['fid'] with the value of the fid of the included file.

So, in the node creation code in step 12 above, find the line 

'body[und][0][format]' => 'plain_text',

 

and under it add

'field_file[und][0][fid]' => 6,

 

(fid of the file you uploaded). Please note how the position of single quotation marks changed.

This code section now looks like this:

$node_data = array(

    'type' => 'page',

    'title' => 'Testing',

    'language' => 'und',

    'body[und][0][value]' => 'This is another test',

    'body[und][0][format]' => 'plain_text',

    'field_file[und][0][fid]' => 6,

  );

 

The file you have previously uploaded will be added to the node you created.

Please note that a similar approach can be used for any fields that the content type may have. Go to the 'devel' tab and see the contents of this field's array, and then recreate the key elements in the request. This may require some trial and error.

Accessing Views

15. Finally, we are going to look at the ways to access Drupal Views via Services and the REST server.

Go to admin/structure/views/add and add new view. For simplicity, show content of type "Page", sorted by newest first and do not create either page or block. Click on 'Continue and edit' and add a 'Services' display. You can choose unformatted list and only Content: Nid and Content: Title fields. Under 'Services Settings' enter the path, for example '/nodelist', and save the view.

Now edit the resurces for the rest-service you created earlier by going to admin/structure/services/list/rest_service/resources and check the 'nodelist' resource. There is only one available operation, 'index'. 

In the PHP code that you had for retrieving nodes in step 11, change the line

$oauth->fetch("$api_url/node/1");

 

into

$oauth->fetch("$api_url/nodelist");

 

so that the test.php file looks like this:

<?php

//API urls

$req_url = 'http://mydrupalserver.com/oauth/request_token';

$authurl = 'http://mydrupalserver.com/oauth/authorize';

$acc_url = 'http://mydrupalserver.com/oauth/access_token';

 

//Your Application key and secret

$conskey = 'FASMSeZqsNJwDLzhPBNgV46vdxcCV8BT';

$conssec = 'nVsqAzqBTtWhwYsQzguZmd5Mw4bWWTnA';

 

//Your Application URL

$api_url = 'http://myapp.com/rest';

 

//Enable session.  We will store token information here later

session_start();

 

// state will determine the point in the authorization request our user is in

// In state=1 the next request should include an oauth_token.

// If it doesn't go back to 0

if(!isset($_GET['oauth_token']) && $_SESSION['state']==1) $_SESSION['state'] = 0;

 

try {

  //create a new Oauth request

  $oauth = new OAuth($conskey, $conssec, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);

  $oauth->enableDebug();

  //If this is a new request, request a new token with callback and direct user to allow/deny page

  if(!isset($_GET['oauth_token']) && !$_SESSION['state']) {

    $request_token_info = $oauth->getRequestToken($req_url);

    $_SESSION['secret'] = $request_token_info['oauth_token_secret'];

    $_SESSION['state'] = 1;

    header('Location: '.$authurl.'?oauth_token='.$request_token_info['oauth_token']);

    exit;

  //If this is a callback from allow/deny page, request the auth token and auth token secret codes and save them in session

  } else if($_SESSION['state']==1) {

    $oauth->setToken($_GET['oauth_token'], $_SESSION['secret']);

    $access_token_info = $oauth->getAccessToken($acc_url);

    $_SESSION['state'] = 2;

    $_SESSION['token'] = $access_token_info['oauth_token'];

    $_SESSION['secret'] = $access_token_info['oauth_token_secret'];

  } 

  $oauth->setToken($_SESSION['token'], $_SESSION['secret']);
  

  $oauth->fetch("$api_url/nodelist");

 

  $json = json_decode($oauth->getLastResponse());

  print_r($json);

} catch(OAuthException $E) {

  print_r($E);

}

?>

 

After the authorization, the code returned to your application will be similar to this:

Array

(

    [0] => stdClass Object

        (

            [nid] => 7

            [node_title] => Testing

        )

 

    [1] => stdClass Object

        (

            [nid] => 6

            [node_title] => Sample page

        )

 

    [2] => stdClass Object

        (

            [nid] => 5

            [node_title] => Test

        )

 

)

 

Now that you have the nid for each node, you can use

$oauth->fetch("$api_url/node/[nid]");

 

to retrieve information about that node.

Please note that when using Services and OAuth Drupal permissions are fully respected. You can create a new page via the REST server only if you have authenticated as a user who has permissions to create such a node. You can see the view in the last example only if the user you have authenticated as can see this view.

Sure hope that helps you get going on this quickly!