Monthly Archives: August 2014

Picking the Right Voice for Windows Phone

Recently, I had an interesting problem come up when testing localization (in French and Spanish in my case) in conjunction with letting the user choose a female or male voice in one of my Windows Phone apps (http://www.windowsphone.com/en-us/store/app/fn8/dd72d13d-1a7b-42c4-81d3-adbc98d4b002). The main part of the problem is basically financial, i.e. given the economics of app development it isn’t feasible to localize for many languages, let alone variants (cultures), e.g. whatever dialect of French is spoken in lower, mid-western Upper Volta. Additionally, given the inefficient workings of the Windows Phone store, it takes forever to submit (or even update) an app for each supported language. In my case, I have asked my translators to use a international a flavor as possible to cover as many markets as I can. Localizing into a language will still allow you to reach all the cultures that support the base language, e.g. localizing into Spanish will work for users in Spain (es-es) as well as Mexico (es-mx).

Part of the problem is technical. If you go this route, then when the user selects a gender for an installed voice, the app has to be able to match the appropriate voices from among those installed. With my first approach what I found when testing was that no voices were being selected at all in a device set to Mexican Spanish. The reason became immediately apparent when debugging:

The app was working fine in English because I was looking for a culture name and because en-us was the default culture for the app, it was finding that. But since the Spanish-speaking user is only getting a generic Spanish translation, the app was looking for a culture name of es-mx and blithely skipping over plain ol’ es.

So then I switched to grabbing the two-letter ISO language name instead. That made the app feliz in Español, but now on my device set to American English I was getting a male voice with a British accent when the radio button was set to female.

Another quick debug session and I could see that American voices Mike and Zira had some friends in the house I didn’t know about, including George and Susan from Great Britain and their friends Raul from Mexico and Stefan from Germany. All obviously there to enjoy Cortana’s predictions on the World Cup.

So here’s what can only be described as an amateurish hack to deal with this issue. Well, as my co-workers and I used to tell our boss in a previous job, all our code is “QDC – quick, dirty, or cheap. Pick any two.”

var twoLetterIsoLanguageName = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;

var currentCultureName = CultureInfo.CurrentCulture.Name;

IEnumerable<VoiceInformation> voiceInfos = from voice in InstalledVoices.All select voice;

var localVoices = new List<VoiceInformation>();

// See if any voices match the current culture specifically
foreach (var voiceInfo in voiceInfos)
{
 if (voiceInfo.Language == currentCultureName)
 {
 localVoices.Add(voiceInfo);
 }

 localVoices =
 localVoices.OrderByDescending(v => v.Language).ThenByDescending(v => v.Gender).ToList();
}

// No matches? Go for the generic language instead
if (localVoices.Count < 1)
{
 foreach (var voiceInfo in voiceInfos)
 {
 var primaryLangStr = voiceInfo.Language.Split('-')[0];

 if (primaryLangStr == twoLetterIsoLanguageName)
 {
 localVoices.Add(voiceInfo);
 }

 localVoices =
 localVoices.OrderByDescending(v => v.Language).ThenByDescending(v => v.Gender).ToList();
 }
}

App.StorageSettings.TryGetValue("VoiceGender", out _voiceGender);

_speechSynthesizer.SetVoice(localVoices.ElementAt(_voiceGender));

Authentication and Authorization with Auth0

Pre-Ramble

Apps that connect to services that allow users to contribute or distribute information become more complex than stand-alone apps, or even apps that just retrieve data, e.g. weather forecasts, from an online source. Connectivity, versioning and authentication/authorization all become issues. At least they do if you care about user experience and security.

In particular, authentication and authorization can be hard to do. Users expect to be able to use social authentication providers like Facebook or Twitter. They expect to not have to login repeatedly yet will find it hard to forgive privacy violations and security breaches (and rightfully so).

In my own quest to find tools and solutions in this area I was surprised how hard it was. Some providers, like Microsoft, have pretty decent answers for their own platforms. On Windows 8.x you can easily use Microsoft accounts and adopt the SSO (single sign-on) experience that makes it smoother for the user. With a little jiggering (and ignoring the cruft of the Live SDK) the same can be achieved with Windows Phone. Although the reason I say “pretty decent” is because the samples (even one written by ScottGu) were devoid of error handling to accommodate user cancellation or lack of connectivity.

The server-side of the equation is still a bit complicated. I covered this in my post http://code.stonetip.com/2014/01/31/using-microsoft-live-for-authentication-with-windows-8-x-and-windows-phone-8-apps/, which discussed how to process JWT (JSON Web Tokens) on the server. However, I was never comfortable with just this, knowing users would want other options

I kept looking for other approaches, spending mind-numbing hours with ADAL samples and Fiddler sessions…to the point where I was attempting to reverse-engineer the parts I needed – namely mobile-friendly login screens and true verification of signed tokens. One thing that became increasingly clear is that the providers of social auth have (and understandably so) absolutely no interest in working together to provide an integrated solution. Even within certain (evolving) standards like OAuth2 each has their own implementations and quirks.

This led me back to checking out Auth0 again (https://auth0.com/). Months ago, I’d looked into this service and ran into a couple difficulties with parts of the SDK and with the use of the Microsoft desktop sign-in page in a mobile browser. At the time, they also price-limited the number of providers you could use, which is no longer the case. Stupidly, I wrote Auth0 off. Perhaps if I’d not wanted to include Microsoft accounts I would have been off to the races. On the positive side, in the interim, I learned many useful things I might not have had I not turned the stones over myself.

One major lesson learned is that I really should have communicated the issues I had to Auth0. After whining a bit on Twitter about the login screen I got a tweet from them a few hours later asking me to try it again. Auth0 took the initiative to do what even Microsoft’s own properties fail to do: find the parameters that tell the Live servers that a mobile client is knocking at the door. Since then, Auth0 has been nothing but helpful in answering questions and pointing to solutions.

Right there, that should tell you the most important thing you need to know: These guys are serious about customer support. That’s on top of offering a service which goes way beyond just slapping a social auth login onto a website or in an app. One thing you’ll find after signing up for an account is that the help docs are tailored to work with your credentials. Often you can copy-and-paste pre-configured, working code or try it live from within the documentation. How cool is that?

Before reading on, I’d encourage you to sign up for an account. It’s free and will let you try out some of the things that follow. A downloadable code sample is at the end of this post.

Successful Ingredients for Authentication and Authorization

To me, the number one goal is for the user to have a smooth, secure experience. Like many things that share this design goal, this translates into more complexity for the developer. Granted, some of the issues that may occur are such edge cases that they have to be ignored. Yet there are things that may happen infrequently but still have a major impact on perception. For example, if a user cancels logging in (this might occur just from inadvertently hitting the back button) how impressive is it if your app freezes or crashes? Or hard as it may be to believe, your app may not be used every day or every week or even once a month. What do you do when the user fires it up after a long while and forgets which social provider login was used?

My number two goal is to keep my server assets secure, controlling who has access and which things they can view or modify. This isn’t some IT Admin lockdown control-freak thing. Without a reasonable level of security you open up your web services and user data to exploitation or destruction.

Since my services are dependent on OAuth2 that means the authentication vehicle is the JWT. It’s worth mentioning again that a JWT is encoded, not encrypted. The only protection mechanisms you have are the JWT’s signature – an HMAC of the header + payload sections of the JWT using the Client Secret (which should never, ever, be included in any client-side code) – and HTTPS. Other than for testing, never send JWTs over an insecure pipe.

Using a JWT means I need to know, among other items, three important things:

  1. Is it authentic?
  2. When does it expire?
  3. Who is the intended audience (and, no, that’s not the same as considering who you’re writing for, but rather the backend service)

Together, these establish the validity of the token. If don’t validate them, neither my service nor transactions with clients are secure.

Mixing it up

Putting the right pieces together in both the client and server-side code is a combination of having the right components, coordinating the flow of authorization and keeping track of tokens (for reuse) and expiration (requiring refreshing a token). Here’s a walk-through using an Auth0 Application, Auth0 API, a WebAPI/OWIN service and a Windows Phone client app.

It should be pointed out that Auth0 Applications and APIs are not the home of your application or server code. Rather, think of them as configurable authorization gateways that live on Auth0’s servers. Why have both an Auth0 Application and an API? While it’s possible to get by with just one set of credentials, separating the two provides more security (if the client credentials are compromised the server credentials aren’t automatically compromised as well). It also give you more flexibility. Say down the road you have three apps that use the same API but you no longer want to support or publish one of the apps. You could allow only the rest to have access, effectively blocking the discontinued app.

Even more important, since most of your assets, including all users’ data will probably reside on your servers, you can give the less-sensitive app tokens longer expiration times, e.g. 30 days, while keeping them short, e.g. 1 day or 4 hours, for API tokens. This would, for example, allow you to use the Auth0 Users console to block someone who is abusing your terms of service. (This wasn’t actually supported by Auth0 until I requested it…testament once again to their focus on customer service).

Auth0 is fairly straightforward but I had to ask a few questions to figure out how to get server tokens from the client app and renew either client or server tokens. Once I had the answers it became clear that:

  1. When a user is first logging in or the Client token is no longer valid (expired) Auth0 directs the client to the authorize endpoint. Depending on whether the user has ever logged in or the length of time since the last login, the user may be presented with the available choices for social providers or an individual provider’s login screen. In some cases, the provider may have cached the login, which requires no action on the user’s part.
  2. When it comes to client app token renewal or acquiring/renewing a server token, Auth0 has a delegation endpoint that accepts a client ID, a valid JWT, and a target ID. If renewing a Client token, you use the client ID as the target ID. If requesting an API token, the App ID will be the target ID.
    Since the delegation takes place on Auth0’s servers all the verification/validation happens without having to either resort to storing an API secret within an app or maintain the framework to do this yourself. I can’t say it too many times: Never store a secret client-side. There are numerous examples of apps (mobile and otherwise) being reverse-engineered all the time. Amazingly, even people that should know better do it (talking to you Visual Studio team – http://www.visualstudio.com/integrate/get-started/get-started-auth-oauth2-vsi)!

If you haven’t already done so, I’d recommend creating an Auth0 account and set up both a client app and an API for testing and playing around.

The first thing I recommend after creating an Auth0 account is to pick two or more social auth providers. This is done under the Connections section (found on the left-hand pane). The ones I’ve chosen to offer users are Google, Twitter, Microsoft and Facebook. After enabling a provider, you can try out logging in, which will give you a good idea of what information is returned by each.

The next step is to create an Application. Select Applications and then the new application button. In this case, choose a Windows Phone app. Enter a friendly name and choose which of the auth providers you want to use. The next page you’ll see is the Tutorial page, custom-configured to match your App’s settings. For now, there’s nothing that needs to be changed. But here a couple things to note:

  1. I would recommend leaving the callback URL as the auto-generated default. In practice, you shouldn’t need it because the app will not share a Client ID with the API
  2. At the bottom of the page, you’ll see the token expiration duration. The default duration is 36,000 seconds (10 hours). When testing or debugging it is really useful to be able to change this at will. (I’ve set it to as little as 120 seconds). This is a luxury you can’t obtain directly with social providers. Sometimes, I’ve switched to other tasks while waiting 24 hours to come back and see what happens when a token expired. You don’t have to do this with Auth0.
  3. If you select Facebook as a social provider you’ll need to register the app with the Facebook developer site. Not all providers, namely Microsoft, Google and Twitter, require this step.

I’ve provided a working sample with this post, but scanning the Tutorial page will give you an idea of how to implement Auth0 in a client app, including which NuGet package to install.

After creating a client Application, select APIs from the left-hand pane and create a new API. You’ll see there are (currently) 10 options. For our purposes, choose the OWIN option. This will customize the tutorial to be as close as possible to the sample code I’m providing.

Once both are created, you’re ready to create the Visual Studio projects (or use the sample code provided):

  1. Launch Visual Studio using Run as Administrator.
  2. In Visual Studio, right-click on your Solution and add a new project, selecting Web > ASP.NET Web Application. (Avoiding the cruft of the typical MVC template). Check the box for Web API:
  3. Open the NuGet console and run the following:
    1. Install-Package Microsoft.Owin.Security.Jwt
    2. Update-Package System.IdentityModel.Tokens.Jwt
    3. Update-Package Newtonsoft.Json
    4. Install-Package Microsoft.Owin.Host.SystemWeb (Note: Without this one, OWIN will never invoke the Startup.Auth.cs code shown below).
  4. Optionally, open the project’s properties, navigate to Web and change the server from IIS Express to Local IIS. This allows you to choose a Project Url using the machine name, e.g. http://<MY_MACHINE>/<my_project_name>, which makes it possible to test using actual devices or to view the Web API from another desktop machine. Hit the Create Virtual Directory button.NOTE: I found that I’m unable to see a local machine from some devices or WP 8.1 emulators. After looking at workarounds, which all seemed to require opening a firewall port, I just deployed the Web API to Azure and am using that website for testing.
  5. Open Web.Config and if there is not already an appSettings section, create it and add keys for the Auth0 Client ID, Client Secret and Domain, e.g:

    
    <appSettings>
     <add
     key="Auth0:ClientId"
     value="CLIENT_ID" />
    
    <add
     key="Auth0:ClientSecret"
     value="CLIENT_SECRET" />
    
    <add
     key="Auth0:Domain"
     value="https://DOMAIN" />
    
    </appSettings>
    
    

    (These keys can be found in the Settings section for the API on Auth0’s site under Apps/APIs).

  6. In the Project add a new class file to App_Start called “Startup.Auth.cs” and add the following code:
    using System.Configuration;
    using Microsoft.Owin.Security;
    using Microsoft.Owin.Security.DataHandler.Encoder;
    using Microsoft.Owin.Security.Jwt;
    using Owin;
    
    namespace auth0JWT2API
    {
     public class Startup
     {
     public void Configuration(IAppBuilder app)
     {
     var issuer = ConfigurationManager.AppSettings["Auth0:Domain"];
     var audience = ConfigurationManager.AppSettings["Auth0:ClientId"];
     var secret = TextEncodings.Base64Url.Decode(
     ConfigurationManager.AppSettings["Auth0:ClientSecret"]);
    
    // Api controllers with an [Authorize] attribute will be validated with JWT
     app.UseJwtBearerAuthentication(
     new JwtBearerAuthenticationOptions
     {
     AuthenticationMode = AuthenticationMode.Active,
     AllowedAudiences = new[] {audience},
     IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
     {
     new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
     }
     });
     }
     }
    }
    
  7. Create a WebApiConfig.cs file in App_Start:
    using System.Web.Http;
    namespace auth0JWT2API
    {
     public static class WebApiConfig
     {
     public static void Register(HttpConfiguration config)
     {
     // Web API routes
     config.MapHttpAttributeRoutes();
     }
     }
    }
    
    
  8. Open Global.asax.cs and edit it to:
    using System.Linq;
    using System.Web;
    using System.Web.Http;
    
    namespace auth0JWT2API
    {
     public class WebApiApplication : HttpApplication
     {
     protected void Application_Start()
     {
     GlobalConfiguration.Configure(WebApiConfig.Register);
    
    var config = GlobalConfiguration.Configuration;
    
    // Ignores browsers like Chrome requesting XML
     var appXmlType =
     config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
     config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
     }
     }
    }
    
    
  9. Next, in the Controllers folder, add a new empty controller named “PublicController.cs”. This, as you might guess, will be a controller that is open, i.e. no authorization is required.
    using System.Web.Http;
    namespace auth0JWT2API.Controllers
    {
     public class PublicController : ApiController
     {
     [Route("v1/public")]
     public string Get()
     {
     return "You've reached the open controller.";
     }
     }
    }
  10. Create a second controller called “PrivateController.cs” and add this code to it:
    using System;
    using System.Collections.Generic;
    using System.Security.Claims;
    using System.Web.Http;
    
    namespace auth0JWT2API.Controllers
    {
     [Authorize]
     public class AuthenticatedController : ApiController
     {
     [Route("v1/private")]
     public IEnumerable<string> Get()
     {
     var identity = User.Identity as ClaimsIdentity;
     if (identity == null) return new[] { "nuthin'" };
    
    var exp = identity.FindFirst("exp").Value;
     return new[] { "Auth user 1", "Auth user 2", String.Format("exp: {0}", exp) };
     }
     }
    }
    
    
  11. In the project’s properties, select Web and edit the Start URL to http://<MACHINE NAME>/auth0JWT2API/v1/public. Build and run the Web API. You should see a browser launch and, depending on which it is, either display this small JSON array or ask what you want to do with the JSON file:
  12. Try replacing “public” in the URL with “private”. You should get a message stating“Authorization has been denied for this request.”

    The reason being, of course, that no JWT bearer token was supplied to the controller, which is globally protected by the “[Authorize]” attribute.

So, great. Now I have a Web API that supports OAuth2 with both open and restricted routes. But how do I test this to make sure it really works? For one thing, you’ll get the same denied message whether you don’t supply a token or the token is invalid. For another, you’ll simply be unable to return anything useful.

Using a Client App to Test the API

It’s time to create a new client app. In this case, I’m making that a Windows Phone 8.0 Silverlight app.

  1. Add a new project in Visual Studio, selecting whatever Windows Phone template you like that is compatible with your test device.
  2. Using the NuGet Console, add the following packages:
    1. Install-Package Microsoft.Net.Http
    2. Install-Package Newtonsoft.Json
    3. Install-Package Auth0.WindowsPhone
  3. Create a class file named “Auth0UserProfile.cs” and add the following code. This gives us a tidy way to work with the decoded JSON:
    using System.Collections.Generic;
    namespace auth0Jwt2WP
    {
     public class Auth0UserProfile
     {
     public string clientID { get; set; }
     public string created_at { get; set; }
     public string email { get; set; }
     public List<string> emails { get; set; }
     public string family_name { get; set; }
     public string given_name { get; set; }
     public List<Identity> identities { get; set; }
     public string locale { get; set; }
     public string name { get; set; }
     public string nickname { get; set; }
     public string picture { get; set; }
     public string user_id { get; set; }
    
    public class Identity
     {
     public string access_token { get; set; }
     public string provider { get; set; }
     public object user_id { get; set; }
     public string connection { get; set; }
     public bool isSocial { get; set; }
     public string access_token_secret { get; set; }
     }
     }
    }
    
    
  4. Create another class file named “DecoderUtils.cs” and add the following code:
    using System;
    using System.Diagnostics;
    using System.Text;
    using Newtonsoft.Json;
    
    namespace Auth0JwtWP
    {
     public class DecoderUtils
     {
     public static TokenExpirationInfo GetTokenExpirationInfo(string token)
     {
     try
     {
     // Decode the token payload to get the expiration date
     var decodedPayload = DecodeJwtPayload(token);
    
    var decodedPayloadObj = JsonConvert.DeserializeObject(decodedPayload) as dynamic;
    
    var tokenExpirationInfo = new TokenExpirationInfo
     {
     Expiration = decodedPayloadObj.exp,
     IssuedAt = decodedPayloadObj.iat
     };
    
    return tokenExpirationInfo;
     }
     catch (Exception err)
     {
     Debug.WriteLine(err.Message);
    
    return null;
     }
     }
     public static string DecodeJwtPayload(string token)
     {
     try
     {
     var parts = token.Split('.');
     var payload = parts[1];
    
    var payloadBytes = Base64UrlDecode(payload);
    
    return Encoding.UTF8.GetString(payloadBytes, 0, payloadBytes.Length);
     }
     catch (Exception err)
     {
     Debug.WriteLine(err.Message);
    
    return null;
     }
     }
     public static byte[] Base64UrlDecode(string input)
     {
     try
     {
     var output = input;
     output = output.Replace('-', '+'); // 62nd char of encoding
     output = output.Replace('_', '/'); // 63rd char of encoding
     switch (output.Length%4) // Pad with trailing '='s
     {
     case 0:
     break; // No pad chars in this case
     case 2:
     output += "==";
     break; // Two pad chars
     case 3:
     output += "=";
     break; // One pad char
     default:
     throw new Exception("Illegal base64url string!");
     }
     var converted = Convert.FromBase64String(output); // Standard base64 decoder
     return converted;
     }
     catch (Exception err)
     {
     Debug.WriteLine(err.Message);
    
    return null;
     }
     }
    
    /// <summary>
     /// Returns decoded information containing a JWT's expiration and the time it was issued.
     /// The calculated lifespan is provided as a convenience.
     /// </summary>
     public class TokenExpirationInfo
     {
     public long IssuedAt { get; set; }
     public long Expiration { get; set; }
    
    public long Lifespan
     {
     get { return Expiration - IssuedAt; }
     }
     }
     }
    }
    
    
  5. Open MainPage.xaml and replace all the code with:
    <phone:PhoneApplicationPage
     x:Class="auth0Jwt2WP.MainPage"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
     xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     mc:Ignorable="d"
     FontFamily="{StaticResource PhoneFontFamilyNormal}"
     FontSize="{StaticResource PhoneFontSizeNormal}"
     Foreground="{StaticResource PhoneForegroundBrush}"
     SupportedOrientations="Portrait" Orientation="Portrait"
     shell:SystemTray.IsVisible="True"
     DataContext="{Binding RelativeSource={RelativeSource Self}}">
    
    <!--LayoutRoot is the root grid where all page content is placed-->
     <Grid x:Name="LayoutRoot" Background="Transparent">
     <Grid.RowDefinitions>
     <RowDefinition Height="42"/>
     <RowDefinition Height="*"/>
     <RowDefinition Height="96"/>
     </Grid.RowDefinitions>
     <!--TitlePanel contains the name of the application and page title-->
     <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="6">
     <TextBlock Text="AUTH0 JWT TEST" Style="{StaticResource PhoneTextNormalStyle}"
     Margin="0"/>
     </StackPanel>
    
    <ListBox Grid.Row="1" VerticalAlignment="Top" Margin="12 0 12 0"
     Name="OutputListbox" ItemsSource="{Binding OutputItems}"
     FontSize="16" Background="#2400F9FF" Padding="4" />
    
    <Button Grid.Row="2" Content="C Token" HorizontalAlignment="Left" Margin="10"
     VerticalAlignment="Bottom" Tap="JustClientToken" />
    
    <Button Grid.Row="2" Content="API Call" HorizontalAlignment="Center" Margin="10"
     VerticalAlignment="Bottom" Tap="TriggerApiCall" />
    
    <Button Grid.Row="2" Content="Clear" HorizontalAlignment="Right" Margin="10"
     VerticalAlignment="Bottom"
     Tap="ClearOutput" />
    
    </Grid>
    
    </phone:PhoneApplicationPage>
    
    
  6. Open MainPage.xaml.cs and replace all the code with:
    </pre>
    using System.Linq;
    using Auth0.SDK;
    using Newtonsoft.Json.Linq;
    using System;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.IO;
    using System.IO.IsolatedStorage;
    using System.Net.Http;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Input;
    
    namespace auth0Jwt2WP
    {
     public partial class MainPage
     {
     private const string Auth0Domain = "<YOUR DOMAIN>";
     private const string Auth0ClientID = "<APP CLIENT ID>";
     private const string Auth0ApiID = "<API ID>";
    
    private const string BaseAddress = "<WEB API URI>";
    
    // Determines when we'd attempt to refresh a token, e.g. if expiration is 30 days, .1 = any time within 3 days of that.
     private const double TokenLifespanOffset = .1;
    
    private readonly IsolatedStorageSettings _applicationSettings = IsolatedStorageSettings.ApplicationSettings;
     private readonly ObservableCollection<string> _outputItems;
     private int _apiCallCount;
    
    private string _auth0ApiIdToken;
     private long _auth0ApiIdTokenExpiration;
     private long _auth0ApiTokenLifespan;
    
    private string _auth0ClientIdToken;
     private long _auth0ClientIdTokenExpiration;
     private long _auth0ClientTokenLifespan;
     private string _auth0Provider;
    
    private string _state;
    
    // Constructor
     public MainPage()
     {
     InitializeComponent();
    
    _outputItems = new ObservableCollection<string>();
     }
    
    public ObservableCollection<string> OutputItems
     {
     get { return _outputItems; }
     }
    
    private async Task TestAPICall()
     {
     try
     {
     _apiCallCount++;
    
    _outputItems.Add(String.Empty);
     _outputItems.Add(String.Format("API called: {0}", _apiCallCount));
    
    var completed = await CheckApiToken();
    
    if (completed)
     {
     try
     {
     Dispatcher.BeginInvoke(() => _outputItems.Add("Making remote request..."));
     var httpClient = new HttpClient { BaseAddress = new Uri(BaseAddress) };
    
    httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + _auth0ApiIdToken);
    
    var response = await httpClient.GetAsync(httpClient.BaseAddress + "v1/private");
    
    if (response.IsSuccessStatusCode)
     {
     var output = await response.Content.ReadAsStringAsync();
    
    Dispatcher.BeginInvoke(() => _outputItems.Add(String.Format("results: {0}", output)));
     }
     else
     {
     throw new Exception(response.StatusCode.ToString());
     }
     }
     catch (Exception err)
     {
     _outputItems.Add(err.Message);
     }
     }
     else
     {
     Dispatcher.BeginInvoke(() => _outputItems.Add("API call failed."));
     }
     }
     catch (Exception err)
     {
     Debug.WriteLine(err.Message);
    
    Dispatcher.BeginInvoke(() => _outputItems.Add(err.Message));
     }
     }
    
    private async Task<bool> CheckApiToken()
     {
     var apiTokenCheckSucceeded = false;
    
    try
     {
     // Any operation dependent on having a valid client token should always call this first.
     var clientCheckCompleted = await CheckClientToken();
    
    if (clientCheckCompleted)
     {
     Dispatcher.BeginInvoke(() => _outputItems.Add("Client token check okay."));
    
    // Do we need a new API token?
     var currentEpochTime = GetSecondsSinceEpoch();
     _applicationSettings.TryGetValue("auth0ApiIdToken", out _auth0ApiIdToken);
     _applicationSettings.TryGetValue("auth0ApiIdTokenExpiration", out _auth0ApiIdTokenExpiration);
     _applicationSettings.TryGetValue("auth0ApiTokenLifespan", out _auth0ApiTokenLifespan);
    
    var offsetExpTime = _auth0ApiIdTokenExpiration - (_auth0ApiTokenLifespan * TokenLifespanOffset);
    
    if (currentEpochTime < offsetExpTime && _auth0ApiIdToken != null)
     {
     Dispatcher.BeginInvoke(() => _outputItems.Add("API token is still good."));
    
    return true; // token is still good.
     }
    
    var delegateTokenObject = await GetDelegationToken(Auth0ApiID, Auth0ClientID, _auth0ClientIdToken);
    
    // Get the new API token
     _applicationSettings["auth0ApiIdToken"] = _auth0ApiIdToken = delegateTokenObject.IdToken;
    
    var tokenExpirationInfo = DecoderUtils.GetTokenExpirationInfo(_auth0ApiIdToken);
    
    _applicationSettings["auth0ApiIdTokenExpiration"] =
     _auth0ApiIdTokenExpiration = tokenExpirationInfo.Expiration;
    
    // Get the API token lifespan - used with subsequent transactions to determine if a new token is required
     _applicationSettings["auth0ApiTokenLifespan"] =
     _auth0ApiTokenLifespan = tokenExpirationInfo.Lifespan;
    
    apiTokenCheckSucceeded = true;
    
    Dispatcher.BeginInvoke(() => _outputItems.Add("A new API token was obtained."));
     }
     else
     {
     Dispatcher.BeginInvoke(() => _outputItems.Add("Client token check failed."));
     }
     }
     catch (Exception err)
     {
     Debug.WriteLine(err.Message);
     }
    
    return apiTokenCheckSucceeded;
     }
    
    private async Task<bool> CheckClientToken()
     {
     try
     {
     // Do we need a new client token?
     _applicationSettings.TryGetValue("auth0ClientIdToken", out _auth0ClientIdToken);
     _applicationSettings.TryGetValue("auth0ClientIdTokenExpiration", out _auth0ClientIdTokenExpiration);
     _applicationSettings.TryGetValue("auth0ClientTokenLifespan", out _auth0ClientTokenLifespan);
    
    var currentEpochTime = GetSecondsSinceEpoch();
    
    var offsetExpTime = _auth0ClientIdTokenExpiration - (_auth0ClientTokenLifespan * TokenLifespanOffset);
    
    if (currentEpochTime < offsetExpTime && _auth0ClientIdToken != null)
     {
     Dispatcher.BeginInvoke(() => _outputItems.Add("Client token is still good."));
    
    return true; // token is still good
     }
    
    if (currentEpochTime < _auth0ClientIdTokenExpiration && _auth0ClientIdToken != null)
     {
     // Renew with a delegate token. This avoids having the user log in again
     var delegateTokenObject =
     await GetDelegationToken(Auth0ClientID, Auth0ClientID, _auth0ClientIdToken);
    
    // Get the new client token
     _applicationSettings["auth0ClientIdToken"] = _auth0ClientIdToken = delegateTokenObject.IdToken;
    
    _outputItems.Add("Client token was renewed.");
     }
     else // Get a new token via user login
     {
     var auth0 = new Auth0Client(Auth0Domain, Auth0ClientID);
    
    // Try to use the last-logged provider
     _applicationSettings.TryGetValue("auth0Provider", out _auth0Provider);
    
    // If _auth0Provider is null, will present all providers to user
     var user = await auth0.LoginAsync(_auth0Provider);
    
    _state = user.State;
    
    // Get the client token
     _applicationSettings["auth0ClientIdToken"] = _auth0ClientIdToken = user.IdToken;
    
    // Get the user profile so we extract the provider, e.g. "windowslive", for future streamlined login
     // Note: In a production app, this would also be the source of user name, email and other useful values
     var profile = user.Profile.ToObject<Auth0UserProfile>();
    
    _applicationSettings["auth0Provider"] =
     _auth0Provider = profile.identities.FirstOrDefault().provider;
    
    _outputItems.Add("A new Client token was obtained.");
     }
    
    var tokenExpirationInfo = DecoderUtils.GetTokenExpirationInfo(_auth0ClientIdToken);
    
    // Get the client token expiration
     _applicationSettings["auth0ClientIdTokenExpiration"] =
     _auth0ClientIdTokenExpiration = tokenExpirationInfo.Expiration;
    
    // Get the client token lifespan - used with subsequent transactions to determine if a new token is required
     _applicationSettings["auth0ClientTokenLifespan"] =
     _auth0ClientTokenLifespan = tokenExpirationInfo.Lifespan;
    
    return true;
     }
     catch (Exception err)
     {
     Debug.WriteLine(err.Message);
    
    throw new Exception(err.Message);
     }
     }
     private async void JustClientToken(object sender, GestureEventArgs e)
     {
     try
     {
     _outputItems.Add("Starting JustClientToken.");
    
    await CheckClientToken().ContinueWith(async task =>
     {
     await Task.Run(() =>
     {
     if (!task.IsFaulted && task.Result)
     {
     Dispatcher.BeginInvoke(() => _outputItems.Add("JustClientToken completed okay."));
     }
     else
     {
     Dispatcher.BeginInvoke(() => _outputItems.Add("JustClientToken failed."));
     }
     });
     });
     }
     catch (Exception err)
     {
     _outputItems.Add(err.Message);
     }
     }
     private async void TriggerApiCall(object sender, GestureEventArgs e)
     {
     await TestAPICall();
     }
     private void ClearOutput(object sender, GestureEventArgs e)
     {
     _outputItems.Clear();
     }
     public async Task<DelegateTokenObject> GetDelegationToken(string targetClientId, string clientId,
     string clientToken)
     {
     if (string.IsNullOrEmpty(clientToken))
     {
     throw new InvalidOperationException("You need to login first or specify a value for id_token parameter.");
     }
    
    try
     {
     var endpoint = string.Format("https://{0}/delegation", Auth0Domain);
    
    var parameters = String.Format(
     "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&id_token={0}&target={1}&client_id={2}&state={3}",
     clientToken,
     targetClientId,
     clientId,
     _state);
    
    var httpClient = new HttpClient();
    
    var request = new HttpRequestMessage(HttpMethod.Post, endpoint)
     {
     Content = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes(parameters)))
     };
    
    request.Content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
    
    var result = await httpClient.SendAsync(request);
    
    var content = await result.Content.ReadAsStringAsync();
    
    var delegationResult = JObject.Parse(content) as dynamic;
    
    var delegateTokenObject = new DelegateTokenObject
     {
     Expires = delegationResult.expires_in,
     IdToken = delegationResult.id_token,
     };
    
    return delegateTokenObject;
     }
     catch (Exception err)
     {
     Debug.WriteLine(err.Message);
    
    return null;
     }
     }
    
    public long GetSecondsSinceEpoch()
     {
     var t = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
     return (long)t.TotalSeconds;
     }
    
    public class DelegateTokenObject
     {
     public long Expires { get; set; }
     public string IdToken { get; set; }
     }
     }
    }

    Note: Don’t forget to substitute your particular values for the Auth0 domain, client ID, API client ID and API base address!

