[Azure] Logging out of Azure AD oauth

In a previous post you 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 Azure Mobile Apps plugin for Cordova (GitHub).  This plugin simplifies the authentication flow process, which uses server flow in this case. Basically that means that our Web API handles most of the flow by configuring the Microsoft.Azure.Mobile.Server.Authentication NuGet package. There is also client flow, where the flow is handled client-side, for more info on that check out the great blog series by Adrial Hall on Azure Mobile Apps.

This post assumes that you’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:


Seems to good to be true right? That’s because it is. If you look a bit deeper in the source code of the mobile client library, you’ll notice that the implementation of this call is pretty minimalistic:

MobileServiceClient.prototype.logout = Platform.async(function(callback) {
    /// <summary>
    /// Log a user out of a Mobile Services application.
    /// <param name="callback" type="Function" mayBeNull="true">
    /// Optional callback accepting error as a parameter.
    /// </param>
    /// </summary>
    this.currentUser = null;

All it does is set currentUser to null, which clears the object from knowing what the current user is.

So what’s the issue?

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:

  • The mobile client will call our API to authenticate.
  • The API will forward the request to Azure AD.
  • Azure AD will display a login prompt.
  • After being logged in, our application will be returned the tokens which it can use to issue further requests.
  • This token (amongst other things) are stored in a local cookie for reuse.

Again, this is very high level and certainly doesn’t do right to the process but it’s enough to have a rough understanding for now. If you’ve now used the above line of code to “logout” and you try logging in again, the following will occur:

  • The mobile client will call our API to authenticate.
  • The browser will send the previously acquired cookie with the request, since that was not cleared in any way.
  • The token sent as part of the request will be validated by the server.
  • You’re logged in.

So even though the client library cleared the information about the current user, we’re now immediately logged in again due to the cookie which is still there. No credentials needed. Not what we want.

How to solve it, part 1

Now that we understand the problem, it’s time to start thinking about solutions. One solution would be to somehow clear the local browser of its cookies. Because we’re working in a Cordova app, we have to use the InAppBrowser plugin to interact with the browser. That plugin does not support a straightforward call to clear the cookies, but there’s something similar you can use instead:

var browser = window.open(url, "_self", "clearsessioncache=yes,clearcache=yes");

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.

How to solve it, part 2

Next to having a “login” endpoint, the underlying AD auth endpoint also supports a “logout” 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:


Note: I’ve used “common”, but you can also use the ID of the Azure AD application you want to log off. This is important when you’re using multiple applications from within the same session and want to log out of only a specific one.

To call this endpoint, fire up a new InAppBrowser window to the specific URL. The complete logout method now looks like this:

logout(): ng.IPromise<void> {
    var deferred = this.$q.defer<void>();
    this.$q.when(this.mobileServiceClient.logout().then(() => {
        var logoutUrl = "https://login.windows.net/common/oauth2/logout";

        // after the mobile service clien has been logged out; do an explicit call to
        // the AAD oauth endpoint which will clear the stored cookies and session
        var browser = window.open(logoutUrl, '_blank', 'hidden=yes');

        browser.addEventListener('loadstop', (event) => {
    }, () => { deferred.reject(); });

    return deferred.promise;

Let’s see what happens here:

  • We first call the logout() method of the mobile service client (which is async for some reason).
  • When that succeedes; it will fire up a browser to the logout endpoint. The browser is hidden to not bother the user with it.
  • This will perform it’s magic making sure there is no valid authentication token present any more.
  • When it’s done, we’ll close the browser and resolve the promise to let the outside world know that work is done.

Now there’s one very important note: although the above logs us out from our API, it does not log us out of Azure AD since that’s yet another endpoint. If you want to do that too, you’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 clearsessioncache=yes,clearcache=yes again, but remind yourself that this will clear out a whole lot more than just the authentication cookie.


As always, feel free to comment below and maybe leave even better solutions!

, ,

Related posts

Latest posts

1 comment

Leave a Comment

Leave a Reply to Ridmal Madushanka Cancel reply

Your email address will not be published. Required fields are marked *