{"id":222,"date":"2013-08-11T20:30:01","date_gmt":"2013-08-11T20:30:01","guid":{"rendered":"http:\/\/thomasgr.im\/shares\/?p=222"},"modified":"2014-05-12T17:02:17","modified_gmt":"2014-05-12T17:02:17","slug":"making-app-with-phonegap-jqm-part-3-stateless-authentication-layer","status":"publish","type":"post","link":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/","title":{"rendered":"Making An App With PhoneGap &#038; jQm Part III: A Stateless Authentication Layer"},"content":{"rendered":"<p>If you missed <a href=\"http:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/\" title=\"Making An App With PhoneGap &#038; jQm Part II: Loading JavaScript Properly\">Part II: Loading JavaScript Properly<\/a>, you may want to go there and grab the code we wrote so far so that you can follow more easily what we&#8217;ll do in this part.<\/p>\n<p>Today, we&#8217;ll see how to implement a <abbr title=\"REpresentational State Transfer\">REST<\/abbr>ful, stateless authentication layer in our application.&nbsp;Let&#8217;s first talk a bit about <a href=\"http:\/\/en.wikipedia.org\/wiki\/Representational_state_transfer\u200e\">REST<\/a>; the acronym stands for &#8220;<em>REpresentational State Transfer<\/em>&#8221; and describes a way of handling client-server transactions in a uniform, layered, stateless, scalable and cacheable way.&nbsp;I know that&#8217;s a lot of adjectives to process so I won&#8217;t delve into much more details here.&nbsp;We&#8217;ll focus on the <em>stateless<\/em> constraint, which requires that <strong>the exchanges have no memory per se<\/strong>, they always contain all the information that the other party has to know to process them correctly.<\/p>\n<div style=\"border:1px solid;margin:10px 0px;padding:15px 10px 15px 50px;background-repeat:no-repeat;background-position:10px center;color:#00529B; background-color:#BDE5F8;background-image:url('\/wp-content\/themes\/thomasgr.im\/images\/Knob Info.png');\">I have tried to simplify my code examples as much as possible.&nbsp;The goal being &#8220;<em>stateless authentication<\/em>&#8220;, I have focused on that, which means that for now we will deal with &#8220;<em>ugly<\/em>&#8221; URLs like https:\/\/foo.bar\/server.php?a=b&#038;id=123; we&#8217;ll see in a while how to do the same with &#8220;<em>clean<\/em>&#8221; URLs like https:\/\/foo.bar\/users\/bob<\/div>\n<p>The REST philosophy is widely considered one of the best ways to solve communication challenges between a client and a server over the Internet.&nbsp;You may want to read the linked Wikipedia article for more details about the how and why.<\/p>\n<p>Now what we&#8217;re interested in is the design of a system in which we can &#8220;<em>phone home<\/em>&#8221; from our application to an online server.&nbsp;This is needed for applications which have to pull data from a remote host like news items or score boards, or on the contrary push data to a remote host like creating a new account or updating profile information.&nbsp;Obviously <strong>such a system must allow for authentication<\/strong>, or we could end up with users accessing or modifying other users&#8217; data at will!<\/p>\n<h2>Phoning Home (Cross-Domain) With jQuery<\/h2>\n<p>Phoning home is surprisingly easy with PhoneGap &#038; jQuery, and at the same time it can prove hard to do it right.&nbsp;What I mean is that on the one hand jQuery provides us with powerful tools like <a href=\"http:\/\/api.jquery.com\/jQuery.ajax\/\u200e\" target=\"_blank\"><kbd>jQuery.ajax()<\/kbd><\/a>, which we can leverage in order to query our servers; on the other hand, there is a bunch of things we need to do so that our query is not blocked by the anti-cross-domain security features embedded in web browsers like Chrome.<\/p>\n<p><!--more--><\/p>\n<p>Thanks to jQuery, the client code is rather straightforward; we simply need to add a function like this to our <kbd>App<\/kbd> namespace:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\t&quot;servers&quot;: {\r\n\t\t&quot;public&quot;: {\r\n\t\t\t&quot;URL&quot;: &quot;https:\/\/foo.bar\/public.server.php&quot;,\r\n\t\t\t&quot;query&quot;: function (action, data, callback) {\r\n\t\t\t\tconsole.log(&quot;&#x5B;public.query]&quot;);\r\n\t\t\t\tjQuery.ajax(App.servers.public.URL, {\r\n\t\t\t\t\t&quot;type&quot;: &quot;GET&quot;,\r\n\t\t\t\t\t&quot;dataType&quot;: &quot;json&quot;,\r\n\t\t\t\t\t&quot;data&quot;: {&quot;action&quot;: action, &quot;data&quot;: data},\r\n\t\t\t\t\t&quot;contentType&quot;: &quot;application\/json&quot;,\r\n\t\t\t\t\t&quot;success&quot;: callback\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t}\r\n\t}<\/pre>\n<p>As for the server, you can hack something quite quickly in pure PHP:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">&lt;?php\r\nfunction successJSON ($content, $die=true) {\r\n\twrapJSON('SUCCESS', $content, $die);\r\n}\r\nfunction errorJSON ($content, $die=true) {\r\n\twrapJSON('ERROR', $content, $die);\r\n}\r\nfunction wrapJSON ($code, $content, $die=true) {\r\n\t\/\/ encode content as {foo:bar,foobar:baz}\r\n\t$content = json_encode((object)$content);\r\n\t\/\/ strip the first character '{' from the json encoded string\r\n\t$content = substr($content, 1, strlen($content)-1);\r\n\t\/\/ add the &quot;code&quot; part to the string\r\n\t$buffer = '{&quot;code&quot;:&quot;'.$code.'&quot;,'.$content;\r\n\r\n\techo $buffer;\r\n\tif ($die) {\r\n\t\tdie;\r\n\t}\r\n}\r\n\r\n\/\/ empty request?\r\nif (empty($_REQUEST&#x5B;'action'])) {\r\n\terrorJSON(array(&quot;message&quot; =&gt; &quot;empty_request&quot;));\r\n}\r\n\r\nswitch ($_REQUEST&#x5B;'action']) {\r\n\tcase 'test':\r\n\t\t$data = 'yeepee';\r\n\t\tif (!empty($_REQUEST&#x5B;'data']&#x5B;'foo'])) {\r\n\t\t\t$data = $_REQUEST&#x5B;'data']&#x5B;'foo'];\r\n\t\t}\r\n\t\tsuccessJSON(array('foo' =&gt; $data));\r\n\t\tbreak;\r\n\tdefault:\r\n\t\terrorJSON(array(&quot;message&quot; =&gt; &quot;access_denied&quot;));\r\n\t\tbreak;\r\n}<\/pre>\n<p>Change the <kbd>URL<\/kbd> content in your client code and you can test the function from anywhere in your application:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">App.servers.public.query(&quot;test&quot;, {&quot;foo&quot;:&quot;bar&quot;}, function (response) {\r\nif (response &amp;&amp; response.code) {\r\n\tif (response.code === 'SUCCESS') {\r\n\t\tPGproxy.navigator.notification.alert(response.foo);\r\n\t} else {\r\n\t\tconsole.log(&quot;error while phoning home!&quot;);\r\n\t}\r\n});\r\n\/\/ should result in an alert box saying &quot;bar&quot;<\/pre>\n<p>I wrote &#8220;<em>should<\/em>&#8221; specifically because if you try it locally (on your <abbr title=\"Mac Apache MySQL PHP\">MAMP<\/abbr>\/<abbr title=\"Linux Apache MySQL PHP\">LAMP<\/abbr>\/<abbr title=\"Windows Apache MySQL PHP\">WAMP<\/abbr> stack) with a remote server, it&#8217;s actually probable that it won&#8217;t work as expected.&nbsp;Here&#8217;s the thing: querying cross-domains from PhoneGap on a smartphone is typically not a problem, but doing so on your desktop system can be, due to browsers&#8217; security policies.<\/p>\n<p>To fix this, we have to (1) <strong>make the server tell the client it allows it<\/strong> to perform queries from a remote origin, and (2) <strong>make jQuery and jQuery.Mobile know they are allowed<\/strong> to perform cross-domain calls.<\/p>\n<p>First, simply add the following lines to the top of your PHP file:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">header(&quot;Access-Control-Allow-Origin: *&quot;);\r\nheader('Access-Control-Allow-Methods: POST, GET, OPTIONS');\r\nheader('Access-Control-Max-Age: 1000');\r\nheader('Access-Control-Allow-Headers: Content-Type');\r\nheader('Content-Type: application\/javascript');\r\nif ($_REQUEST&#x5B;'method'] === 'OPTIONS') {\r\n\techo '1';\r\n\tdie;\r\n}<\/pre>\n<p>What does this code do? Basically, <kbd>jQuery.ajax()<\/kbd> will always start by pinging the remote URL with an <kbd>HTTP OPTIONS<\/kbd> request and checking the response headers; if it detects <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/HTTP\/Access_control_CORS#The_HTTP_response_headers\"><kbd>Access-Control-*<\/kbd><\/a> headers and in particular <kbd>Access-Control-Origin<\/kbd> matches the source domain, then it will perform the &#8220;real&#8221; query.&nbsp;We die early if the request method is &#8220;<em><kbd>OPTIONS<\/kbd><\/em>&#8221; in order to prevent our script from processing the same data twice!<\/p>\n<p>Then, make sure you have those lines in your jQuery.Mobile configuration code:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\t\t\tjQuery.support.cors = true;\r\n\t\t\tjQuery.mobile.allowCrossDomainPages = true;<\/pre>\n<p>Now you should be able to run the example call successfully on both your desktop browser and your Android smartphone!<\/p>\n<h2>Stateless Authentication Using Public\/Private Tokens<\/h2>\n<p>Now that&#8217;s great and all, but how about sensible queries like updating a user&#8217;s account information or posting things in his name? As I wrote earlier, surely we can&#8217;t use this public server queries implementation, or we&#8217;d allow anyone to mess with our system!<\/p>\n<p>In order to solve this problem, we&#8217;ll use an additional server script named <kbd>private.server.php<\/kbd>, which will only accept authenticated queries.&nbsp;Users will authenticate themselves using an email\/password, like they do on most websites.&nbsp;The REST philosophy then requires us to pass authenticating information inside all our requests, as opposed to, say, using session cookies.&nbsp;We&#8217;ll use private\/public tokens to <strong>securely authenticate the user across requests to our <abbr title=\"Application Programming Interface\">API<\/abbr> without passing its login information<\/strong>, which is very sensible in essence, in every request.&nbsp;<\/p>\n<p>Here&#8217;s how the login process works:<\/p>\n<dl>\n<dt>App: Present the user a login form<\/dt>\n<dd>User enters his\/her login info (email\/pass, username\/pass, etc.)<\/dd>\n<dd>Send the login information to <kbd>public.server.php<\/kbd><\/dd>\n<dt>Server: Find user in the database based on the login info<\/dt>\n<dd>Retrieve his\/her public &#038; private tokens<\/dd>\n<dd>Send the two tokens back to the app<\/dd>\n<dt>App: Store the two tokens locally for future reference (using localStorage)<\/dt>\n<\/dl>\n<p>Having those two tokens, we can authenticate every subsequent query that needs so:<\/p>\n<dl>\n<dt>App: Construct a hash from the private token and another value, say the timestamp<\/dt>\n<dd>We&#8217;ll use the <kbd>sha1()<\/kbd> algorithm: <kbd>hash = sha1(private_token + timestamp)<\/kbd><\/p>\n<dt>App: Send a query to <kbd>private.server.php<\/kbd> passing additional variables<\/dt>\n<dd>We&#8217;ll need hash, timestamp and public_token added to action &#038; data<\/dd>\n<dd>We&#8217;ll automate this using an additional query method: <kbd>App.servers.private.query<\/kbd><\/dd>\n<dt>Server: Find user in the database from the public token<\/dt>\n<dd>We actually only need the private_token field from our users table<\/dd>\n<dd>Construct the same hash from the (retrieved) private token and the (passed) timestamp<\/dd>\n<dd>Compare (reconstructed) hash to (passed) hash. If the two match, the query is authenticated<\/dd>\n<\/dl>\n<h2>Stateless Exchanges While Logged In<\/h2>\n<p>Let&#8217;s assume we&#8217;re logged in.&nbsp;As we&#8217;ve seen, this means that we have stored our public and private tokens on the mobile device.&nbsp;Now all we have to do is create a brand new <kbd>App.servers.private.query()<\/kbd> method, which we will use for every authenticated query.&nbsp;I&#8217;ve actually written a wrapper method called <kbd>App.servers.query<\/kbd>, which allows us to have cleaner code.&nbsp;Here is the whole thing; take your time reading it, it should be quite easy to understand but please do not hesitate to post a comment if you need me to explain anything:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">&quot;servers&quot;: {\r\n\t&quot;query&quot;: function (url, type, data, callback) { console.log(&quot;&#x5B;query &quot;+url+&quot;]&quot;);\r\n\t\tjQuery.ajax(url, {\r\n\t\t\t&quot;type&quot;: type,\r\n\t\t\t&quot;dataType&quot;: &quot;json&quot;,\r\n\t\t\t&quot;data&quot;: data,\r\n\t\t\t&quot;contentType&quot;: (type===&quot;GET&quot; ? &quot;application\/json&quot; : &quot;application\/x-www-form-urlencoded&quot;),\r\n\t\t\t&quot;success&quot;: callback\r\n\t\t});\r\n\t},\r\n\t&quot;public&quot;: {\r\n\t\t&quot;URL&quot;:  &quot;https:\/\/foo.bar\/public\/&quot;,\r\n\t\t\/\/ perform unauthenticated query\r\n\t\t&quot;query&quot;: function (action, data, callback) { console.log(&quot;&#x5B;public.query]&quot;);\r\n\t\t\tApp.servers.query(App.servers.public.URL+action, &quot;GET&quot;, {&quot;data&quot;: data}, callback);\r\n\t\t}\r\n\t},\r\n\t&quot;private&quot;: {\r\n\t\t&quot;URL&quot;: &quot;https:\/\/foo.bar\/private\/&quot;,\r\n\t\t\/\/ perform authenticated query\r\n\t\t&quot;query&quot;: function (action, data, callback) { console.log(&quot;&#x5B;private.query]&quot;);\r\n\t\t\tif (!localStorage || !localStorage.getItem(&quot;token_public&quot;) || !localStorage.getItem(&quot;token_private&quot;)) {\r\n\t\t\t\tconsole.log(&quot;can't do private query: empty private\/public tokens!&quot;);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\t\/\/ used as seed for hashing the private token\r\n\t\t\tvar timestamp = Math.round(+new Date()\/1000);\r\n\t\t\tApp.servers.query(App.servers.private.URL+action, &quot;POST&quot;, {\r\n\t\t\t\t\t&quot;timestamp&quot;: timestamp,\r\n\t\t\t\t\t&quot;token_public&quot;: localStorage.getItem(&quot;token_public&quot;),\r\n\t\t\t\t\t&quot;hash&quot;: sha1(timestamp+localStorage.getItem(&quot;token_private&quot;)),\r\n\t\t\t\t\t&quot;data&quot;: data\r\n\t\t\t}, callback);\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>That&#8217;s it, basically: once you&#8217;re logged in, you can run whatever type of query you want in a very simple, unobstrusive way:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">App.servers.public.query('deposit_cash', {&quot;account&quot;:1,&quot;amount&quot;:&quot;\u20ac4,321.00&quot;}, function (result) { \/* ... *\/ });\r\nApp.servers.private.query('wire_money', {&quot;account&quot;:2,&quot;amount&quot;:&quot;\u20ac1,234.00&quot;}, function (result) { \/* ... *\/ });<\/pre>\n<p>As for the private server, here&#8217;s what you could have set up:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n\/\/ reject incomplete requests\r\nif (empty($_REQUEST&#x5B;'token_public']) || empty($_REQUEST&#x5B;'hash']) || empty($_REQUEST&#x5B;'timestamp'])) {\r\n\terrorJSON(array('message' =&gt; &quot;empty_tokens&quot;));\r\n}\r\n\r\n\/\/ try to find a user from the public token we've been sent (die if not found)\r\ntry {\r\n\t$User = User::find('first', array('conditions' =&gt; array('token_public = ?', $_REQUEST&#x5B;'token_public'])));\r\n} catch (Exception $e) { errorJSON(array('message' =&gt; &quot;invalid_tokens&quot;, &quot;e&quot; =&gt; $e-&gt;getMessage())); }\r\n\/\/ make sure the hash hasn't been tampered\r\nif ($_REQUEST&#x5B;'hash'] !== sha1($_REQUEST&#x5B;'timestamp'].$User-&gt;token_private)) {\r\n\terrorJSON(array('message' =&gt; &quot;invalid_tokens&quot;));\r\n}\r\n\r\n\/\/ try and parse any additional data we've been sent\r\nif (!empty($_REQUEST&#x5B;'data'])) {\r\n\tparse_str($_REQUEST&#x5B;'data'], $_REQUEST&#x5B;'data']);\r\n}\r\n\r\nswitch ($_REQUEST&#x5B;'action']) {\r\n\tcase 'check_tokens':\r\n\t\t\/\/ if we've arrived this far, it's all good\r\n\t\tsuccessJSON(array('message' =&gt; &quot;all_good&quot;));\r\n\t\t\/\/ shouldn't happen\r\n\t\tbreak;\r\n\tcase 'wire_money':\r\n\t\t\/\/ &#x5B;...]\r\n\t\t\/\/ &#x5B;...]\r\n\t\tsuccessJSON(array('message' =&gt; &quot;tranfer_order_placed&quot;));\r\n\t\t\/\/ shouldn't happen\r\n\t\tbreak;\r\n\tdefault:\r\n\t\terrorJSON(array('message' =&gt; &quot;invalid_action&quot;));\r\n\t\t\/\/ shouldn't happen\r\n\t\tbreak;\r\n}<\/pre>\n<h2>How To Authenticate For The First Time<\/h2>\n<p>Now how about logging in?&nbsp;Let&#8217;s start slowly with the addition of two fields in the <kbd>users<\/kbd> table: <kbd>public_token<\/kbd> and <kbd>private_token<\/kbd>, both as <kbd>VARCHAR(40)<\/kbd>, <kbd>NOT NULL<\/kbd> and with an index so that we can retrieve their associated record quickly later on.&nbsp;You will then have to generate the two tokens in a random way upon registration, and I encourage you to generate new ones regularly.<\/p>\n<p>Here&#8217;s a <kbd>App.handleLogin<\/kbd> function launched when the user submits the login form:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">&quot;handleLogin&quot;: function () { console.log(&quot;&#x5B;handleLogin]&quot;);\r\n\tvar m = jQuery(&quot;#input-Login_mail&quot;).val();\r\n\tvar p = jQuery(&quot;#input-Login_pass&quot;).val();\r\n\r\n\tif (m.length === 0 || p.length === 0) {\r\n\t\tconsole.log(&quot;! empty fields&quot;);\r\n\t\tPGproxy.navigator.notification.alert(&quot;empty fields&quot;, function() {});\r\n\t\treturn false;\r\n\t}\r\n\tApp.servers.public.query(&quot;login&quot;, {\r\n\t\t&quot;mail&quot;: m,\r\n\t\t&quot;pass&quot;: p\r\n\t}, App.callbacks.handleLogin, true);\r\n\treturn false;\r\n}<\/pre>\n<p>&#8230; and finally the <kbd>App.callbacks.handleLogin()<\/kbd> callback:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">&quot;handleLogin&quot;: function(r) { console.log(&quot;&#x5B;callback: handleLogin]&quot;);\r\n\tif (r &amp;&amp; r.code &amp;&amp; r.code === &quot;SUCCESS&quot; &amp;&amp; r.token_private &amp;&amp; r.token_public) {\r\n\t\tconsole.log(&quot;user is now logged in!&quot;);\r\n\t\tlocalStorage.setItem(&quot;token_private&quot;, r.token_private);\r\n\t\tlocalStorage.setItem(&quot;token_public&quot;, r.token_public);\r\n\t\tlocalStorage.setItem(&quot;id&quot;, r.id); \/\/ you can store whatever other data you want\r\n\t\treturn;\r\n\t} else {\r\n\t\tPGproxy.navigator.notification.alert(&quot;login failed&quot;, function() {});\r\n\t}\r\n}<\/pre>\n<p>Those functions were rather simple and intuitive, the most interesting part to my mind comes from the servers and their handling of the requests.&nbsp;Here&#8217;s a rough example in <kbd>PHP<\/kbd> of the code could have in your <strong>public server<\/strong>:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">switch ($_REQUEST&#x5B;'action'])\r\n{\r\n\tcase 'login':\r\n\t\t\/\/ exit early if obviously invalid fields\r\n\t\tif (empty($_REQUEST&#x5B;'data']&#x5B;'mail']) || empty($_REQUEST&#x5B;'data']&#x5B;'pass'])) {\r\n\t\t\terrorJSON(array('message' =&gt; &quot;invalid_fields&quot;));\r\n\t\t}\r\n\t\t\/\/ get corresponding user or exit early\r\n\t\ttry {\r\n\t\t\t$User = User::find('first', array('conditions' =&gt; array(\r\n\t\t\t\t&quot;email = ? AND password = ?&quot;, $_REQUEST&#x5B;'data']&#x5B;'mail'], sha1(SOME_SALT.$_REQUEST&#x5B;'data']&#x5B;'pass'])\r\n\t\t\t)));\r\n\t\t} catch (RecordNotFound $e) { }\r\n\t\tif (empty($User)) {\r\n\t\t\terrorJSON(array('message' =&gt; &quot;invalid_credentials&quot;));\r\n\t\t}\r\n\r\n\t\t\/\/ send the tokens back to the app &amp; die\r\n\t\tsuccessJSON(array(\r\n\t\t\t&quot;token_private&quot; =&gt; $User-&gt;token_private,\r\n\t\t\t&quot;token_public&quot; =&gt; $User-&gt;token_public,\r\n\t\t\t&quot;id&quot; =&gt; $User-&gt;id\r\n\t\t));\r\n\t\t\/\/ shouldn't reach this\r\n\t\tbreak;\r\n\t}\r\n}<\/pre>\n<div style=\"border:1px solid;margin:10px 0px;padding:15px 10px 15px 50px;background-repeat:no-repeat;background-position:10px center;color:#00529B; background-color:#BDE5F8;background-image:url('\/wp-content\/themes\/thomasgr.im\/images\/Knob Info.png');\">Note that here I&#8217;m using a very simple <kbd>ORM<\/kbd> called <a href=\"http:\/\/www.phpactiverecord.org\/\">PHP ActiveRecord<\/a> to manage my models and their relations.&nbsp;Feel free to use anything else, as long as it suits you and sends queries in a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Prepared_statement\">secure way<\/a>!<\/div>\n<\/p>\n<h2>Wrapping Up<\/h2>\n<p>Phew, that was a long post.&nbsp;I hope that these snippets and explanations will be somewhat useful to you; if you would like me to give more details on something specific please do tell me in the comments or send me a mail through the contact form, I&#8217;ll try my best to reply.&nbsp;This public\/private tokens system is <strong>quite simple to handle<\/strong> once you&#8217;ve grasped the concepts around it, it is <strong>reasonably secure<\/strong> and is completely <strong>platform independant<\/strong>.&nbsp;I wanted to include a section on how to add even more security features to it like using a user-specific salt instead of a global one or implementing a retry limit, however the article was becoming way too long so I&#8217;ll talk about it in another part &#59;) Thanks for reading!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you missed Part II: Loading JavaScript Properly, you may want to go there and grab the code we wrote so far so that you can follow more easily what we&#8217;ll do in this part. Today, we&#8217;ll see how to implement a RESTful, stateless authentication layer in our application.&nbsp;Let&#8217;s first talk a bit about REST; &#8230;<a class=\"post-readmore\" href=\"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/\">read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[10,2],"tags":[21,12,13,14,15,11,48,20],"class_list":["post-222","post","type-post","status-publish","format-standard","hentry","category-mobile","category-programming","tag-api","tag-javascript","tag-jquery","tag-jquery-mobile","tag-oop","tag-phonegap","tag-php","tag-rest"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Making An App With PhoneGap &amp; jQm Part III: A Stateless Authentication Layer - ThomasGR.IM<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Making An App With PhoneGap &amp; jQm Part III: A Stateless Authentication Layer - ThomasGR.IM\" \/>\n<meta property=\"og:description\" content=\"If you missed Part II: Loading JavaScript Properly, you may want to go there and grab the code we wrote so far so that you can follow more easily what we&#8217;ll do in this part. Today, we&#8217;ll see how to implement a RESTful, stateless authentication layer in our application.&nbsp;Let&#8217;s first talk a bit about REST; ...read more\" \/>\n<meta property=\"og:url\" content=\"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/\" \/>\n<meta property=\"og:site_name\" content=\"ThomasGR.IM\" \/>\n<meta property=\"article:published_time\" content=\"2013-08-11T20:30:01+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2014-05-12T17:02:17+00:00\" \/>\n<meta name=\"author\" content=\"Thomas\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\\\/\"},\"author\":{\"name\":\"Thomas\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/#\\\/schema\\\/person\\\/a9f452898a055ceb0f17967802c852da\"},\"headline\":\"Making An App With PhoneGap &#038; jQm Part III: A Stateless Authentication Layer\",\"datePublished\":\"2013-08-11T20:30:01+00:00\",\"dateModified\":\"2014-05-12T17:02:17+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\\\/\"},\"wordCount\":2482,\"commentCount\":33,\"keywords\":[\"API\",\"JavaScript\",\"jQuery\",\"jQuery.Mobile\",\"OOP\",\"PhoneGap\",\"PHP\",\"REST\"],\"articleSection\":[\"Mobile\",\"Programming\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\\\/\",\"url\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\\\/\",\"name\":\"Making An App With PhoneGap & jQm Part III: A Stateless Authentication Layer - ThomasGR.IM\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/#website\"},\"datePublished\":\"2013-08-11T20:30:01+00:00\",\"dateModified\":\"2014-05-12T17:02:17+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/#\\\/schema\\\/person\\\/a9f452898a055ceb0f17967802c852da\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Making An App With PhoneGap &#038; jQm Part III: A Stateless Authentication Layer\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/#website\",\"url\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/\",\"name\":\"ThomasGR.IM\",\"description\":\"Geeky Musing\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/#\\\/schema\\\/person\\\/a9f452898a055ceb0f17967802c852da\",\"name\":\"Thomas\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g\",\"caption\":\"Thomas\"},\"sameAs\":[\"https:\\\/\\\/thomasgr.im\\\/\"],\"url\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/author\\\/thomadmin\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Making An App With PhoneGap & jQm Part III: A Stateless Authentication Layer - ThomasGR.IM","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/","og_locale":"en_US","og_type":"article","og_title":"Making An App With PhoneGap & jQm Part III: A Stateless Authentication Layer - ThomasGR.IM","og_description":"If you missed Part II: Loading JavaScript Properly, you may want to go there and grab the code we wrote so far so that you can follow more easily what we&#8217;ll do in this part. Today, we&#8217;ll see how to implement a RESTful, stateless authentication layer in our application.&nbsp;Let&#8217;s first talk a bit about REST; ...read more","og_url":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/","og_site_name":"ThomasGR.IM","article_published_time":"2013-08-11T20:30:01+00:00","article_modified_time":"2014-05-12T17:02:17+00:00","author":"Thomas","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/#article","isPartOf":{"@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/"},"author":{"name":"Thomas","@id":"https:\/\/thomasgr.im\/shares\/#\/schema\/person\/a9f452898a055ceb0f17967802c852da"},"headline":"Making An App With PhoneGap &#038; jQm Part III: A Stateless Authentication Layer","datePublished":"2013-08-11T20:30:01+00:00","dateModified":"2014-05-12T17:02:17+00:00","mainEntityOfPage":{"@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/"},"wordCount":2482,"commentCount":33,"keywords":["API","JavaScript","jQuery","jQuery.Mobile","OOP","PhoneGap","PHP","REST"],"articleSection":["Mobile","Programming"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/","url":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/","name":"Making An App With PhoneGap & jQm Part III: A Stateless Authentication Layer - ThomasGR.IM","isPartOf":{"@id":"https:\/\/thomasgr.im\/shares\/#website"},"datePublished":"2013-08-11T20:30:01+00:00","dateModified":"2014-05-12T17:02:17+00:00","author":{"@id":"https:\/\/thomasgr.im\/shares\/#\/schema\/person\/a9f452898a055ceb0f17967802c852da"},"breadcrumb":{"@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/thomasgr.im\/shares\/"},{"@type":"ListItem","position":2,"name":"Making An App With PhoneGap &#038; jQm Part III: A Stateless Authentication Layer"}]},{"@type":"WebSite","@id":"https:\/\/thomasgr.im\/shares\/#website","url":"https:\/\/thomasgr.im\/shares\/","name":"ThomasGR.IM","description":"Geeky Musing","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/thomasgr.im\/shares\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/thomasgr.im\/shares\/#\/schema\/person\/a9f452898a055ceb0f17967802c852da","name":"Thomas","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g","caption":"Thomas"},"sameAs":["https:\/\/thomasgr.im\/"],"url":"https:\/\/thomasgr.im\/shares\/author\/thomadmin\/"}]}},"_links":{"self":[{"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/posts\/222","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/comments?post=222"}],"version-history":[{"count":0,"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/posts\/222\/revisions"}],"wp:attachment":[{"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/media?parent=222"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/categories?post=222"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/tags?post=222"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}