Select an emulator and run the app. On the screen, hit the “C Token” button to obtain just a client token. You should be redirected to a web-browser control page showing the social auth providers you selected. Choose one and log in. You’ll then be taken back into the app page and should see something like the third image in this series:

Find this line in the CheckClientToken method:


// Get the client token


_applicationSettings[“auth0ClientIdToken”] = _auth0ClientIdToken = user.IdToken;

If you set a breakpoint, you can grab the token and try it out in a browser-based decoder. Auth0 provides one at http://jwt.io/. If you paste the encoded JWT into the window on the left, you’ll see the decoded results to the right. If you copy and paste your client secret (found in the Settings section of your client Application on the Auth0 website) and check the “secret base64 encoded” checkbox, you should see your JWT signature verified:

Now try clicking on the API Call button. You should see something like this:

A bit of explanation: The output is letting you know that a new client token was obtained, it was verified, and a new API token was obtained. This was then used to make calls to the remote server. In this case, I’ve drastically shortened the expiration times for the client and API tokens (a great feature of Auth0 when it comes to testing). I’ve also used an offset factor to create a “refresh window” for the tokens, e.g:

// Determines when we’d attempt to refresh a token, e.g. if expiration is 30 days, .1 = any time within 3 days of that.

private
const
double
TokenLifespanOffset = .1;


