Drupal Services 3 Login and CSRF
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 &amp; 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:

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


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.