Authenticating a OneDrive Account

It seems, these days, that everyone is going "Cloud Crazy." I mean, really, what is the big deal about the cloud?

Well, okay, it's massively accessible and synced across global data centres around the world; but, outside of that sphere, there's nothing massively different about cloud versus a traditional hosting account on a standard server. You could. if you wanted to, set up your own servers in strategic locations around the world, develop your own software to keep them in sync, and then just run standard server operating systems on them, and it would probably work out a lot cheaper.

Before we get sidetracked, however, the one big benefit of globally shared cloud storage is that it's globally shared. The key part here is "Shared."

In today's ever-connected world, we rely more and more on being able to access all of our data on all of our devices, and this is where global shared storage really shines. The problem is, the powers that be (and by that, I mean the marketers and cloud software sales people) would have you believe that you have to be using a certain operating system, running on a specific device that uses special cloud-based software to share your files in this manner.

Case in fact, Windows 8 Store apps :-)

If you read any of the books out there, or many of the blog posts floating around, it's all about Windows RT, Windows 8, Store Apps, and all of this stuff that's now being aligned with the new Microsoft "One Vision."

So, What's This Got to Do with OneDrive?

Well, first off, as you already know, OneDrive is Microsoft's entry into the global cloud storage market. What's perhaps not so widely published (there are some articles, but most of them are quite difficult to find) is the fact that you actually can use OneDrive (and many other Live APIs) from within more traditional .NET apps, such as ASP.NET MVC, Win-form, and WPF-based projects.

What I want to show you in this article, and the next, is how you use the LiveSDK rest-based interface to interact with a standard Windows Forms desktop-based application. The code I show, however, can be very easily adapted to other application scenarios very easily.

We'll do four things. In this first part, I'll:

  • Authenticate the user to OneDrive
  • Get a file listing of available files

Then, in the next article, I'll show how to use that authentication to:

  • Upload a file
  • Download the file we uploaded

All set? Great, let's get started.

Authenticating Your Application

Like any of the popular frameworks these days, you first need to generate yourself an application key. To do this, you need an MS-Live/MSDN account. Having a Microsoft account should not be a challenge for most Windows developers. If you have a Hotmail/Outlook email account, have (or had) an MS-Messenger account, or have a Skype account, you'll most likely (even though you may not realise it) have a suitable account to use.

I don't have space in this article to go through all the possible variations on getting/signing up for an account, so I'm afraid I'll have to leave that one up to you. Once you have an account, however, the first thing you need to do is browse to:

http://msdn.microsoft.com/onedrive

