.NET Zone is brought to you in partnership with:

Den is a DZone Zone Leader and has posted 460 posts at DZone. You can read more from them at their website. View Full User Profile

How To Manage API Keys in a Windows Phone App

09.25.2013
| 2377 views |
  • submit to reddit

As I was working on Beem Plus,  I noticed that I ended up with a pile of services that I was using in different parts of the application, including Azure Mobile ServicesSkyDrive, and Last.fm.  Each of these services relies on some authentication mechanism, that subsequently relies on an individually issued API key that is passed to the client before any operations can be invoked.

Initially, I was simply hard-coding the API key into the application. For example, the Azure Mobile Services SDK exposes the MobileServiceClient class that accepts the key as one of the constructor arguments:

public static MobileServiceClient MobileService = new MobileServiceClient(
    "https://local-ams-instance/",
    "YOUR_API_KEY_HERE"
);

Pretty straightforward, right? There is nothing that could potentially go wrong - given that you are using the correct key, AMS data will be accessible to your app. Now let's say you have the LastFmClient class that exposes the GetMobileSession function:

public async void GetMobileSession(string userName, string password, Action<LastFmAuthResponse> onCompletion)
{
    var parameters = new Dictionary<string, string>();
    parameters.Add("username", userName);
    parameters.Add("password", password);
    parameters.Add("method", "auth.getMobileSession");
    parameters.Add("api_key", CoreViewModel.Instance.ApiKeys.LastFmKey);

    string signature = GetSignature(parameters);

    string comboUrl = string.Concat("method=auth.getMobileSession", "&api_key=", CoreViewModel.Instance.ApiKeys.LastFmKey,
        "&username=", userName, "&password=", password, "&api_sig=", signature);

Yet another place where you need to keep track of an API key.

On the other hand, we have the SkyDrive key bound in XAML:

<live:SignInButton x:Name="btnSignIn" ClientId="YOUR_KEY_HERE" 
                   Scopes="wl.signin wl.basic wl.offline_access wl.skydrive wl.skydrive_update" Branding="Skydrive" TextType="Connect" Visibility="Collapsed"></live:SignInButton>

What is the issue here? Think about maintaining all the API keys when you need to change the infrastructure. What happens if you decide to make the project public and yet don't want to give all other developers access to your data? 

Although manual changes can be done, chances are at some point you will forget to remove a key and all of a sudden you are faced with a different problem - like the need to reset the key and cause a disruption in the application workflow until the new key is pushed into production.

Instead, consider this XML file:

<keys>
  <key type="msa" value=""></key>
  <key type="lastfm" value=""></key>
  <key type="lastfmsecret" value=""></key>
  <key type="difm" value=""></key>
  <key type="zumo" value=""></key>
  <key type="zumourl" value=""></key>
</keys>

It is extremely easy to put all the keys in one place, and later read them through a standard serialization routine:

public static KeyContainer GetKeys(string name)
{
    XDocument keyDocument = XDocument.Load(name);

    KeyContainer container = new KeyContainer();
    container.DiFmPremiumKey = (from c in keyDocument.Root.Elements() where c.Attribute("type").Value == "difm"
                                select c).FirstOrDefault().Attribute("value").Value;
    container.LastFmKey = (from c in keyDocument.Root.Elements()
                                where c.Attribute("type").Value == "lastfm"
                                select c).FirstOrDefault().Attribute("value").Value;
    container.MsaKey = (from c in keyDocument.Root.Elements()
                                where c.Attribute("type").Value == "msa"
                                select c).FirstOrDefault().Attribute("value").Value;
    container.ZumoKey = (from c in keyDocument.Root.Elements()
                                where c.Attribute("type").Value == "zumo"
                                select c).FirstOrDefault().Attribute("value").Value;
    container.ZumoUrl = (from c in keyDocument.Root.Elements()
                         where c.Attribute("type").Value == "zumourl"
                         select c).FirstOrDefault().Attribute("value").Value;
    container.LastFmSecret = (from c in keyDocument.Root.Elements()
                              where c.Attribute("type").Value == "lastfmsecret"
                              select c).FirstOrDefault().Attribute("value").Value;

    return container;
}

KeyContainer can then be part of a ViewModel and can be easily bound to any UI or code-behind targets.

public class KeyContainer
{
    public string DiFmPremiumKey { get; set; }
    public string LastFmKey { get; set; }
    public string LastFmSecret { get; set; }
    public string ZumoKey { get; set; }
    public string ZumoUrl { get; set; }
    public string MsaKey { get; set; }
}

In addition to the benefit of having a unified key block, you will also be able to have different build configurations, with different files pushed into production and source control services. For example, Beem Plus ships with pre-filled API keys, while when it goes on GitHub (build through a different iteration) it ships with an APIKeyManifest.xml that does not contain any values, yet preserves the layout. In such a manner, it is also very easy to fetch a custom API container from a remote source without impacting the core code base.