3

I'm working on a Phonegap app (JS) anndn I'm confused how to pass along the CSRF token with my requests. Would I need to call user/login.json first and then store the token? For future requests how do I pull the token?

$('#page_dashboard').live('pageshow',function(){
  try {
    // Obtain session token.
    $.ajax({
        url:"http://" + website + "/?q=my_services/user/token.json",
        type:"post",
        dataType:"text",
        error:function (jqXHR, textStatus, errorThrown) {
            alert(errorThrown);
        },
        success: function (token) {
        // Call system connect with session token.
        $.ajax({
      url:"http://" + website + "/?q=my_services/system/connect.json",
      type: "post",
      dataType: "json",
      beforeSend: function (request) {
        request.setRequestHeader("X-CSRF-Token", token);
      },
      error: function (jqXHR, textStatus, errorThrown) {
        alert(errorThrown);
      },
      success: function (data) {
        var drupal_user = data.user;
        if (drupal_user.uid == 0) { // user is not logged in, show the login button, hide the logout button
            $('#button_login').show();
            $('#button_logout').hide();
            $('#button_create').hide();
        }
        else { // user is logged in, hide the login button, show the logout button
            document.getElementById("welcome").innerHTML = "<center>Welcome " + drupal_user.name + "</center>";
            $('#button_login').hide();
            $('#button_logout').show();
            $('#button_create').show();
        }
      }
    });
        }
    });
  }
 catch (error) { alert("page_dashboard - " + error); }
});
  • There's a working PHP implementation here, should be trivial to convert that to JavaScript if it helps...if the problem was just CORS all along, that'll be why you couldn't find any Drupal-specific docs to help you (it's not Drupal-related as such, docs will assume you know how to handle same origin already if you're consuming a REST service over AJAX) – Clive Jul 23 '14 at 9:03
  • CORS was one of my issues but the primary issue was not having the proper method of passing the token. Documentation for Services is very poor with active articles dated < 2009. – Ken J Jul 23 '14 at 9:38
  • I think you're looking in the wrong place - the documentation was last updated in 2012. Most of the other articles in the docs were updated 2012/2013 or more recently. Maybe you're looking at the Services 2.x instead of 3.x docs? Apart from the CSRF token (which is documented), Services hasn't really changed in that time, so new documentation is unlikely to be written. I personally think it's one of the better documented API modules out there. But maybe that just speaks to the state of the other modules, don't know. – Clive Jul 23 '14 at 9:43
  • It's worth noting that $("#button_login").show() sets display:inline (at least in my case) but you need display:block so you could do $("#loginBtn").css("display","block"); – Kartagis Sep 13 '15 at 9:23
8

I've been surprised by the lack of documentation and general information about Drupal RESTful services.

The best resources out there are from Tyler Frankenstein, the creator and maintainer of Drupalgap. Much thanks goes to Tyler as I started with his article here. Unfortunately, the documentation is rather old and no longer works with the current version of Services.

I highly recommend Drupalgap to any newbies that are looking to put a simple site on an Android/IOS app. It's a great tool that is well-maintained and constantly growing.

That being said....Drupalgap is great but ultimately I want the ability to expand beyond a framework requirement (i.e. Drupal). Understanding how JavaScript interacts with RESTful services is key to that point. After much effort I finally scraped together data from different sources and came up with a solution. Keep in mind that this worked for me. I'm sure there are better ways to do this but I only know this one.

Creating a PhoneGap app that Connects with Drupal 7 & Services 3.5+

My Environment:

Ubuntu 14.04
Cordova 3.5
Android SDK Manager 23
Apache 2.4.7
Drupal 7.28
Services 7.x-3.7
JQuery 1.11.1
jQuery Mobile 1.4.3

1) Setup your environment

Install Apache, Drupal, Phonegap, etc. I installed drupal to a sub-directory (localhost/drupal). Remember to setup your Services endpoint and enable resources for it. I use cordova to build the scaffolding for my Phonegap app:

 cordova create MyApp
 cd MyApp
 cordova platform add android
 cordova build

I have a symbolic link from my phonegap www folder to my apache root so I can test my mobile app on a browser before deploying.

2) Enable CORS for testing

Testing locally is a big requirement as the JavaScript console gives a developer so much more insight into what is actually happening with the code. Enabling CoRS was a requirement to get testing working. Getting CORS to function proper was a pain. Keep in mind that I'm testing from a local server (localhost) and CORS is configured to match, update your configuration accordingly:

1. Enable mod-headers,mod-setenvif
2. Add:
    Header add Access-Control-Allow-Origin "*"
   to the Apache Directory Directives (example.conf)
3. Add:
    SetEnvIf Origin "^(https?://localhost|https://[a-z]+\.my\.base\.domain)$" ORIGIN_SUB_DOMAIN=$1
    Header set Access-Control-Allow-Origin: "%{ORIGIN_SUB_DOMAIN}e" env=ORIGIN_SUB_DOMAIN
    Header set Access-Control-Allow-Methods "GET, PUT, POST, OPTIONS"
    Header set Access-Control-Allow-Credentials "true"
    Header set Access-Control-Allow-Headers "Authorization, Origin, Content-Type, X-CSRF-Token"
   to the .htaccess file at the root of your Drupal installation 
4. Reload/restart apache

