{"id":3245,"date":"2015-04-06T07:00:11","date_gmt":"2015-04-06T14:00:11","guid":{"rendered":"http:\/\/www.cloudidentity.com\/blog\/?p=3245"},"modified":"2015-04-06T13:33:19","modified_gmt":"2015-04-06T20:33:19","slug":"adal-plugin-for-apache-cordova-deep-dive","status":"publish","type":"post","link":"https:\/\/www.cloudidentity.com\/blog\/2015\/04\/06\/adal-plugin-for-apache-cordova-deep-dive\/","title":{"rendered":"ADAL Plugin for Apache Cordova: Deep Dive"},"content":{"rendered":"<p>I am super happy to finally be able to talk about this! Today our friends in <a href=\"https:\/\/msopentech.com\/blog\/2015\/04\/06\/cordova-azure-active-directory-plugin-is-now-available\">MS open Tech are releasing<\/a> the first developer preview of an <a href=\"https:\/\/github.com\/AzureAD\/azure-activedirectory-library-for-cordova\">Apache Cordova plugin for ADAL<\/a>, the result of few months of merry collaboration between out teams. This plugin will be yet another arrow in your developer quiver for adding the power of Azure AD to your multi-platform applications \u2013 namely, the ones targeting iOS, Android, Windows Store and Windows Phone. You can find the announcement posts <a href=\"https:\/\/msopentech.com\/blog\/2015\/04\/06\/cordova-azure-active-directory-plugin-is-now-available\">here<\/a> and <a href=\"http:\/\/blogs.technet.com\/b\/ad\/archive\/2015\/04\/06\/developer-preview-adal-plugin-for-apache-cordova.aspx\">here<\/a>. And if you want to get started quickly, instead of sifting through all my words salad below, <a href=\"https:\/\/github.com\/AzureADSamples\/NativeClient-MultiTarget-Cordova\">head straight to the sample repo<\/a> and follow the <a href=\"https:\/\/github.com\/AzureADSamples\/NativeClient-MultiTarget-Cordova\/blob\/master\/README.md\">detailed readme<\/a>! In this post I am going to dig a bit deeper on the role that the plugin plays in the ADAL franchise, mention intended usage and take a peek under the hood of the plugin itself.<\/p>\n<h2>No compromises: JavaScript apps with the power of native ADALs<\/h2>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/04\/image.png\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/04\/image_thumb.png\" alt=\"image\" width=\"378\" height=\"654\" border=\"0\" \/><\/a><\/p>\n<p>Since we announced <a href=\"https:\/\/www.cloudidentity.com\/blog\/2015\/02\/19\/introducing-adal-js-v1\/\">ADAL JS<\/a>, we had a constant stream of questions about using it in Cordova applications: how to do it, why it was not optimized for that use case, and so on. Technically it <em>is<\/em> possible to use ADAL JS in Cordova apps \u2013 I know of people who do it. However ADAL JS is designed to operate in a different environment, SPA apps coming from a\u00a0 server, and assumes constraints that are simply not present in Cordova apps: browser sandboxing, absence of refresh token in the implicit flow, and so on. The Cordova plugin for ADAL does not have to cope with such limitations, and it grants you far more access to the advanced authentication capabilities of the devices themselves. How? Perhaps I should start from getting on the same page on what Cordova is. Quoting from its <a href=\"https:\/\/cordova.apache.org\/\">about page<\/a>:<\/p>\n<blockquote><p>Apache Cordova is a set of device APIs that allow a mobile app developer to access native device function such as the camera or accelerometer from JavaScript. Combined with a UI framework such as jQuery Mobile or Dojo Mobile or Sencha Touch, this allows a smartphone app to be developed with just HTML, CSS, and JavaScript. When using the Cordova APIs, an app can be built without any native code (Java, Objective-C, etc) from the app developer. Instead, web technologies are used, and they are hosted in the app itself locally (generally not on a remote http server). And because these JavaScript APIs are consistent across multiple device platforms and built on web standards, the app should be portable to other device platforms with minimal to no changes.<\/p><\/blockquote>\n<p>That is a very, very neat trick. As our <a href=\"https:\/\/github.com\/AzureADSamples\/NativeClient-MultiTarget-Cordova\">first sample<\/a> shows, it is amazingly simple to whip together one app \u2013 and <em>run it on many different platforms without a single change<\/em>. Of course our sample is a toy, as samples demonstrating API usage ought to be \u2013 IRL you\u2019d likely at least add some CSS to comply to the look &amp; feel of the targeted platform. But even taking that into account, I am amazed by how succinct the app code turns out to be. Cordova achieves its tricks by exposing native platform capabilities by <em>plugins<\/em>: JavaScript fa\u00e7ades which route calls to fragments of native code \u2013 native code that the plugin must supply for each of the platforms it wants to support. That is also how the ADAL plugin came to be: we decided a JavaScript API to use for exposing the most basic ADAL capabilities, then the valiant developers at MS open Tech created a bridge between that and the native ADALs on iOS, Android and .NET. (specifically, the two Windows Runtime Components in the ADAL\u00a0 .NET NuGet targeting Windows Store and Windows Phone 8.1 store apps). Concretely: say that you write a Cordova app and you deploy it to an iOS device, real or emulated. When in your JavaScript you invoke one of the ADAL Cordova plugin methods, say the classic <span style=\"font-family: Consolas;\"><strong>acquireTokenAsyc<\/strong><\/span>, what actually happens is that the parameters will be dispatched down and the logic will be executed by the Objective-C flavor of ADAL: the cached tokens will be looked up from the Keychain, for example. Take the same application, and deploy it to a Windows device: the exact same JavaScript call will end up being executed by the corresponding .winmd component, and the tokens will be looked up from the Windows Store isolated storage. None of that would be possible with ADAL JS, of course: the storage on the actual device would be completely unreachable. The same holds for any other capability you get when you use the native ADALs.<\/p>\n<h2>ADALs\u2019 Rosetta Stone<\/h2>\n<p>This isn\u2019t the first time we work on an ADAL deliverable that can target multiple targets at once: <a href=\"https:\/\/www.cloudidentity.com\/blog\/2015\/03\/04\/adal-v3-preview-march-refresh\/\">ADAL .NET 3.0 preview<\/a> uses PCLs and Xamarin technology to target the same platforms discussed here. Apart from the obvious audience difference between the two libraries (one is aimed at C# developers, the other at JavaScript ones) the main characteristic that sets those apart is how deep they need to drill in the platform layers to achieve their goals. For ADAL .NET, it\u2019s the .NET Framework itself that is now available on every platform. The differences between platforms do exist, and we do need to take them into account in our programming model, but those all still live at the .NET level: we do need to change the component that shows the web authentication experience on every platform, but on every platform there\u2019s a .NET API for it. Those differences surface all the way to you, the application developer: your Visual Studio solution typically has projects for each platform, where you write platform specific code (though that\u2019s still .NET). That basically means that we are only limited by what makes sense for the target platform, but as a baseline we can expose whatever is in ADAL .NET. In ADAL for Cordova things are different. The JavaScript layer is just a fa\u00e7ade and all the hard work is delegated to actual platform bits. We can only execute on platforms where we have an ADAL flavor available. For example: Cordova can run on Ubuntu, but we don\u2019t have an ADAL that would run natively on it. That has two main consequences:<\/p>\n<ol>\n<li>The JavaScript fa\u00e7ade we expose must utilize features that are available on ALL of the ADAL libraries used by the plugin.\n<ol>\n<li>Corollary: if there are differences in the way in which ADALs on different platforms handle things, the plugin should try to normalize those as much as possible<\/li>\n<\/ol>\n<\/li>\n<li>If there are platform specific features that MUST be surfaced to support mandatory functionalities, they have to be done in ways that won\u2019t interfere with the platform-neutral programming model<\/li>\n<\/ol>\n<p>That\u2019s quite a tall order! To avoid the analysis paralysis that was very likely to ensue, we deliberately kept things very simple:<\/p>\n<ul>\n<li>We picked two <span style=\"font-family: Consolas;\">AcquireTokenAsync<\/span> and <span style=\"font-family: Consolas;\">AcquireTokenSilentAsync<\/span> overloads and ensured they were well mapped. Those two methods are enough to implement the main ADAL pattern on a native client<\/li>\n<li>We tool all the surface those two entail \u2013 <span style=\"font-family: Consolas;\">AuthenticationContext<\/span> properties and constructors, <span style=\"font-family: Consolas;\">AuthenticationResult<\/span>, cache and cache items \u2013 and ensured that we could map those back and forth from the JS fa\u00e7ade while guaranteeing coherent results<\/li>\n<\/ul>\n<p>That approach makes it possible for you to write something like<\/p>\n<pre class=\"csharpcode\">authenticate: <span class=\"kwrd\">function<\/span> (authCompletedCallback) {\r\n      app.context = <span class=\"kwrd\">new<\/span> Microsoft.ADAL.AuthenticationContext(authority);\r\n      app.context.acquireTokenSilentAsync(resourceUri, clientId)\r\n          .then(authCompletedCallback, <span class=\"kwrd\">function<\/span> () {\r\n              app.context.acquireTokenAsync(resourceUri, clientId, redirectUri)\r\n                .then(authCompletedCallback, <span class=\"kwrd\">function<\/span> (err) {\r\n                    app.error(<span class=\"str\">\"Failed to authenticate: \"<\/span> + err);\r\n              });\r\n          });\r\n      });\r\n},<\/pre>\n<p>Which is pretty much the base of all native flows \u2013 try to get the token I need without showing any UX, and if it fails \u2013 prompt. That forced us to ensure that what we return from all libraries is consistent. That is mostly the case \u2013 the ADAL dev team makes semantic (!=syntactic<img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/04\/wlEmoticon-smile.png\" alt=\"Smile\" \/>) consistency across platforms a priority, but there are few things here and there that for a reason or another diverge. For example, not all ADALs agree about what should be used as user ID in the <span style=\"font-family: Consolas;\">AuthenticationResult<\/span>: some use a human readable identifier, others do not. A more serious difference is in how platforms handle the common endpoint. The plugin normalized what was easy to normalize, but in general you can expect the consistency between native ADALs to increase with new releases. Anyhow: I personally really like the minimal interface this plugin offers. I am hoping that you guys will like it \u2013 I am all for lightweight, and if we see few important apps built on top of this doing their auth stuff just fine, we might be able to spread the approach back to other ADALs <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/04\/wlEmoticon-smile.png\" alt=\"Smile\" \/><\/p>\n<h2>The Plugin<\/h2>\n<p>The plugin in itself has a pretty interesting architecture, dictated by how Cordova organizes things in a plugin. You don\u2019t need to know any of the below in order to use the plugin in your app, I am reporting it just because it\u2019s cool \u2013 and who knows. maybe I\u2019ll entice you to contribute to it! <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/04\/wlEmoticon-smile.png\" alt=\"Smile\" \/> Here there\u2019s the screenshot of the structure of the plugin repo:<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/04\/image1.png\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/04\/image_thumb1.png\" alt=\"image\" width=\"430\" height=\"979\" border=\"0\" \/><\/a><\/p>\n<p>The JavaScript fa\u00e7ade is in the www folder. That\u2019s super convenient for figuring out the development surface offered by the plugin. All the files there are artifact exposed by the OM, apart from CordovaBridge.js. That file contains the main dispatcher (<span style=\"font-family: Consolas;\">executeNativeMethod<\/span>) to route JS calls to their native counterparts (see <a href=\"https:\/\/cordova.apache.org\/docs\/en\/4.0.0\/guide_hybrid_plugins_index.md.html\">this<\/a> for more details). For example, if you taker a look at AuthenticitonContext.js you\u2019ll find that a call to <span style=\"font-family: Consolas;\">acquireTokenAsync <\/span>actually boils down to<\/p>\n<pre class=\"csharpcode\">bridge.executeNativeMethod(<span class=\"str\">'acquireTokenAsync'<\/span>, [<span class=\"kwrd\">this<\/span>.authority, resourceUrl, clientId, redirectUrl])<\/pre>\n<p>The native action is all under \/src. Here, every platform is represented by a subfolder (with the exception of Windows Store and Windows Phone, which are bundled). Every platform folder follows the same logical structure.<\/p>\n<ul>\n<li>It includes the bits of the corresponding native ADAL (or in the Android case, the Maven reference to it).<\/li>\n<li>It includes a proxy of some sort, establishing the interface used by the dispatcher: ADALProxy.js in Windows, CordovaAdalPlugin.* for iOS, CordovaAdalPlugin.java for Android.<\/li>\n<li>The rest of the files are mostly impedance mismatch fixers <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/04\/wlEmoticon-smile.png\" alt=\"Smile\" \/><\/li>\n<\/ul>\n<p>The Scripts folder is also interesting, but before I get in the details of it I have to mention how one actually sets up the plugin in one application. Remember, we have detailed instructions on the readmes of both the <a href=\"https:\/\/github.com\/AzureAD\/azure-activedirectory-library-for-cordova\">library<\/a> and the <a href=\"https:\/\/github.com\/AzureADSamples\/NativeClient-MultiTarget-Cordova\">sample<\/a> \u2013 the below is only for explaining the plugin\u2019s architecture. Let\u2019s say that you wrote your JS app and you are now ready to give it a spin. Here there\u2019s the ceremony you follow if you use the Cordova command line tools:<\/p>\n<div class=\"csharpcode\">\n<pre><span class=\"lnum\">   1: <\/span>cordova create MySample --copy-from=\"sample\"<\/pre>\n<pre><span class=\"lnum\">   2: <\/span>cd MySample<\/pre>\n<pre><span class=\"lnum\">   3: <\/span>cordova platform add https:\/\/github.com\/apache\/cordova-android.git<\/pre>\n<pre><span class=\"lnum\">   4: <\/span>cordova platform add ios<\/pre>\n<pre><span class=\"lnum\">   5: <\/span>cordova platform add windows<\/pre>\n<pre><span class=\"lnum\">   6: <\/span>cordova plugin add android@97718a0a25ec50fedf7b023ae63bfcffbcfafb4b<\/pre>\n<pre><span class=\"lnum\">   7: <\/span>cordova run<\/pre>\n<\/div>\n<p>(Note that if you are on Mac you can\u2019t run a Windows emulator, and on Windows you can\u2019t emulate iOS) The first line takes your code and creates a new local repository based on that. It will be used to host both your code and whatever it is necessary to support the platforms you\u2019ll choose to support. The lines 3 to 5 tell Cordova to set your sample app to include all the artifacts necessary for supporting the platforms specified. Finally \u2013 line 6 sets up the ADAL plugin in your project. That\u2019s where the files in the \/Script folder come in \u2013 they contain logic that needs to be executed as the plugin code is added to each platforms, and in some cases at app build time . For example: if in Windows you want to be able to authenticate against one ADFS in your intranet, the Windows Runtime expects lots of settings to be set; iOS requires specific entitlements for code singing; and so on. All in all, I have to say that Cordova offers one of the cleanest and easier to understand plugin structure I\u2019ve seen. Navigating through the ADAL plugin repo is a joy, as everything is nicely readable and just makes sense. Again, you don\u2019t need to see what\u2019s inside the plugin to use it \u2013 but I find it interesting and instructive <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/04\/wlEmoticon-smile.png\" alt=\"Smile\" \/><\/p>\n<h2>Feedback!<\/h2>\n<p>This is a preview, and as usual the reason we put previews out is to give you the change of giving it a spin and letting us know what you like, dislike and what does not work for you. I am personally very excited about this plugin, I just love the simplicity and power it offers \u2013 and I know that lots of you were searching for a solution for using Azure AD and call the API it protects (Office 365, Azure, Graph API, etc) from Cordova applications. Please do not hesitate to hit us with your feedback directly on github. Happy coding!!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I am super happy to finally be able to talk about this! Today our friends in MS open Tech are releasing the first developer preview of an Apache Cordova plugin for ADAL, the result of few months of merry collaboration between out teams. This plugin will be yet another arrow in your developer&#8230;<\/p>\n","protected":false},"author":1,"featured_media":3248,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-3245","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/3245","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/comments?post=3245"}],"version-history":[{"count":6,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/3245\/revisions"}],"predecessor-version":[{"id":3256,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/3245\/revisions\/3256"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/media\/3248"}],"wp:attachment":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/media?parent=3245"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/categories?post=3245"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/tags?post=3245"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}