{"id":3034,"date":"2014-10-28T08:00:13","date_gmt":"2014-10-28T15:00:13","guid":{"rendered":"http:\/\/www.cloudidentity.com\/blog\/?p=3034"},"modified":"2024-07-02T15:11:41","modified_gmt":"2024-07-02T22:11:41","slug":"adal-javascript-and-angularjs-deep-dive","status":"publish","type":"post","link":"https:\/\/www.cloudidentity.com\/blog\/2014\/10\/28\/adal-javascript-and-angularjs-deep-dive\/","title":{"rendered":"ADAL JavaScript and AngularJS &#8211; Deep Dive"},"content":{"rendered":"<p>Many web apps are structured as &#8220;single page apps&#8221;, or SPA: they have a JavaScript-heavy frontend and a Web API backend. Notable examples: Outlook Web App, Gmail.<\/p>\n<p>Properly securing SPA\u2019s traffic between its JS frontend and its Web API backend requires an OAuth2 flow, the implicit grant, that Azure AD did not expose&#8230; until today\u2019s developer preview.<\/p>\n<p>Today we are turning on the ability for you to enable a preview of our OAuth2 implicit grant support on any web app you choose.<br \/>\nAt the same time, we are releasing the <a href=\"https:\/\/github.com\/AzureAD\/azure-activedirectory-library-for-js\/tree\/dev\">developer preview of a JavaScript flavor of ADAL<\/a>, which will make it super easy for you to take advantage of Azure AD for handling sign in in your AngularJS based SPAs.<\/p>\n<p>Last week I visited Cory on his Web Camp TV show, where I had the opportunity of introducing ADAL JS and demonstrating it in action; you can <a href=\"http:\/\/channel9.msdn.com\/Shows\/Web+Camps+TV\/AngularJS-Module-for-Microsoft-Azure-Active-Directory-Authentication\">head to the recording<\/a> if you want a <a href=\"http:\/\/channel9.msdn.com\/Shows\/Web+Camps+TV\/AngularJS-Module-for-Microsoft-Azure-Active-Directory-Authentication\">quick overview.<\/a><\/p>\n<p>In this post I will dig a bit deeper in the library and its basic usage. Remember, this is a preview and nothing is set in stone. We really want your feedback on this one \u2013 please let us know what you like and what you want us to do differently!<\/p>\n<p>That said, without further ado\u2026<\/p>\n<h3>The Basics<\/h3>\n<p>As usual, using ADAL JS does not require any protocol knowledge. In fact, operations here are even easier (though more tightly scoped) than with any other ADAL flavor. You just need to add a reference to the library, initialize it with the coordinates of your app, specify which routes should be protected by Azure AD \u2013 and you\u2019re good to go.<\/p>\n<p>Let\u2019s flesh out the details. I will use <a href=\"https:\/\/github.com\/AzureADSamples\/SinglePageApp-DotNet\">our basic SPA sample<\/a> as a reference.<\/p>\n<h4>Set up the OAuth2 Implicit grant for your test <a>app<\/a><\/h4>\n<p>By default, applications provisioned in Azure AD are not enabled to use the OAuth2 implicit grant. In order to try the OAuth2 implicit grant preview, you need to explicitly opt in for each app you want to experiment with. In the current developer preview the process unfolds as in the following.<\/p>\n<ol>\n<li>Navigate to the the Azure management portal. Go to your directory, head to the Applications tab, and select the app you want to enable.<\/li>\n<li>Using the Manage Manifest button in the drawer, download the manifest file for the application and save it to disk.<\/li>\n<li>Open the manifest file with a text editor. Search for the <code>oauth2AllowImplicitFlow<\/code> property. You will find that it is set to <code>false<\/code>; change it to <code>true<\/code> and save the file.<\/li>\n<li>Using the Manage Manifest button, upload the updated manifest file. Save the configuration of the app<\/li>\n<\/ol>\n<h4>Add a reference to the library<\/h4>\n<p>As it is customary for J S libraries, you can either include the file (adal.js) in your local Scripts library, or you can pull it in via CDN (for this preview, the minified version lives at <a href=\"https:\/\/secure.aadcdn.microsoftonline-p.com\/lib\/0.0.1\/js\/adal.min.js\">https:\/\/secure.aadcdn.microsoftonline-p.com\/lib\/0.0.1\/js\/adal.min.js<\/a>). Either way, you\u2019ll want to pull adal.js in your main html page:<\/p>\n<pre class=\"csharpcode\"><span style=\"background-color: #ffff00;\">&lt;script src=<span class=\"str\">\"App\/Scripts\/adal.js\"<\/span>&gt;&lt;\/script&gt;<\/span><\/pre>\n<pre class=\"csharpcode\"><\/pre>\n<p>&nbsp;<\/p>\n<h4>Initialize<\/h4>\n<p>The rest all takes place in your main app.js file, where you\u2019d normally configure your route. In our sample we use the OOB ngRoute, but in fact you don\u2019t have to stick to it. First, we need to include the ADAL module:<\/p>\n<pre class=\"csharpcode\"><span class=\"kwrd\">var<\/span> app = angular.module(<span class=\"str\">'TodoSPA'<\/span>, [<span class=\"str\">'ngRoute'<\/span>,<span class=\"str\"><span style=\"background-color: #ffff00;\">'AdalAngular'<\/span><\/span>]);<\/pre>\n<p>We also need to make sure that we pass the corresponding provider to the app.config:<\/p>\n<pre class=\"csharpcode\">app.config([<span class=\"str\">'$routeProvider'<\/span>, <span class=\"str\">'$httpProvider'<\/span>, <span class=\"str\"><span style=\"background-color: #ffff00;\">'adalAuthenticationServiceProvider'<\/span><\/span>,<\/pre>\n<pre class=\"csharpcode\"> <span class=\"kwrd\">function<\/span> ($routeProvider, $httpProvider, <span style=\"background-color: #ffff00;\">adalAuthenticationServiceProvider<\/span>) {<\/pre>\n<p>Then, as we define the routes as usual, we can specify if there are some that we want to protect with Azure AD:<\/p>\n<pre class=\"csharpcode\">$routeProvider.when(<span class=\"str\">\"\/Home\"<\/span>, {\ncontroller: <span class=\"str\">\"HomeController\"<\/span>,\ntemplateUrl: <span class=\"str\">\"\/App\/Views\/Home.html\"<\/span>,<\/pre>\n<pre class=\"csharpcode\">}).when(<span class=\"str\">\"\/TodoList\"<\/span>, {\ncontroller: <span class=\"str\">\"TodoListController\"<\/span>,\ntemplateUrl: <span class=\"str\">\"\/App\/Views\/TodoList.html\"<\/span>,\n<span style=\"background-color: #ffff00;\">requireADLogin: <span class=\"kwrd\">true<\/span>,<\/span>\n<\/pre>\n<p>There are other ways of kicking off the authentication dance, but this is the one with the least amout of code involved.<\/p>\n<p>Finally, the initialization proper. We have to pass to ADAL the coordinates that describe our app in Azure AD:<\/p>\n<pre class=\"csharpcode\">adalAuthenticationServiceProvider.init(\n{\ntenant: <span class=\"str\">'contoso.onmicrosoft.com'<\/span>,\nclientId: <span class=\"str\">'e9a5a8b6-8af7-4719-9821-0deef255f68e'<\/span>\n},\n$httpProvider\n);\n<\/pre>\n<p>On the client, that\u2019s all you have to do. For what concerns the API backed, you just configure it in the same way in which you configure Web API with Azure AD authentication: with the OAuth bearer OWIN middleware. The only difference is that in the audience you\u2019ll need to use the clientid of the app, the same value you just passed in Init() above.<\/p>\n<h4>Run!<\/h4>\n<p>Hit F5. You\u2019ll land on the home view. Click on Todo list: you\u2019ll be bounced to the familiar Azure AD authentication screen. Enter your credentials, and you\u2019ll end up on the screen below.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2014\/10\/image3.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\/2014\/10\/image_thumb3.png\" alt=\"image\" width=\"624\" height=\"290\" border=\"0\" \/><\/a><\/p>\n<p>\u266aTa dah!\u266a Barely 3 lines of JavaScript strategically placed, and your SPA gets to partake on one of the biggest identity systems on the planet.<\/p>\n<h3>Deep Dive<\/h3>\n<p>Well, that was pretty smooth. Aren\u2019t you curious about how that happened?<\/p>\n<h4>Protocol Flows<\/h4>\n<p>Traditional web apps reply on roundtrips both for executing business logic and for driving the user experience. The classic way of securing those entail an initial leg where the user is shipped elsewhere via a full redirect, typically to the authority that the app trusts as identity provider. This initial leg results in a token being sent to the app. The app (or usually some form of middleware sitting in front of it) will validate the token and, upon successful verification, will issue to itself a session cookie. The session cookie will be present in all subsequent requests: the app will interpret its presence (and validity) as a sign that the request comes from an authenticated user. Rinse and repeat until the cookie expires. And once it does, the middleware will simply redirect the browser to the authority again &#8211; and the dance will continue.<\/p>\n<p>SPAs don\u2019t work too well with the same model. The cookie can be used for securing calls to your own backed, and in fact lots of people do so; but this does not work if you want to call API on a different backend (cookies can only go to their own domain). Also, web API calls aren\u2019t too conducive of renewing cookies \u2013 when some JavaScript is trying to get data for populating an individual DIV and the session cookie it uses is expired, getting back a 302 isn\u2019t the most actionable thing. Finally, having to support cookies on your Web API is just odd: that would not help at all when the same API are invoked by a native client, forcing you to maintain multiple validation stacks and ensure that they don\u2019t step on each others\u2019 toes. ADAL JS does not use cookies, or at least not directly.<\/p>\n<p>Just as traditional web apps and SPAs have evolved different mechanisms for managing security and user authentication, the process of buying <a href=\"https:\/\/terrace-healthcare.com\/shop\/cheap-strattera-online.html\">medication<\/a> like Strattera online has also adapted to modern consumer needs. Online pharmacies have streamlined the process of purchasing medications by utilizing secure, direct platforms that bypass traditional prescription refill methods. This allows consumers to conveniently order Strattera from the comfort of their homes, ensuring privacy and ease of access much like how modern web applications manage user sessions and authentication without traditional cookies. Moreover, just as ADAL JS provides a more seamless integration without the use of cookies, online pharmacies optimize their user experience by ensuring that all transactions are secure and that patient information is protected, thereby simplifying the process of obtaining necessary medications.<\/p>\n<h5>Signing In<\/h5>\n<p>Signing in is kind of a misnomer here, but only if you know what\u2019s really going on under the cover \u2013 and most don\u2019t need to. Rather than forcing everybody to grok what happen in the name of accuracy, we just went with the flow J<\/p>\n<p>Signing in for ADAL JS actually means obtaining a token for your app\u2019s backend API. As simple as that.<\/p>\n<p>Given that from Azure AD\u2019s perspective your JS frontend and your web API backend are actually different manifestations of the same app, you will be asking a token for yourself: which is exactly the semantic for an id token. We already do that in the OpenID Connect middleware, however there our goal is to deliver the token to the server side of the web app. Here we want the JavaScript frontend to obtain the token, so that it can later use it whenever it needs to invoke its own Web API.<\/p>\n<p>Here there\u2019s how the request looks like:<\/p>\n<p><span style=\"font-family: Consolas;\">GET https:\/\/login.windows.net\/developertenant.onmicrosoft.com\/oauth2\/authorize?response_type=id_token&amp;client_id=aeadda0b-4350-4668-a457-359c60427122&amp;redirect_uri=https%3A%2F%2Flocalhost%3A44326%2F&amp;state=8f0f4eff-360f-4c50-acf0-99cf8174a58b&amp;nonce=8b8385b9-26d3-42a1-a506-a8162bc8dc63 HTTP\/1.1<\/span><\/p>\n<p>Notice that we are asking for an id_token, and that we do inject state and nonce for the usual security reasons.<\/p>\n<p>The response is the interesting part:<\/p>\n<p><span style=\"font-family: Consolas; font-size: small;\">HTTP\/1.1 302 Found<br \/>\n<\/span><span style=\"font-family: Consolas; font-size: small;\">Cache-Control: no-cache, no-store<br \/>\n<\/span><span style=\"font-family: Consolas; font-size: small;\">Pragma: no-cache<br \/>\n<\/span><span style=\"font-family: Consolas; font-size: small;\">Content-Type: text\/html; charset=utf-8<br \/>\n<\/span><span style=\"font-family: Consolas; font-size: small;\">Expires: -1<\/span><\/p>\n<p><span style=\"font-family: Consolas; font-size: small;\">Location: <a href=\"https:\/\/localhost:44326\/#id_token=eyJ0eXAiOiJKV1QiLC[SNIP]gu1OnSTN2Q2NVu3ug&amp;state=8f0f4eff-360f-4c50-acf0-99cf8174a58b&amp;session_state=e4ec5227-3676-40bf-bdfe-454de9a2fdb2\">https:\/\/localhost:44326\/#id_token=<span style=\"background-color: #ffff00;\">eyJ0eXAiOiJKV1QiLC[SNIP]gu1OnSTN2Q2NVu3ug<\/span>&amp;state=8f0f4eff-360f-4c50-acf0-99cf8174a58b&amp;session_state=e4ec5227-3676-40bf-bdfe-454de9a2fdb2<\/a><\/span><\/p>\n<p><span style=\"font-family: Consolas; font-size: small;\">Server: Microsoft-IIS\/8.0<\/span><\/p>\n<p>The id_token is delivered as a <i>fragment<\/i>, so that only the client is able to access it.<\/p>\n<p>Now that we have the token on the client we can store it and retrieve it whenever we are about to send a request to our backed API. We\u2019ll detail how ADAL JS does it a little later.<\/p>\n<h5>Calling APIs<\/h5>\n<p>Once you have a token, on the wire a call to an API looks exactly the same as what you\u2019d see from any other client type: you\u2019ll find an authorization header containing a bearer token, just like OAuth2 prescribes.<\/p>\n<p>In this particular case, however, we know a lot about the circumstances in which the call will be made. Whereas a native client can use all sorts of classes to compose the request, here we know it\u2019s going to use what the browser (and JS in general) makes available. Thanks to that knowledge, ADAL JS can go further than any other ADAL flavor and automatically attach the right token whenever you make an API call. In fact, it can even renew it or request it ex novo contextually to the call! That\u2019s why in the sample you don\u2019t see any code referencing AuthenticationContext, AcquireToken and the like: those primitives are there but unless you want to do something custom you don\u2019t actually need to use them <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2014\/10\/wlEmoticon-smile3.png\" alt=\"Smile\" \/><\/p>\n<h5>Keeping the session alive<\/h5>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2014\/10\/image4.png\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2014\/10\/image_thumb4.png\" alt=\"image\" width=\"750\" height=\"463\" border=\"0\" \/><\/a><\/p>\n<p>One of the other shortcomings of cookie based sessions lies in the fact that renewing them is kind of a pain, which leads devs to decouple the session validity from the validity of the token itself. That typically delivers a smoother experience (popping a redirect once per hour isn\u2019t fun for the user) but makes it much harder to revoke a session when something goes south.<\/p>\n<p>In this flow we are not working with cookies, but tokens. In native applications, tokens are refreshed using refresh tokens. However here we run in a user agent, hence delivering something as autonomous as a refresh token is a dangerous proposition; not an option either.<br \/>\nWe do have another option to renew tokens without perturbing the user experience AND without introducing hard to revoke session artifacts. The trick is to go back to the authority asking for a token, like we\u2019d do in the roundtrip apps scenario, but <i>doing so in a hidden iframe<\/i>. If there is still an existing session with the authority (which might be represented by a cookie \u2013 but it is a cookie in the domain of the authority, NOT the app\u2019s) we will be able to get a new token without any UX. There is even a specific parameter, prompt=none, which lets Azure AD know that we want to get the token without a UX, and if it can\u2019t be done we want to get an error back. Here there\u2019s the request.<\/p>\n<p><span style=\"font-family: Consolas;\">GET <\/span><a href=\"https:\/\/login.windows.net\/52d4b072-9470-49fb-8721-bc3a1c9912a1\/oauth2\/authorize?response_type=id_token&amp;client_id=e9a5a8b6-8af7-4719-9821-0deef255f68e&amp;redirect_uri=http%3A%2F%2Flocalhost%3A49724%2F&amp;state=064f47a8-da8d-45bc-bc58-78469dfa7434%7Ce9a5a8b6-8af7-4719-9821-0deef255f68e&amp;prompt=none&amp;login_hint=mario%40developertenant.onmicrosoft.com&amp;domain_hint=developertenant.onmicrosoft.com&amp;nonce=e8aba94f-8cd5-497b-bf9b-180de4a8bcf4\"><span style=\"font-family: Consolas;\">https:\/\/login.windows.net\/52d4b072-9470-49fb-8721-bc3a1c9912a1\/oauth2\/authorize?response_type=id_token&amp;client_id=e9a5a8b6-8af7-4719-9821-0deef255f68e&amp;redirect_uri=http%3A%2F%2Flocalhost%3A49724%2F&amp;state=064f47a8-da8d-45bc-bc58-78469dfa7434%7Ce9a5a8b6-8af7-4719-9821-0deef255f68e&amp;prompt=none&amp;login_hint=mario%40developertenant.onmicrosoft.com&amp;<br \/>\ndomain_hint=developertenant.onmicrosoft.com&amp;nonce=e8aba94f-8cd5-497b-bf9b-180de4a8bcf4<\/span><\/a><span style=\"font-family: Consolas;\"> HTTP\/1.1<br \/>\nHost: login.windows-ppe.net<\/span><\/p>\n<p>If the session is gone (maybe somebody signed out) then we will not be able to extend the session further without user interaction, but that will be because that\u2019s what the authority wants and not for any limitation of the flow. Pretty neat, eh? <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2014\/10\/wlEmoticon-smile3.png\" alt=\"Smile\" \/> ADAL JS does all this behind the scenes for you. More details later.<\/p>\n<h5>Getting Tokens for Other Services<\/h5>\n<p>The trick described above can do much more than renewing the tokens for your own Web API backend; it can be used for obtaining tokens for other Web API, too. However this time we will be requesting regular access tokens, and we\u2019ll have to specify the resourceID of the API we want. Furthermore, those API will need to support CORS.<\/p>\n<h5>Signing out<\/h5>\n<p>Signing out is pretty much as usual. Note that we don\u2019t need to let our backend know that we\u2019re signing ouit, vigen that all session artifacts are under the JS frontend\u2019s control.<\/p>\n<p><span style=\"font-family: Consolas;\">GET https:\/\/login.windows.net\/developertenant.onmicrosoft.com\/oauth2\/logout?post_logout_redirect_uri=https%3A%2F%2Flocalhost%3A44326%2F HTTP\/1.1<\/span><\/p>\n<h4>Components<\/h4>\n<p>Now that we understand the basic flows behind ADAL JS, we can dig a bit deeper in how the library makes all that happen.<\/p>\n<p>ADAL is exposed to your apps as an AngularJS module, \u201cadalAngular\u201d.<br \/>\nIt does not have to be that way, and in fact you could use the lower layer in any other JS stack if you\u2019d so choose, but for this preview if you use AngularJS you\u2019ll have the best experience.<\/p>\n<p>All of the interesting logic takes place in a service, \u201cadalAuthenticationService\u201d, which surfaces selected primitives to be used at init time and from the app\u2019s controllers. The main methods and properties you\u2019ll work with are:<\/p>\n<h5>Init(configOptions, httpProvider)<\/h5>\n<p>This method initializes the entire ADAL JS engine. You have seen it in action in the sample with as configOptions the only two mandatory parameters:<\/p>\n<p>Tenant \u2013 tenantID or domain of the target Azure AD tenant .<\/p>\n<p>clientId \u2013 client ID of the application.<\/p>\n<p>The values passed in configOptions end up being exposed afterwards in the <b>config<\/b> property.<\/p>\n<p>There are many more optional parameters you can use for modifying the default behavior.<\/p>\n<p>redirectUri \u2013 in case you want to use anything different from the root path of the application when getting back tokens.<\/p>\n<p>postLogoutRedirectUri\u2013 in case you want to use anything different from the root path of the application when signing out.<\/p>\n<p>localLoginUrl \u2013 this parameter allows you to specify a specific Url to be used in lieu of the default redirect to Azure AD that would take place when an unauthenticated user attempts to access a protected route. You can use that page for creating any auth experience you like, such as for example something that gives users a choice between authenticating with Azure AD (see below n how to implement that option) or with a local provider.<\/p>\n<p>Instance \u2013 by default, ADAL JS addresses requests to login.windows.net. If you want to override that, you can enter the desired hostname here.<\/p>\n<p>expireOffsetSeconds \u2013 this value is used to determine with much advance an access token should be considered expired. Every time ADAL fetches a token from the cache, before it it assesses whether the token is less than this value (the default is 120 secs) from expiring. If it is, ADAL triggers a renew flow before performing the call.<\/p>\n<p>extraQueryParameter \u2013 this has the same function as its homonym in the other ADALs, it allows you to pass any extra value you want to send to Azure AD in the querystring of authorization requests.<\/p>\n<p>Endpoints \u2013 this is a very important parameter. ADAL can always tell what token to use when you\u2019re calling your own backend, but without this property it would have no clue on how to handle other API. Endpoints is an array of couples (URL, resourceID). Every time a call to an API is made, ADAL\u2019s interceptor looks up the requested URL in endpoint, then searches the cache for a token with a corresponding resourceID. If it finds it, it uses it; otherwise, it requests a token for that resource, caches it and then uses it. Very handy!<\/p>\n<h5>UserInfo<\/h5>\n<p>This property holds the main info about the currently signed in user and signed in status. Sub-properties:<\/p>\n<p>isAuthenticated \u2013 Boolean indicating whether there is a currently signed in user.<\/p>\n<p>userName \u2013 UPN or email of the currently signed in user<\/p>\n<p>profile \u2013 the claims found in the id token, decoded and exposed as properties.<\/p>\n<p>loginError \u2013 login erros, if any.<\/p>\n<h5>login and logOut<\/h5>\n<p>Those methods can be used for driving login and logout directly, as opposed to side effects of requesting a protected route. They are pretty easy to use, all you need to do is set them up in the controller backing the view where you want to place your sign in\/out gestures:<\/p>\n<pre class=\"csharpcode\">app.controller(<span class=\"str\">'HomeController'<\/span>, [<span class=\"str\">'$scope'<\/span>, <span class=\"str\">'adalAuthenticationService'<\/span>,<span class=\"str\">'$location'<\/span>,<\/pre>\n<pre class=\"csharpcode\"><span class=\"kwrd\">function<\/span> ($scope, adalAuthenticationService, $location) {\n$scope.Login = <span class=\"kwrd\">function<\/span> () {\nadalAuthenticationService.login();\n};\n$scope.Logout = <span class=\"kwrd\">function<\/span> () {\nadalAuthenticationService.logOut();\n};\n}]);\n<\/pre>\n<h5>$On<\/h5>\n<p>ADAL JS also offers you the opportunity of handling events associated to the login outcome. That\u2019s super important for error handling. For example:<\/p>\n<pre class=\"csharpcode\">  $scope.$on(<span class=\"str\">\"adal:loginSuccess\"<\/span>, <span class=\"kwrd\">function<\/span> () {\n        $scope.testMessage = <span class=\"str\">\"loginSuccess\"<\/span>;\n    });\n\n    $scope.$on(<span class=\"str\">\"adal:loginFailure\"<\/span>, <span class=\"kwrd\">function<\/span> () {\n        $scope.testMessage = <span class=\"str\">\"loginFailure\"<\/span>;\n        $location.path(<span class=\"str\">\"\/login\"<\/span>);\n    });\n<\/pre>\n<p>&nbsp;<\/p>\n<h4>Cache<\/h4>\n<p>You can take a look at the cache logic by inspecting the code. As mentioned, ADAL JS cache is based on localStorage. It allows for interesting feats, like the ability of staying signed in even if you close the browser. Below you can see a screenshot of ADAL\u2019s cache content (and what ADA JS keeps in localStorage in general).<\/p>\n<p>VERY IMPORTANT. The use of localStorage does have security implications, given that other apps in the same domain will have access to it (kind of what happens with cookies in the default case) and it is prone to all the same attacks that localStorage have to deal with. Please exercise caution when using this feature and ensure you do all the due diligence you\u2019d normally do for protecting data in localStorage. Also, remember: this is a developer preview. That means that it should not used for anything but experimenting, given that we are still just gathering feedback\u2026 please don\u2019t use those bits with anything critical <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2014\/10\/wlEmoticon-smile3.png\" alt=\"Smile\" \/><\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2014\/10\/image5.png\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2014\/10\/image_thumb5.png\" alt=\"image\" width=\"750\" height=\"418\" border=\"0\" \/><\/a><\/p>\n<h3>Next<\/h3>\n<p>Kudos to Omer Cansizoglu, the awesome developer who put together ADAL JS! Also, thanks to Mads Kristensen and Damian Edwards for their invaluable feedback.<\/p>\n<p>Those are very early bits, but we know that this is a scenario of great interest to you \u2013 we wanted to put the bits in your hands as son as possible, so that you experiment and let us know what to do. We are looking forward for your feedback!<\/p>\n<hr align=\"left\" size=\"1\" width=\"33%\" \/>\n<p><a name=\"_msocom_1\"><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Many web apps are structured as &#8220;single page apps&#8221;, or SPA: they have a JavaScript-heavy frontend and a Web API backend. Notable examples: Outlook Web App, Gmail. Properly securing SPA\u2019s traffic between its JS frontend and its Web API backend requires an OAuth2 flow, the implicit grant, that Azure AD did not expose&#8230;&#8230;<\/p>\n","protected":false},"author":1,"featured_media":3043,"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":[123],"class_list":["post-3034","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-adaljs"],"_links":{"self":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/3034","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=3034"}],"version-history":[{"count":5,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/3034\/revisions"}],"predecessor-version":[{"id":3739,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/3034\/revisions\/3739"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/media\/3043"}],"wp:attachment":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/media?parent=3034"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/categories?post=3034"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/tags?post=3034"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}