3) Create jQuery Mobile app I followed Tyler's initial instructions and used jQuery Mobile to develop my application. The JavaScript was taken from a post in the Services issue queue here. This is a VERY basic app - just takes a login and outputs your username...but once you understand how the token is passed you can extend this to do ANY request.

Here's my index.html file:

<!DOCTYPE html>
<html>
    <head>
    <title>Hello World</title>
        <script src="js/jquery.min.js"></script>
        <link rel="stylesheet" href="css/jquery.mobile.css" />
        <script src="js/jquery.mobile.js"></script>
    </head>
    <body>
        <div data-role="page" id="page_dashboard"><!-- page -->
            <script type="text/javascript" charset="utf-8" src="script.js"></script>
            <div data-role="header"><h1>Home</h1></div>
            <div data-role="content"><!-- content -->
                <h2 align="center">Shopping List App</h2>
                <p id="welcome"></p>
            </div>
            <div>
                <label for="page_login_username">My Name</label>
                <input type="text" id="page_login_username" />
            </div>
            <div>
                <label for="page_login_name">Username</label>
                <input type="text" id="page_login_name" />
            </div>
            <div>
                <label for="page_login_pass">Password</label>
                <input type="password" id="page_login_pass" />
            </div>
            <input type="text" id="page_login_token" />
            <input type="hidden" id="page_login_session_ID" />
            <input type="hidden" id="page_login_session_name" />
            <fieldset>
                <div><button type="button" data-theme="b" id="page_login_submit">Login</button></div>
            </fieldset>
  </div>
        </div> <!-- end page -->
    </body>
</html>

Here's my script.js file:

$(document).ready(function(){

function setToken(){

// Get a Token from the site
$.ajax({
url: "http://localhost/drupal/my_services/user/token",
type: 'POST',
dataType: 'json',
//crossDomain: true,
error: function(XMLHttpRequest, textStatus, errorThrown) {
// Error getting Token
console.log('Get Token Failed');
console.log(JSON.stringify(XMLHttpRequest));
console.log(JSON.stringify(textStatus));
console.log(JSON.stringify(errorThrown));
},
success: function (data) {
// Set the Token Value in a textfield
$("#page_login_token").val( data.token );
}
}); // End Ajax
}

function doLogin( tokenid ){
var tokenValue = $(tokenid).val();
$.ajax({
url: "http://localhost/drupal/my_services/user/login",
type: 'POST',
dataType: 'json',
crossDomain: true,
data: {"username":$("#page_login_name").val(),"password":$("#page_login_pass").val()},

beforeSend: function (request) {
request.setRequestHeader("X-CSRF-Token", tokenValue);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log(JSON.stringify(XMLHttpRequest));
console.log(JSON.stringify(textStatus));
console.log(JSON.stringify(errorThrown));
},
success: function( userdata ){
$("#page_login_session_ID").val( userdata.sessid );
$("#page_login_session_name").val( userdata.session_name );
$("#page_login_username").val("Welkom SUPER" + userdata.user.name +"You are my Hero!");

// Get and Set a new Token after Login!!!
setToken();

}
});
}

function doLogout( tokenid, session_id, session_name ){

$.ajax({
url: "http://localhost/drupal/my_services/user/logout",
type: 'POST',
dataType: 'json',
crossDomain: true,
beforeSend: function (request) {
request.setRequestHeader("X-CSRF-Token", jQuery("#page_login_token").val());
},

error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log(JSON.stringify(XMLHttpRequest));
console.log(JSON.stringify(textStatus));
console.log(JSON.stringify(errorThrown));
},
success: function (data) {
$("#page_login_session_ID").val("");
$("#page_login_session_name").val("");
$("#page_login_username").html("");
$("#page_login_token").val("");
}

});

}

function testConnect( tokenid ){
var tokenValue = $(tokenid).val();
var gorge = tokenValue.substring(14);
$.ajax({
url: "http://localhost/drupal/my_services/system/connect",
type: 'POST',
dataType: 'json',
crossDomain: true,
beforeSend: function (request) {
request.setRequestHeader("X-CSRF-Token", tokenValue);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
//alert( 'Error with Connect.json' );
alert( gorge );
console.log( 'Error with Connect.json' );
console.log(JSON.stringify(XMLHttpRequest));
console.log(JSON.stringify(textStatus));
console.log(JSON.stringify(errorThrown));
},
success: function( data ){
console.log( data );
alert( data );
}
});
}


$(document).on("click", "#page_login_submit", function () {
    //alert("Goodbye!");
    setToken();
    //testConnect( "#page_login_token" );
    doLogin( "#page_login_token" );
});


});// End document ready

4) Test with Poster and Chrome

Firefox was a bit of a disappointment in the testing arena; except for Poster. This allows you to test Drupal Services directly by using Get/Post. Very handy for testing. Chrome developer tools came in very handy - they were the first to tell me what was actually wrong with my CORS configuration.

I hope this helps you! Feel free to add any comments/advice. I can update this in time and make it better.

| improve this answer | |
  • Thank you for the great write up. I'm curious, what limitations did you run into with DrupalGap? – tyler.frankenstein Aug 22 '14 at 19:35
  • thanks. so, you are getting a new token after login. You are never sending the sessionname/sessionid yourself - are you assuming this is carried by the http headers ? in subsequent calls, all we have to send is the csrf header ? – commonpike Oct 12 '14 at 9:35
  • login may fail with a http 406 unacceptable if the user is already logged in, you may want to catch that situation. – commonpike Oct 12 '14 at 9:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.