and sign in (If you're not already).

Once you're on the main OneDrive developers page, you need to click the menu option marked 'Dashboard.' This should take you to your "Microsoft Account Developer Centre", which should look something like this:

Auth1
Figure 1: The "Microsoft Account Developer Centre"

I already have an application key defined, which, as you can see, is listed on my front page already, Yours, if it's the first time you've used it, will likely be empty. To create a new key, click 'Create Application,' and then fill in the name of your application on the first tab that appears. Click the 'I accept' button to continue.

You're allowed to create up to 100 application keys the last time I checked. However, as is always the case with these things, if it's unclear, check it!! (It'll not be the first time I've breached Ts&Cs without realising.)

Once you've accepted, and moved on to the next page, you should see something like:

Auth2
Figure 2: After you've been accepted

Most of what you see in the following dialogs is aimed at web apps, but because we're going to be using win-forms, we don't need to be bothered about most of it. Click the 'API Settings' option, and set 'Mobile or Desktop client app' to 'Yes;' for a desktop app, you MUST make sure that you leave the 'Redirect URL' empty. This URL is used during the OAuth2 stage of interacting with the service. In a desktop app, because we have no public endpoint, the OneDrive service has to be told to use a special internal endpoint. We do this by leaving the redirect URL blank, so that it knows to choose its own.

Click save, and then move to the 'App Settings' page. You should now see something like:

Auth3
Figure 3: The 'App Settings' page

And yes, for those of you who are wondering, I will be deleting this app entry after I finish writing this article!

Make a note of the 'ClientID.' This is the item you'll need to make use of in just a moment. Most of the other stuff is self explanatory, but it doesn't need to be changed to use this account for our demo, so feel free to explore if you want, but for the next part you'll need to fire up Visual Studio.

Getting Down with Some Code

Okay, so now you have a client ID. The next step is to make a start on your project. Hopefully, you've done this many times before, so I'm simply going to tell you to start a standard .NET 4.5 windows forms project. I'll be using VS2013 Enterprise for these articles, but I'll not be doing anything that can't be done with the free/express versions of the same products.

Once you've created your win-forms project, the first thing you need to do is use NuGet to get the required libs for interfacing to the OneDrive API. Perform your usual method of using NuGet and search for the "LiveSDK" package. As of this writing, the current version is 5.6, and it'll be a minimum of this version you need to follow along with this article.

The NuGet page for the libs can be found here:

https://www.nuget.org/packages/LiveSDK

Once you've installed the NuGet package, you should have everything you need set up and ready to roll.

Getting Authentication from the User

Before you can do anything with the SDK, you first have to get permission from the user of the application to access their OneDrive account. This shouldn't come as a surprise; it's a standard practice these days.

Request Authentication, pass in your token, get a response back, save that response, and then use that response forever until the user either chooses to re-authenticate or removes permission for the application to access their account.

Unfortunately, it's not quite that simple with OneDrive. The process is still the same, but you can't use the returned token indefinitely. In fact, I've found while writing this article, by default the timeout on any token only seems to be approximately one hour.

We'll come back to this in just a moment. For now, however, add a new from to your project, so that you have a 'Form1' and 'Form2' available.

FYI: I use 'Form2' in a descriptive term here, so the distinction is simple; I actually called my form 'FrmWebBrowser'. It's entirely up to you what you want to name your forms. Just remember that From1 is your main form and Form2 is your WebBrowser form.

Set 'From2's properties to have a 'FormBorderStyle' of 'FixedToolWindow' and 'StartPosition' of 'CenterScreen' and then size it to a suitable size for a web dialog, approximately 500x600 pixels.

Once you've got your form set up, from your toolbox, drop a standard web browser control onto your window and set its 'Dock' property to 'Fill'. You should have something the resembles the following:

Auth4
Figure 4: Dropping a standard web browser control onto your window

Once you've gotten your form set up, you then need to add a bit of code to make the authentication happen. Because we also need to get access to the authentication token, we're also going to add a custom property to the form class so that's also easier to access.

We need to add code to the forms constructor to browse to the Microsoft Accounts authorization page. We do this by navigating to the start page in our constructor and then we attach a document ready handler, which in turn waits until the authentication process has completed.

At this point, we then extract the supplied access token, make it available to the rest of the application, and dismiss the dialog. It's all quite basic stuff, so rather than do a line by line, I'll just present the full form class in just a moment. First, however, we need to discuss the URL used in the sign-in process.

When you want to sign in to a Microsoft Account, you need to provide two items of information in the request. The first is the ClientID you created in your application ID creation previously, using your developer account. The second bit of information needed is a list of the scopes requesting the access that your application desires.

A full list of possible scopes can be found here:

http://msdn.microsoft.com/en-gb/library/dn631845.aspx

For our purposes, however, we only require:

'wl.skydrive_update'

You'll see in the code that we define these as constants in the form class, along with the client ID. Then, we assemble them into the URL:

https://login.live.com/oauth20_authorize.srf

This is the endpoint you need to call to kick start the authorization process. You can see the full URL and required parameters in the following code. Once we call this endpoint, the user should be presented with an authorization form that looks similar to this:

Auth5
Figure 5: The sign-in screen

Once you fill in the form and go through the authentication process, the form should then close and we should, at that point, have an access token available that allows us to make use of the various calls that the OneDrive API has available.

The full code for the Web Browser form is as follows:

using System;
using System.Windows.Forms;

namespace Onedrive
{
   public partial class FrmWebBrowser : Form
   {
      public string AccessToken { get { return _accessToken; }}

      private string _accessToken = string.Empty;

      private const string _scope = "wl.skydrive_update";

      // Remember to change this to your ClientID, this one will be
      // invalid by the time this is published
      private const string _clientID = "00000000481275D0";

      private const string _signInUrl = @"https://login.live.com/
         oauth20_authorize.srf?client_id={0}&redirect_uri=
         https://login.live.com/oauth20_desktop.srf&response_type=
         token&scope={1}";
      private Timer _closeTimer;

      public FrmWebBrowser()
      {
          InitializeComponent();
         StartAuthenticationProcess();
      }

      private void StartAuthenticationProcess()
      {
         AuthenticationBrowser.DocumentCompleted +=
            AuthenticationBrowserDocumentCompleted;
         AuthenticationBrowser.Navigate(string.Format
            (_signInUrl, _clientID, _scope));
      }

      void AuthenticationBrowserDocumentCompleted(object sender,
         WebBrowserDocumentCompletedEventArgs e)
      {
         if (e.Url.AbsoluteUri.Contains("#access_token="))
         {
            var x = e.Url.AbsoluteUri.Split(new[]
               { "#access_token" }, StringSplitOptions.None);
            _accessToken = x[1].Split(new[] {'&'})[0];
            _closeTimer = new Timer {Interval = 500};
            _closeTimer.Tick += CloseTimerTick;
            _closeTimer.Enabled = true;
         }
      }

      private void CloseTimerTick(object sender, EventArgs e)
      {
         _closeTimer.Enabled = false;
         DialogResult = DialogResult.OK;
         Close();
      }
   }
}

We should now be able to instantiate an object of our web browser form when needed, and upon showing it, we should automatically get passed to the authentication process. However, because the access token we get is good for at least an hour, we should ideally have some kind of caching strategy. This prevents us having to ask the user to sign in for every call we make. In reality, what you might want to do is write the access token to a temp file in the applications folder. Doing this would allow you to actually exit the application, and, as long as you ran it again within the hour, you should still be okay. In this case, you would likely want to check for the existence of the file as you start up. If the file exists, you could check the creation date, see how long ago it was, and, if less than an hour, read and use the access token stored within.

For our purposes, however, we're just going to simply keep the string in memory, with a time stamp of its generation time. Then, every time we want to use it, check this time and, if more than an hour has passed, re-request authentication using our auth process.

To assist with this, add a new class to your application. called 'AccessToken.cs', and add the following code to it:

using System;

namespace Onedrive
{
   public class AccessToken
   {
      public string Token
      {
         get { return _token; }
         set
         {
            _token = value;
            _generationTime = DateTime.Now;
         }
      }

      public bool IsValid
      {
         get
         {
            if (string.IsNullOrEmpty(_token)) return false;
            return (DateTime.Now -
               _generationTime).TotalMinutes < 60;
         }
      }

      private string _token = string.Empty;
      private DateTime _generationTime;

   }
}

We now can make use of this new class in our main 'Form1' class by adding the following method:

private string GetAccessToken()
{
   if(myAccessToken != null && myAccessToken.IsValid)
   {
      return myAccessToken.Token;
   }

   using(FrmWebBrowser authBrowser = new FrmWebBrowser())
   {
      if (authBrowser.ShowDialog() != DialogResult.OK) return string.Empty;
      myAccessToken = new AccessToken();
      myAccessToken.Token = authBrowser.AccessToken;
      return myAccessToken.Token;
   }
}

Remember, also, to add a class private variable:

private AccessToken myAccessToken;

to store the retrieved access token.

This wraps everything up neatly so that whenever we call GetAccessToken, we'll automatically go through the authentication process if we don't have a valid token. Or, if our token has timed out, and if it's still valid, will simply just return the token we have.

What's Next?

Now that we can authenticate, and we have an access token, we now can use that token to access the user's OneDrive. To finish off this article, I'll show you how to get a listing of files and folders in the root of the logged-in user's drive. Just as with the authentication, to do anything using the live API, we need to use HTTP-based restful calls. The good news is, because we don't require any user interaction any longer, we can do these using the regular web client.

The first thing we need to do is to get the basic information available about the user's OneDrive. We do that by making a get request to

https://apis.live.net/v5.0/me/skydrive

Onto the end of this URL, we need to add a parameter called 'access_token' that contains the access token we obtained previously, using the authentication process. The results from this call will be a snippet of JSON containing various details about the OneDrive you accessed. That will look like the following:

{
   "id": "folder.4515677xxxxxxxxx",
   "from": {
      "name": null,
      "id": null
   },
   "name": "SkyDrive",
   "description": "",
   "parent_id": null,
   "size": 9514852942,
   "upload_location":
      "https://apis.live.net/v5.0/folder.4515677xxxxxxxxx/files/",
   "comments_count": 0,
   "comments_enabled": false,
   "is_embeddable": false,
   "count": 8,
   "link": "https://onedrive.live.com?cid=4515677xxxxxxxxx",
   "type": "folder",
   "shared_with": {
      "access": "Just me"
   },
   "created_time": null,
   "updated_time": "2014-08-28T11:46:03+0000",
   "client_updated_time": "2013-08-09T14:30:52+0000"
}

Among the data returned, you can see the 'id', 'name', 'description', and most importantly, the 'upload_location'. The upload location is important because it's this path you'll use to get a complete file listing of the contents of the drive.

Once you have this upload folder, you then simply just need to make a get request to it again as was done previously, making sure you append the access token. This will also return JSON data, but this time the data will be an array of file objects listing the files and folders available in the drive:

{
   "data": [
      {
         "id": "folder.4515677bdf99b35f.4515677BDF99B35F!223",
         "from": {
            "name": "Peter Shaw",
            "id": "4515677bdf99b35f"
      },
      "name": "Blog images",
      "description": "",
      "parent_id": "folder.4515677bdf99b35f",
      "size": 66529,
      "upload_location": "https://apis.live.net/v5.0/
         folder.4515677bdf99b35f.4515677BDF99B35F!223/files/",
      "comments_count": 0,
      "comments_enabled": true,
      "is_embeddable": true,
      "count": 3,
      "link": "https://onedrive.live.com/
         redir.aspx?cid=4515677bdf99b35f&page=browse&
         resid=4515677BDF99B35F!223&parId=4515677BDF99B35F!161",
      "type": "album",
      "shared_with": {
         "access": "Shared"
      },
      "created_time": "2009-05-23T10:55:58+0000",
      "updated_time": "2010-09-28T18:30:53+0000",
      "client_updated_time": "2010-09-28T18:30:53+0000"
   }, {
      "id": "file.4515677bdf99b35f.4515677BDF99B35F!1021",
      "from": {
         "name": "Peter Shaw",
         "id": "4515677bdf99b35f"
      },
      "name": "Tiny.png",
      "description": "",
      "parent_id": "folder.4515677bdf99b35f",
      "size": 17630,
      "comments_count": 0,
      "comments_enabled": false,
      "tags_count": 0,
      "tags_enabled": true,
      "is_embeddable": true,
      "picture": "https://public.bn1.livefilestore.com/
         y2m73vLY1yhhnhqJXy7_bt8Z6_o7u9ypLVhZMz-oD_hLbZs-qicCq_dTKFP
         jClvc7JH-BNUg-gAnyMMF-SonUfRPDB1LX2yaQ5lBpRg1V6xDi0/
         Tiny.png.jpg?psid=1",
      "source": "https://public.bn1.livefilestore.com/
         y2mT99M9gkyFFlj1Q9OxQFNhW0P0Hfji8CIiC60dVdrhPuhfZDQXKSG8QViI
          xeRmPClWCQIg4z9IR5R2ZlAc8FbZxwrOwsjMEVv_9ZN0cjG3NA/
         Tiny.png?psid=1",
      "upload_location": "https://apis.live.net/v5.0/
          file.4515677bdf99b35f.4515677BDF99B35F!1021/content/",
      "images": [
         {
            "height": 117,
            "width": 99,
            "source": "https://public.bn1.livefilestore.com/
               y2mbp4bsz2-2tbDrVIUC6LYbM1gCneqOvtLoLayTvIU1Aw1iTHH4idHb4w2
               HOGISHymyob8fz_BP4UAlxlzHH-9BA/Tiny.png.jpg?psid=1&ck=2&ex=720",
            "type": "normal"
         }, {
            "height": 117,
            "width": 99,
            "source": "https://public.bn1.livefilestore.com/
               y2m_lCuh5snt-qp4ttijSDi7lrr1TrK4zsQEFJi2bMbTm6URGSIiDZeBmSxrMz-
               6PTNFDTKllNEjKZ5YtknyYj3WQ/Tiny.png.jpg?psid=1&ck=2&ex=720",

            "type": "album"
         }, {
            "height": 96,
            "width": 81,
            "source": "https://public.bn1.livefilestore.com/
               y2m73vLY1yhhnhqJXy7_bt8Z6_o7u9ypLVhZMz-oD_hLbZs-qicCq_dTKFPjCl
               vc7JH-BNUg-gAnyMMF-SonUfRPDB1LX2yaQ5lBpRg1V6xDi0/Tiny.png.jpg?psid=1",
            "type": "thumbnail"
         }, {
            "height": 117,
            "width": 99,
            "source": "https://public.bn1.livefilestore.com/
               y2mT99M9gkyFFlj1Q9OxQFNhW0P0Hfji8CIiC60dVdrhPuhfZDQXKSG8QViI
               xeRmPClWCQIg4z9IR5R2ZlAc8FbZxwrOwsjMEVv_9ZN0cjG3NA/Tiny.png?psid=1",
            "type": "full"
         }
      ],
      "link": "https://onedrive.live.com/
         redir.aspx?cid=4515677bdf99b35f&page=browse&
         resid=4515677BDF99B35F!1021&parId=4515677BDF99B35F!161",
      "when_taken": null,
      "height": 117,
      "width": 99,
      "type": "photo",
      "location": null,
      "camera_make": null,
      "camera_model": null,
      "focal_ratio": 0,
      "focal_length": 0,
      "exposure_numerator": 0,
      "exposure_denominator": 0,
      "shared_with": {
         "access": "Just me"
         },
         "created_time": "2014-08-28T11:26:38+0000",
         "updated_time": "2014-08-28T11:46:03+0000",
         "client_updated_time": "2014-08-28T11:46:03+0000"
      }
   ]
}

I've trimmed the preceding code down to show just one file and one folder. As you can see, files that have special significance, such as pictures, have much more extra meta data than regular files, allowing you to do all manner of things with them.

I'll close this article off with a method that will make the request and return the JSON data containing a file list, as follows:

private string GetOneDriveRootListing()
{
   var accessToken = GetAccessToken();
   string jsonData;
   string url = string.Format(@"https://apis.live.net/
      v5.0/me/skydrive?access_token={0}", accessToken);
   using (var client = new WebClient())
   {
      var result = client.OpenRead(new Uri(url));
      var sr = new StreamReader(result);
      jsonData = sr.ReadToEnd();
   }

   OneDriveInfo driveInfo = JsonConvert.DeserializeObject
      <OneDriveInfo>(jsonData);

   url = string.Format("{0}?access_token{1}",
      driveInfo.Upload_Location, accessToken);
   using (var client = new WebClient())
   {
      var result = client.OpenRead(new Uri(url));
      var sr = new StreamReader(result);
      jsonData = sr.ReadToEnd();
   }
   return jsonData;
}

I've cheated a little bit here to make extracting the upload_location easier. I've used NuGet to add the excellent 'Newtonsoft.JSON' parsing library, rather than try and search out the line and parse it myself. To make that work as intended, you'll need to add a new class to your project, called 'OneDriveInfo.cs', and add the following code to it:

using System;

namespace Onedrive
{
   public class OneDriveInfo
   {
      public string ID { get; set; }
      public FromUser From { get; set; }
      public string Name { get; set; }
      public string Description { get; set; }
      public object Parent_ID { get; set; }
      public long Size { get; set; }
      public string Upload_Location { get; set; }
      public int Comments_Count { get; set; }
      public bool Comments_Enabled { get; set; }
      public bool Is_Embeddable { get; set; }
      public int Count { get; set; }
      public string Link { get; set; }
      public string Type { get; set; }
      public SharePermissions Shared_With { get; set; }
      public object Created_Time { get; set; }
      public DateTime Updated_Time { get; set; }
      public DateTime Client_Updated_Time { get; set; }
   }

   public class FromUser
   {
      public object Name { get; set; }
      public object ID { get; set; }
   }

   public class SharePermissions
   {
      public string Access { get; set; }
   }

}

All we need from this at the moment is the upload location to get the file listing. But, for now, all we return is the JSON sent by the file listing. You can display this (rather massive) string easily in your app by doing something like the following:

MessageBox.Show(GetOneDriveRootListing());

This one call will authenticate if it needs to, and then call and parse the drive info object, exctract the upload location, and return the JSON data representing the entire root contents of the logged in users OneDrive.

In the next article, we'll expand on this even further by taking the JSON we got back from our file listing, and turning that into a C# object. We'll do this in such a way that each object can represent the different types of data that can be returned. Then, once we can get a file listing, we'll explore how to upload and download files to and from the logged-in drive.

If you have any suggestions or ideas for articles you'd like to see, please feel free to reach out to me on twitter as @shawty_ds, or come and hunt me down in the Lidnug Linked.NET users group on Linked-In that I help run, I'll be more than happy to produce articless on specific topics of interest.



Related Articles

Comments

  • OneDrive for Business for Authentication

    Posted by Dipendra Shekhawat on 03/21/2016 10:46pm

    Hey Peter, Can you help me out with connecting to OneDrive for Business through my web app(ASP.NET/C#) ? I read lot of blog and now very confused and nothing is working for me. I just need to connect to OneDrive for business and list down all the files & folders on my web page. Your help would be much appreciated. Thanks in advance!

    Reply
  • Onedrive authentication through ASP.NET MVC

    Posted by Saby on 03/11/2015 05:51am

    Hi there, It's been a great learning and I could access my Onedrive files through an asp.net form application. Is there by any means a similar way to get the access token through an asp.net MVC application? I couldn't quite manage to grasp one...may be I need more honing as I'm pretty new to drive authentication process...it would be immensely helpful if you give your expert insight on the same...Thank you very much

    Reply
  • Error in article

    Posted by Tim Parr on 11/15/2014 01:53pm

    Hi there - great article. Just one point I noticed - in line 5 of your second-to-last code block (fetching the JSON data for the file list) you missed an = from the end of the url string. Also, is there a way of extending the hour that the token is valid for if the app is still connected? I am trying to create an application which will synchronize a folder between two separate oneDrive accounts. Thanks Tim

    • RE: Error in Article

      Posted by Peter Shaw on 12/03/2014 04:23am

      Hi Tim, thanks for pointing that out, I'll get it corrected asap. As for extending the hour, yes there is but it's not easy to do. You can ad an offline access claim (Sorry cant remember the actual definition) when you request authorization. What this then means is that, instead of caching the token and checking it each time, you actually have to set up a timer that repeatedly verifies the authorization in the background. For every hour that goes by, a new token is generated, and you verify that with the old token. Basically, you send the old token and if the old one comes back your ok, if a new one comes back you then have to re-validate the connection with the new token and save that. You need to do this on a regular schedule too, if you only check it on drive access, then you might miss the small window for re-generating the token and be forced to log in again from scratch, so to do this in needs to be done on some kind of timer in the background. All the details are on the OneDrive developers site along with sample code.

      • What about sporadically used applications

        Posted by T on 02/16/2015 10:43pm

        If I want to extend longer than an hour and my app is used briefly, say once every few days, do I need to re authenticate? Other than cheating by logging the user's id and password (security hazard), is there anyway, in winforms (non WinRT) I can avoid having to reauthenticate if my app has not been used for more than 1 hour? I wish to be able to use the current logged in windows user (microsoft account) to authenticate

        Reply
      • developer

        Posted by Leon on 01/05/2015 03:05am

        Did anyone implement this already? Can you provide an example of how to: - you send the old token and if the old one comes back your ok, if a new one comes back you then have to re-validate the connection with the new token and save that. You need to do this on a regular schedule too,

        Reply
      Reply
    Reply
  • Next Article

    Posted by Peter Shaw on 11/06/2014 05:01am

    Due to a bit of a mix up with publishing schedules, this post was originally intended to be released before the previous sky drive one. If you wish to read them in order, this post should be read before, this post : http://www.codeguru.com/columns/dotnet/accessing-files-in-a-onedrive-account-from-code.html

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date