To understand this, consider what happens when you cache tokens on the client vis-à-vis the user’s interaction with the app. You want the user to have a friction-free experience, i.e. not have to log in every time they use the app. Yet tokens expire. And there’s security to consider. Let’s say, for example, that we decide to allow the app to have a 30-day expiration for the client token. That’s pretty generous, but at least some users won’t use an app that often. It seems reasonable for them to have to log in again (using their social credentials, the choice of which is also cached…more on that minor potential Achilles heel later). For more frequent users (and we want to reward their loyalty, right J) for 27 days the cached token will be used and no calls will need to be made to Auth0’s servers to renew it. If the user uses the app any time within the last three days of the cycle, a new token will be issued.

The API tokens (which generally have a shorter lifespan, e.g. 8 hours) work in a similar way. But here the purpose is simply to avoid trying to use an expired token to make a remote server call. While the JWT specification provides for an optional grace period on using an expired token, I’d prefer to have some leeway. Also, as with client tokens, this avoids excessive calls to Auth0.

Again, there is value in having separate client and API authentication. This gives you the latitude you need to have independent expiration lengths. As mentioned, it might be just fine to allow a client to have a token that lasts 30 days. But the API may need more protection than that, hence shorter token lifespans. Consider for example a set of applications and services revolving around some social scenario. Then you get a user who is a troll or malicious that needs to be locked out, whether temporarily or permanently. Auth0 has a provision for blocking users, which takes effect immediately – but it only prevents the issuance of new tokens. So if you have a troll with a cached token good for hitting the API for 10 days, you’d have to rely on some other way to keep them from wreaking havoc while not impacting other users. With an independent API authorization you could, for instance, temporarily shorten the expiration time. This won’t noticeably impact users. It will only increase authentication calls for API tokens.

Granted, this scenario alone is enough to make me want to use additional ways within my own user database and API to deal with such users. But it is still helpful to have such a combination blocking and short expiration times. The latter also limits the ability of XSRF (cross-site request forgery) attacks.

It’s worth noting that Auth0 (as of version 1.2.2 of their Windows Phone SDK) fixed an UX issue when users are blocked. Before, you’d be stuck in the web browser control. Now it returns control to the app and back to the calling code. There is an error message explaining the user is blocked, which you could trap for if you desire to do more than just deliver a generic message. (I wouldn’t give them that message, but might deliver an explanatory message in general terms).

A Minor Problem

I haven’t quite figured this one out. Suppose a user doesn’t log in for a long time or uninstalls/re-installs my app (or installs it on a new device). Since the “preferred” social auth provider will no longer be cached if the user doesn’t remember which one they used before, then it’s possible to lose the association between the user and all their data. For now, I’ll be relying on providing a support email address to handle this edge case, but it still bothers me. I could add some sort of check that tries to see if there is some common association between the profiles provided by each provider, but that can be problematic in other ways.

Download Sample

http://stonetip.com/auth0/auth0JWT2_sample.zip