{"id":1951,"date":"2016-09-01T14:44:56","date_gmt":"2016-09-01T13:44:56","guid":{"rendered":"http:\/\/blog.repsaj.nl\/?p=1951"},"modified":"2016-09-01T14:44:56","modified_gmt":"2016-09-01T13:44:56","slug":"azure-logging-out-of-azure-ad-oauth","status":"publish","type":"post","link":"http:\/\/blog.repsaj.nl\/index.php\/2016\/09\/azure-logging-out-of-azure-ad-oauth\/","title":{"rendered":"[Azure] Logging out of Azure AD oauth"},"content":{"rendered":"<p>In a <a href=\"http:\/\/blog.repsaj.nl\/index.php\/2016\/03\/iot-aquarium-monitor-mobile-app-cordova-style\/\" target=\"_blank\">previous post<\/a>\u00a0you can read how I used Apache Cordova to create a client application that is linked to my back-end API hosted in Azure. For authentication, I made use of the built in authentication options of the\u00a0<strong>Azure Mobile Apps<\/strong> plugin for Cordova (<a href=\"https:\/\/github.com\/Azure\/azure-mobile-apps-cordova-client\" target=\"_blank\">GitHub<\/a>). \u00a0This plugin simplifies the authentication flow process, which uses\u00a0<strong>server flow<\/strong> in this case. Basically that means that our Web API handles most of the flow by configuring the <strong>Microsoft.Azure.Mobile.Server.Authentication <\/strong>NuGet<strong>\u00a0<\/strong>package.<b>\u00a0<\/b>There is also\u00a0<strong>client flow<\/strong>, where the flow is handled client-side, for more info on that check out the great blog series by Adrial Hall on <a href=\"https:\/\/shellmonger.com\/30-days-of-azure-mobile-apps-the-table-of-contents\/\" target=\"_blank\">Azure Mobile Apps<\/a>.<!--more--><\/p>\n<p>This post assumes that you&#8217;ve got the above all set-up and working. But now you might want to give your users the ability to sign out \/ log-off from your application. So you do the obvious thing (which is what I did) and you call:<\/p>\n<pre class=\"lang:default decode:true\">this.mobileServiceClient.logout();<\/pre>\n<p>Seems to good to be true right? That&#8217;s because it is. If you look a bit deeper in the source code of the mobile client library, you&#8217;ll notice that the implementation of this call is pretty minimalistic:<\/p>\n<pre class=\"lang:default decode:true \">MobileServiceClient.prototype.logout = Platform.async(function(callback) {\r\n    \/\/\/ &lt;summary&gt;\r\n    \/\/\/ Log a user out of a Mobile Services application.\r\n    \/\/\/ &lt;param name=\"callback\" type=\"Function\" mayBeNull=\"true\"&gt;\r\n    \/\/\/ Optional callback accepting error as a parameter.\r\n    \/\/\/ &lt;\/param&gt;\r\n    \/\/\/ &lt;\/summary&gt;\r\n    \r\n    this.currentUser = null;\r\n    callback();\r\n});<\/pre>\n<p>All it does is set currentUser to null, which clears the object from knowing what the current user is.<\/p>\n<h2>So what&#8217;s the issue?<\/h2>\n<p>To understand what the problem is, we need to know just a bit more about how the authentication flow works. On a high level, the following steps are involved:<\/p>\n<ul>\n<li>The mobile client will call our API to authenticate.<\/li>\n<li>The API will forward the request to Azure AD.<\/li>\n<li>Azure AD will display a login prompt.<\/li>\n<li>After being logged in, our application will be returned the tokens which it can use to issue further requests.<\/li>\n<li>This token (amongst other things) are stored in a local cookie for reuse.<\/li>\n<\/ul>\n<p>Again, this is very high level and certainly doesn&#8217;t do right to the process but it&#8217;s enough to have a rough understanding for now. If you&#8217;ve now used the above line of code to &#8220;logout&#8221; and you try logging in again, the following will occur:<\/p>\n<ul>\n<li>The mobile client will call our API to authenticate.<\/li>\n<li>The browser will send the previously acquired cookie with the request, since that was not cleared in any way.<\/li>\n<li>The\u00a0token sent as part of the request will be validated by the server.<\/li>\n<li>You&#8217;re logged in.<\/li>\n<\/ul>\n<p>So even though the client library cleared the information about the current user, we&#8217;re now immediately logged in again due to the cookie which is still there. No credentials needed. Not what we want.<\/p>\n<h2>How to solve it, part 1<\/h2>\n<p>Now that we understand the problem, it&#8217;s time to start thinking about solutions. One solution would be to somehow clear the local browser of its cookies. Because we&#8217;re working in a Cordova app, we have to use the\u00a0<strong>InAppBrowser<\/strong> plugin to interact with the browser. That plugin does not support a straightforward call to clear the cookies, but there&#8217;s something similar you can use instead:<\/p>\n<pre class=\"lang:default decode:true\">var browser = window.open(url, \"_self\", <strong>\"clearsessioncache=yes,clearcache=yes\"<\/strong>);<\/pre>\n<p>The part in bold is what is important here. We open a new browser to the API endpoint and instruct it to clear the session cache and cache. That seems to clear up the cookies. Although this works, I was not convinced that this is really the best way out there, so I kept looking.<\/p>\n<h2>How to solve it, part 2<\/h2>\n<p>Next to having a &#8220;login&#8221; endpoint, the underlying AD auth endpoint also supports a &#8220;logout&#8221; call. This call will terminate the current session with the user and will reset the local stored cookie so that is does not contain a valid token any longer. That seemed like the better approach to me. The endpoint you want to use is:<\/p>\n<pre class=\"\">https:\/\/login.windows.net\/common\/oauth2\/logout?post_logout_redirect_uri=[URI]\r\n<\/pre>\n<p><strong>Note:\u00a0<\/strong>I&#8217;ve used &#8220;common&#8221;, but you can also use the ID of the Azure AD application you want to log off. This is important when you&#8217;re using multiple applications from within the same session and want to log out of only a specific one.<\/p>\n<p>To call this endpoint, fire up a new\u00a0<strong>InAppBrowser<\/strong> window to the specific URL. The complete logout method now looks like this:<\/p>\n<pre class=\"lang:default decode:true\">logout(): ng.IPromise&lt;void&gt; {\r\n    var deferred = this.$q.defer&lt;void&gt;();\r\n    \r\n    this.$q.when(this.mobileServiceClient.logout().then(() =&gt; {\r\n        var logoutUrl = \"https:\/\/login.windows.net\/common\/oauth2\/logout\";\r\n\r\n        \/\/ after the mobile service clien has been logged out; do an explicit call to\r\n        \/\/ the AAD oauth endpoint which will clear the stored cookies and session\r\n        var browser = window.open(logoutUrl, '_blank', 'hidden=yes');\r\n\r\n        browser.addEventListener('loadstop', (event) =&gt; {\r\n            browser.close();\r\n            deferred.resolve();\r\n        });\r\n    }, () =&gt; { deferred.reject(); });\r\n\r\n    return deferred.promise;\r\n}<\/pre>\n<p>Let&#8217;s see what happens here:<\/p>\n<ul>\n<li>We first call the logout() method of the mobile service client (which is async for some reason).<\/li>\n<li>When that succeedes; it will fire up a browser to the logout endpoint. The browser is hidden to not bother the user with it.<\/li>\n<li>This will perform it&#8217;s magic making sure there is no valid authentication token present any more.<\/li>\n<li>When it&#8217;s done, we&#8217;ll close the browser and\u00a0resolve the promise to let the outside world know that work is done.<\/li>\n<\/ul>\n<p>Now there&#8217;s one very important note: although the above logs us out from our API, it\u00a0<strong>does not<\/strong> log us out of Azure AD since that&#8217;s yet another endpoint. If you want to do that too, you&#8217;ll need to handle one more additional endpoint. For me, my use case is to be able to switch users which the above facilitates. I like it because this way I can remain logged in with multiple accounts and still switch between them. You could also add\u00a0<strong>clearsessioncache=yes,clearcache=yes\u00a0<\/strong>again, but remind yourself that this will clear out a whole lot more than just the authentication cookie.<\/p>\n<p>&nbsp;<\/p>\n<p>As always, feel free to comment below and maybe leave even better solutions!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In a previous post\u00a0you can read how I used Apache Cordova to create a client application that is linked to my back-end API hosted in Azure. For authentication, I made use of the built in authentication options of the\u00a0Azure Mobile Apps plugin for Cordova (GitHub). \u00a0This plugin simplifies the authentication flow process, which uses\u00a0server flow<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[34],"tags":[78,161,170],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3KFR1-vt","_links":{"self":[{"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/posts\/1951"}],"collection":[{"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/comments?post=1951"}],"version-history":[{"count":0,"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/posts\/1951\/revisions"}],"wp:attachment":[{"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/media?parent=1951"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/categories?post=1951"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/tags?post=1951"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}