Azure Active Directory with Mobile Services without Prompting Every Time the Application Starts

Currently, every time the application is run the user is prompted to sign into Azure Active Directory, and then the AD issued token is then used to login to Azure Mobile Service. Not only is this a pain for the user (for example if they’ve only just been in the application, to have to sign in again feels somewhat unnecessary), it also adds latency on startup as well as preventing the application from running when offline. In the next couple of posts I’ll look at a couple of techniques to consider in order to improve this sign on experience.

Firstly, it’s worth noting that there was an update posted for the Azure Active Directory Authentication library (ADAL) on NuGet – it’s still prerelease but worth updating to if you’re using v3 of the library. More info on NuGet, here.

One of the nice things about ADAL is that it provides a cache for tokens. In addition to being able to query what tokens are in the cache (for example in order to then login to the Mobile Service) it also wraps the check to determine if a token is still valid. To do this, I can call AcquireTokenSilentAsync to authenticate silently ie without prompting the user. If a valid access token is found in the token cache it will be returned. In the case that no valid token is found and exception is raised and I then need to invoke AcquireTokenAsync as I did previously.

var authContext = new AuthenticationContext(Configuration.Current.ADAuthority);
try
{
    var res = await authContext.AcquireTokenSilentAsync(
        Configuration.Current.MobileServiceAppIdUri,
        Configuration.Current.ADNativeClientApplicationClientId);
    if (res != null && !string.IsNullOrWhiteSpace(res.AccessToken))
    {
        return res.AccessToken;
    }
}
catch (Exception saex)
{
    Debug.WriteLine(saex);
}

As Windows Phone 8.0 isn’t supportedyet by v3 of ADAL, I also need to update my custom implementation of the AuthenticationContext. Firstly, to add a static list of previously acquired tokens:

private static readonly List<AuthenticationResult> Tokens = new List<AuthenticationResult>();  

Next, I need to update my AuthenticationResult option to decode more than just the access and refresh tokens:

public class AuthenticationResult
{
    private static DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime();

    public string ClientId { get; set; }

    [JsonProperty(“access_token”)]
    public string AccessToken { get; set; }

    [JsonProperty(“refresh_token”)]
    public string RefreshToken { get; set; }

    [JsonProperty(“resource”)]
    public string Resource { get; set; }

        [JsonProperty(“expires_on”)]
    public long ExpiresOnSeconds { get; set; }

    public DateTime ExpiresOn
    {
        get { return epoch.AddSeconds(ExpiresOnSeconds); }
    }

    private long refreshTokenExpiresInSeconds;
    [JsonProperty(“refresh_token_expires_in”)]
    public long RefreshTokenExpiresInSeconds
    {
        get { return refreshTokenExpiresInSeconds; }
        set
        {
            refreshTokenExpiresInSeconds = value;
            RefreshTokensExpiresOn = DateTime.Now.AddSeconds(refreshTokenExpiresInSeconds);
        }
    }

    public DateTime RefreshTokensExpiresOn { get;private set; }

}

At the end of the AcquireTokenAsync method I need to set the ClientId property on the AuthenticationResult (so I know which AAD client id it was returned for as this isn’t returned in the response) and add the result to the Tokens list:

var result = JsonConvert.DeserializeObject<AuthenticationResult>(await data.Content.ReadAsStringAsync());
result.ClientId = ClientId;
Tokens.Add(result);
return result;

Finally I need to implement the AcquireTokenSilentAsync method – although it doesn’t require async/Task I’ve kept the method consistent with ADAL to avoid conditional code when calling the method

public async Task<AuthenticationResult> AcquireTokenSilentAsync(
    string resource,
    string clientId)
{
   

    var result = (from t in Tokens
                        where t.ClientId == clientId &&
                                   t.Resource == resource &&
                                   t.ExpiresOn > DateTime.Now
                        select t).FirstOrDefault();

    return result;
}

Note that this implementation doesn’t persist the access token beyond the current session. However, it will avoid the need to reauthenticate if the user does happen to do something that would otherwise require authentication.

Leave a comment