Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

WM5 V's UMPC

Over at Dr Neil's Notes Neil and Hugo started what I think should be a multi-part discussion on the differences and similarities between WM5 and UMPC devices both from a consumer and a developer perspective.  My only frustration with this podcast was that it didn't stay on topic, which makes me think that a second attempt at this topic is in order.  What they did cover was whether there is a future for the Windows Mobile OS - this is an interesting point as we are seeing devices not only decrease in size, weight etc, but also increase in hardware functionality (ie longer battery life, more storage...).  As Neil points out Windows Mobile (essentially Windows CE) has a different set of priorities that Windows Vista.  While support for occasionally connected devices has really improved in Windows Vista, the operating system is still much heavier that Windows CE which effectively cuts the battery life, in some cases by as much as half.

The show also covers the new Zune device, and future versions of the iPod, where WiFi will be built into the device.  Neil makes the point that, much to our dismay, the built in WiFi will be locked down so that you can only connect to other Zune devices.  Currently I have to open iTunes (or equivalent), let it download the latest shows for the podcasts I have subscribed to and then I have to sync it with my iPod.  Although I have become used to doing this (I tend to kick this off before going to bed each night) I would much prefer for the device to be able to automatically download the podcasts when it is connected to a WiFi network.  Does anyone know of a petition that we can all sign to convince Microsoft of the errors of their ways?

Inking with the Wacom Graphire 4 Tablet

Earlier this week I was one of a few New Zealanders to be given a Wacom Graphire 4 Tablet to review.  As I made the trade off last year to substitute a Tablet PC for a high end laptop, the Wacom Tablet provides a pluggable (USB) solution for Inking.  Last week I upgraded my laptop to run Vista RC2 and I am itching to experiment with the new Inking capabilities.  Anyhow, if you are interested in what the Wacom Tablet looks like, here are the photos (thanks to my i-mate K-Jam) from the "unwrapping":

  

Some pictures of the Sept Orcas CTP

Following my previous post on the device improvements in the September Orcas CTP I was asked to post some images.  Anyway here they go:

Device Security Manager

Smart Device Testing

New Smart Device Project

One of the new features I didn't include in my previous post was that there is going to be a new wizard for creating a smart device projects.  In the new project dialog if you select the root language node (ie Visual Basic or C#) you will notice that there is a Device Application (which is the old wizard) and a new Smart Device Project.  The new wizard looks like.... and has been designed to be easy to select the project type you want to create.

SQL Server Everywhere - how big is that data?

As i mentioned in my previous post regarding SQL/e we have encountered a number of issues during our development phase.  One of the issues we encountered early on was that, for a number of different reasons, a reinitialisation maybe required for a merge subscription.  When a SQL/e database is set up as a subscriber to a merge subscription the initial dataset (this is usually a subset of the full database, partitioned according to device/user/other information) is replicated - we refer to this as a full sync.  From then on each sync only uploads/downloads changes that have been made to ensure the database is up to date - we refer to this as a partial sync.  Occasionally a sync was taking much longer than expected.  It turns out that it was essentially reinitialising the subscription, forcing a full sync.

Reinitialisation can result from a number of reasons, such as:

  • SQL/e subscription is reinitialised (directly) by the client
  • Subscriptions are reinitialised (directly) on the server
  • Changes are made to the database schema that require all subscriptions to be reinitialised
  • A subscription has been inactive for too long - effectively a timeout

After spending quite a bit of time reading documentation there appears to be no way to predict (from the client) whether a sync will be a full or partial, prior to it starting.  Take the example that a user is travelling and is connecting using their cellula network (expensive connectivity in most locations).  Before commiting to a large download it would be great for the user to be notified and presented with an option to cancel the sync.  This is also relevant from a timing perspective.  The user may just want to access some data, instead they have to wait 10 minutes for a full sync to complete.

Giving up on the documentation we went hunting for what information resides on the server regarding each subscription.  It turns out that for every subscription the server tracks information about when the subscription was last sync'd, the schema version they have, and much more.  By querying this information it is possible to determine whether the next sync to be carried out will be a full or partial sync.  For example we could run a query like (-1 indicates a reinitialisation is required):

select schemaversion
from dbo.sysmergesubscriptions
where pubid = '4875EFFF-BA73-4660-B7A0-781CAC97384E' --Publication GUID
and subid = '9990AD5A-6A39-4234-A0B4-26BDB29B4C21' --Subscription GUID

Now that we can query the database on the server side we need a way for the client to be able to access this information.  This is done via a webservice which runs with enough privileges to be able to query this information on the server.  The only remaining trick is to work out what the publication GUID and subscription GUIDs are.  On the SQL/e database, once the subscription has been setup, there is a __sysMergeSubscription table that can be queried for this information.

In summary the process is quite simple.  Prior to syncing we query the local database for the publication/subscription ID (if these are not there then it have to be a full sync).  This information is sent to the webservice that queries the subscription information on the server.  If a full sync is required we prompt the user to confirm that they are willing to proceed with the sync.

Hope this information helps others with this issue as we had a tough time trying to locate a good information source on how all these bits and pieces work (or more specifically all the things which can - and inevitably will - go wrong with merge replication).  While I would definitely still recommend using replication to push data out to an occasionally connected client, it is not something you can just "assume it will work".

SQL Code Camp - NZ style

The details of the SQL Code Camp has recently been posted on the NZ .NET user group website.  Despite only being here for a short 6 month contract, I seem to be classified as being from New Zealand.  Anyhow, I'm going to be discussing SQL Server Everywhere, so if you are in NZ on the weekend of the 25/26th November then you should definitely attend the code camp at Porirua!

Vista UAC

Warner across at GottaBeMobile makes an interesting point that User Access Control in Vista can be, at times, quite frustrating to say the least.  That is for all us power users.  Unfortunately given the current state of most applications - where due to poor application design they have had to use some OS hack - UAC is going to be annoying for everyone.  Developers need to build applications that can work in the normal user context, only requiring elevated privileges (perhaps) during installation.  IMHO Vista's UAC is no longer Microsoft's problem, it is up to every developer out there to fix their application to require less privileges, or at least define what privileges it does need (most applications have no idea).

SQL Server Everywhere Security

As we approach the final stages of our third sprint here at Intilecta I took the opportunity this evening to look back at some of the issues we have faced using SQL Server Everywhere for replicating and caching offline data.  Following any of the numerous online samples it is easy to get basic synchronisation working.  However, when the architectural issues associated with an enterprise application kick in there are a number of issues that SQL Server Everywhere doesn't really cater for.

One these issues is how security is managed.  From the local database point of view there are some things that you can do to protect the local database:

  • Have a strong password on the database (as we are creating the local database as an offline store we randomly create this password - the user should never have to key in a password)
  • Enable encryption on the database when it is created (again this is enabled as part of the connection string you specify when creating the local database)
  • Put the database in a user specific directory (ie under "documents and settings" (or "Users" in the case of Vista) - this will ensure you have filesystem protection from other users accessing the raw data file, unless of course they are a local admin)

Ok, but I'm writing my application in .NET which means that any password I use can be easily decoded from the assembly.  This is where you can use the managed wrapper for the Windows DPAPI (see the ProtectedData class in the .NET Framework v2) to encrypt/decrypt the password, placing the resulting value in the registry.  Again the registry key you select should be specific to the current user.

Now that we have secured the local database, how do we connect to the server to synchronise the data down to the local database.  In simple terms there are three main parties involved in the synchronisation process (well there are actually more, but these are the main security concerns).  At the back end you have the database itself which supports SQL Server or Windows Authentication.  You then have IIS, as SQL Server Everywhere only supports synchronising using a Merge Publication accessed via a virtual directory, which supports Anonymous, Windows and Basic Authentication.  Lastly you have the local database, which we have just covered.

Where this gets difficult is when you consider that it is essentially IIS that is pulling data from the database.  Which ever user IIS is running as (or impersonating) will be the user that accesses the database. In order to configure a subscription you start by defining what type of security you are going to use to authenticate against IIS.  You do this by either providing a username and password, or not (in which case anonymous access is used).  This will determine which authenticate mechanism is used against IIS, with the following interesting cases:

  • If you have anonymous access enabled on the virtual directory, this will always be the preferred authentication method
  • If you have Windows Authentication enabled the user that you are running the application as will be used to authenticate against IIS - is this what you want?

Once you have authenticated with IIS it is time to determine what user will be used to access the database.  Again, when you set up the subscription you can either specify a username/pasword combination, or not (in which case pass through authentication is used).  If you don't provide a username the user that IIS is running as will be used to authenticate against sql server.  This user will either be the impersonated account (if using anonymous access), the application account (if Windows Authentication), or a specified account (in the case of basic authentication).

You will note here that we have been including Windows Authentication in this discussion, despite the fact that the documentation clearly states that it is not supported.  The reason for this is that it half works.  In fact it fully works, with the exception that IIS and SQL Server have to reside on the same machine.  As soon as you put them on different boxes you can only use basic authentication with IIS.  The main reason for this (I guessing) is that SQL Everywhere is based on all the work done to build SQL CE and since Windows Mobile never supported Windows Authentication, we can assume it was never in the original product spec.

The upshot of all this discussion is that only Basic Authentication is supported (well anonymous is, but not an ideal solution for securing your data), which means that you need to store a username/password with your application. Alternatively the user could provide a username/password - this option is clearly not great as they have to enter it everytime you want to sync.

Workaround - The workaround that we came up with makes a compromise in both directions with the net effect of improving usability of the product.  Only as a last resort could we afford to prompt the user even once to enter a username/password combination.  The application user has already authenticated with the machine, so we should be able to use this information to authenticate against IIS.  Well we can, in so far as we can authenticate against a webservice.  This webservice will provide us with the necessary username/password combination that we will use to authenticate against IIS. 

The compromise here is that it is not great that a username/password combination is being passed across the wire.  However, the data is being protected since the user has to authenticate using Windows Authentication and the channel itself is encrypted using SSL.  Lastly, the username/password is NEVER cached locally so it is actually harder for anyone to steal the username/password.  Oh, the other plus is that if we ever want to change the username/password, we can.

The end result is that we have a system that authenticates using basic authentication, but without either the poor usability that prompting for a username/password results in or the negative security issues with hardcoding a username/password into the application.

I hope you find this useful and if you have any comments on how you have addressed similar issues I would love to hear from you.

Zero (known & unacceptable) defects

One of the most arguments that seems to dominate the world of agile development is around the concept of zero defect software.  While I agree with the points that Dr Neil raises about zero defect software being in part a mindset - if you think you write bugs, you will write bugs - I disagree that software can remain in a zero defect state.  In fact, as the title suggests I think that there needs to be two adjustments to the term.  Firstly, the concept of zero defect is almost impossible, unless you use formal methods, or equivalent, to prove your software is "correct" (which of course overlooks the discussion around what "correct" is).  Hence the "known" augmentation - you can preserve the zero defect state, so long as you know what the defects are.

The second addition is the word "unacceptable".  Take the example where you are building a product that integrates services from a number of vendors, each of which provides their logo to be displayed in the application alongside their functionality.  During the development phase one of the vendors changes their marketing campaign, including their logo.  Of course, you now have a defect in your software that the logo is wrong.  This isn't a feature or enhancement, the logo is wrong (although I'm sure that some will argue this point).  Given that you have a number of outstanding features that need to be completed before you can ship the current version of the software do you spend the time to fix this issue?  Or do you mark this defect as "acceptable" and move it to the next release cycle.

Of course the danger about moving things to the next release cycle is that the defects never get fixed.  If you are going to mark defects as "acceptable" you need a process where these defects are continuously re-evaluated each development cycle, or even during the cycle.

Orcas - another round of VS improvements

I've finally got around to running the Microsoft Pre-release Software Visual Studio Code Name "Orcas" - September Community Technology Preview (CTP). Big thanks to Mauricio who downloaded the image (my Woosh account is tooo slow to download anything over 10Mb) for me.

Device Security Manager
One of the first features I was eager to look at was the integration of the Device Security Manager (Windows Mobile Powertoy) into the IDE. the DSM is available from the Tools menu and renders within the VS IDE. After connecting to a device/emulator you can export/import security configurations from the device. These are xml scripts like the following, that can be applied to the device/emulator.

<wap-provisioningdoc>
   <characteristic type="SecurityPolicy">
      <parm name="4123" value="0" />
      <parm name="4122" value="1" />
      <parm name="4101" value="16" />
      <parm name="4102" value="0" />
      <parm name="4097" value="2" />
   </characteristic>
</wap-provisioningdoc>

Note, as you play with this feature you might notice that the toolbar for the DSM occasionally displays options to Add/Remove certifications - perhaps this is a hint as to what is to come.

Smart Device Testing
When VS2005 was released there was great disappointment from the mobile developer community as we were severly neglected when it came to SDLC support in Team System. Although we could check out projects into TFS we couldn't use any of the automated testing. In this CTP we have the first sneak preview of the testing support that is going to be built in. To create tests for a smart device project you simply right click on the method and select "Create Unit Tests...". Be sure to select the "Create a new smart device test project" - if you don't the tests will not be executed on the device. Also, be warned that running a smart device test will NOT deploy the .NET framework runtime to the device. To do this you can either manually install it from the redistributable, Or you can just run a .NET device application from the VS IDE - this WILL deploy the runtime to the device.

The Digital Native Platform

Mitch has an interesting concept with his proposed Digital Native Platform.  This is an interesting, albeit not new, concept.  I seemed to remember sitting through a tour at Microsoft at the last MVP summit where we saw similar technology and the dream of a paperless office where collaboration was the key to a successful business.  Regardless I still think it is worth throwing away the printer and seeing just how far you can go.

Splash screens using VB2005 Application Framework

James Avery makes a point that Eric Gunnerson has a way of doing splash screens.  Unfortunately he has overlooked to easiest way to do splash screens which is to use VB.NET and set the splash screen form using the Project properties window.  It's as simple as 1) Enable application framework 2) Set splash screen to the form you want to load.

Oh, as an aside, if you want to control the minimum time that the splash screen loads for all you need to do is trap the application load event (select "View Application Events" from the Application tab on the project properties page, then select the appropriate event (ie Startup) from the combobox) and set the My.Application.MinimumSplashScreenDisplayTime property to the number of milliseconds you want the screen to show for.

VB.NET needs Iterators

Just reading through the RSS sample I mentioned in my previous post on the Windows RSS Platform and noticed that they are using one of the new C# features in .NET v2, Iterators.  Anyhow the code snippet I'm referring to is:

public static IEnumerable<IFeed> CommonFeedList(IFeedFolder folder)
  
{
      Queue<IFeedFolder> queue = new Queue<IFeedFolder>();
      queue.Enqueue(folder);
      while (queue.Count > 0)
      {
         IFeedFolder currentFolder = queue.Dequeue();
         foreach (IFeedFolder subfolder in (IFeedsEnum)currentFolder.Subfolders)
            queue.Enqueue(subfolder);

         foreach (IFeed feed in (IFeedsEnum)currentFolder.Feeds)
         {
            System.Windows.Forms.Application.DoEvents();
            yield return feed;
         }
      }
   }

While not the most elegant piece of code (especially the explicit call to "DoEvents") it got me thinking about how I would do this in VB.  The long answer is that you need to code the full iterator yourself.  Please, please, please VB team can we have iterators?

Joining the IE7 bandwagon

Over the last couple of days over half the blogs that I subscribe to have reposted the fact that IE7 has RTM'd.  Of which probably the best blog to read is probably Sandi's as she is definitely one of the leading authorities on things IE. Not being a big fan of reposting I thought that I would provide a different angle on the release of IE7.  As most people will be aware of is that IE has RSS feed reading capabilities built in.  A feature that some people won't be aware of is that this uses the Common Feeds List which a number of Microsoft products going forward will be using.  These products include Outlook 2007 and Windows Live Mail Desktop.  The built in functionality is more than just a local store for RSS feeds, it also have built in scheduling to synchronise the feeds and the ability to track which feeds/items have been read. Although the interface in IE7 is very rudimentary the support through Outlook 2007 is very nice indeed.

Of course the next question is whether there is an API for accessing this feeds store.  Well the great news is that Microsoft have come to the party with a quite extensive API, including documentation and a C# sample to get you started.

OneCare v's WMDC

Continuing on from previous post regarding the issues with OneCare, Trend and VPNs I have run into another problem with OneCare.  This time it is preventing the beta of the Windows Mobile Device Center from connecting to my mobile device (or perhaps that should be my mobile device from connecting to WMDC).  Note that this behaviour is consistent with the default Windows Firewall that ships with Vista.  This leads me to believe it is probably an issue with WMDC rather than explicitly OneCare; However, it is an issue that both teams need to address.

UMPC - retail or enterprise

Hugo and Dr Neil talk about whether UMPC devices should be a retail (ie appearing in Harvey Norman) or an enterprise device.  While Dr Neil argues that the target price point should be around the US$700 price point so that they are appealing to consumers I'm not convinced that these devices are going to sell like hotcakes even if the prices was $3-400.  Consumers (and I take a family as a typical representation here) will buy a desktop computer for use by the whole family rather than a mobile device that can easily be misplaced.  For those members of the family, such as the kids, that need a computer for school, a convertible tablet is probably going to be the device of choice.

On a different note, Hugo makes the point that he doesn't think consumers will adopt UMPC devices as the learning curve is too high.  My thoughts are that in fact most consumers will understand the interaction model, using touch or stylus input, much better than the traditional mouse and keyboard.  I think acceptance will be high for the operating system; However I feel that unless us developers start to build applications that work with a touch/stylus input, these devices will struggle to be accepted by either consumers or the enterprise.  In fact I would go so far as to say this is the major impediment, especially when even Microsoft can't get it right!

OneCare beta prevents VPN connections in Vista RC2

In my previous post I mentioned the beta of Windows Live OneCare (v1.5) that I am running on my Vista RC2 laptop.  What I didn't mention is that one of the annoying bugs that I have found is that it prevents VPN connections from working.  Perhaps I missed something but even after going in and explicitly allowing "Microsoft Virtual Private Networks" whereever I could find a reference, I still couldn't get a VPN connection to work.  I actually thought it was a bug in Vista until I tried disabling the firewall in OneCare and the VPN connected perfectly.

Updated: There are actually two workarounds for this.  The first is to disable the firewall in OneCare and enable the Windows Firewall that ships with Vista.  This is just as secure, although it does leave the OneCare icon red, which bugs me.  The other workaround is to add port 1723/outgoing to the exceptions list.  This is in addition to the "Microsoft Virtual Private Network" exception (in the default list) mentioned previously.

Pc-cillin v's OneCare

Last week when I reformatted my laptop for the 6 billionth time I installed Vista RC2 - it must be ready to ship soon.... After less than an hour I had a clean install running Media Center so that I could once again kick back and watch TV.  However, this wasn't the end of the road.  When Vista boots up the Security Center flags that there is no virus scanner installed.  You can easily navigate to the Microsoft site where they have a number of virus scanners available for download (they are all in beta at the moment until Vista ships).  Having previously followed this road with Pc-cillin on a previous build I figured it would be a pretty safe bet, so I went with that.

A couple of days later and I get an email inviting me to participate in the beta for OneCare, Microsoft's entry into the Virus scanning/firewalling/backup etc.  From here things started to go bad - I went to uninstall Trend and completely forgot that it was a beta.  Instead of taking the safe option and uninstalling using "Run as Administrator," I instead opted to just uninstall the product.  Unfortunately it crashed mid way through leaving the system with chunks of Trend stuck all over the walls.  Not only could I no longer remove the product (despite it appearing in the installed Programs list), I couldn't even reinstall it (since it thinks it is already installed).  Of course there is no "repair" option on this installer - there is a reason why all good installers have a repair option!

Anyhow I thought that perhaps installing OneCare would clear out Trend.  Nope, it just made matters worse as it identifies Trend as a potential security threat.  To prevent further damage I uninstalled OneCare and tried a number of things to remove Trend.  In the end I had to run regedit and remove all keys associated with Trend (a painful 1 hour + process).  I then rebooted and still the Security Center things Trend is installed.  Luckily I was able to reinstall Trend so that the system was at least stable.  Then, using "Run as Administrator" I proceeded to uninstall Trend.

Finally, after all this I was able to successfully install the OneCare beta - which went on without a hitch and is running quite smoothly in the background.

Walking the walk of shame - when blogs get it wrong

Ok, so I really should have done a bit more research prior to blogging my frustration around the apparent lack of support for VB in the first release of WPF/e.  It turns out that I was wrong (yes I'm big enough to walk the walk of shame on this one) and that I jumped the gun.  According to sources within Microsoft there will be Managed Code Support in WPF/e which will include both C# and VB.NET.  Way to go team - I'm so hanging out for this one.

Screen Resolution - how small should we go

Ok, so a long time ago I had a discussion with the other co-founders of PISCES communications around what screen resolutions and colour depth we should be supporting. At that stage we were arguing over whether 640x480 or 800x600 was the most common resolution.  Of course things have changed with monitors that are much bigger (physically) and support for much higher resolutions.  However, with Tablet PCs, and in particular UMPC devices, there has been a shift back to lower resolutions.  To this end a screen resolution of 800x600 is quite common for a number of smaller devices. 

The other really cool thing about Tablet PCs is that they open up a world of human interface options, such as writing and voice.  Unfortunately, as Craig points out here, the speech recognition team working on Vista has decided that voice is unimportant for users with a screen resolution less than 1024x768.  Rob Chambers indicates that asthetics was the ONLY reason they made this decision - how bad is that. 

Craig follows up by hitting the nail on the head - Developers don't assume any minimum resolution (where possible) as you will always be wrong and frustrate your users.  Make your forms resizable, or at least if they are fixed width/height make them small!!!

WPF/e defining some language priorities (aka web developers use c# not VB)

I was chatting with Darryl this afternoon at the weekly Geekzone catch up and he was filling me in on the progress of Windows Presentation Foundation Everywhere.  Well, it turns out that support for mobile device will be shipping in V2, which is not surprising as this was stated early on by the team.  However, what was a massive surprise, and IMHO a complete insult to the thousands of VB developers, is that only Javascript and C# will be supported for writing client side code to run on the WPF/e runtime. That's right apparently support for VB.NET will not make it into V1 - backlogged to V2.

THIS IS JUST WRONG - Microsoft should reconsider this, delaying shipment of WPF/e if necessary to guarrantee support for VB.NET

Updated: VB will be supported in the first release (see comments)

Windows Live Id Authentication using Messenger Connect for Windows Phone

Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Windows Live Id Authentication using Messenger Connect for Windows Phone

If you saw my post on BuildMobile.com you would have seen that I used the “token” response_type when initially specifying the login url. This works well for getting a short lived access token but the wl.offline_access scope is effectively ignored – the expiry time comes back as 3600. This is completely useless for a Windows Phone application where you don’t want to have to reprompt the user every time they run the application. Luckily, despite the failings in the current implementation, there is a work around which involves a few more steps and uses the “code” response_type.

I’m not going to step through the entire process, instead, here is the code that you should be able to use – it’s based on my post Using Windows Live ID in a WP7 Appon BuildMobile.com so should be easy enough to follow.

Notes:
- You need to replace both <your_client_id> and <your_client_secret> with the corresponding values for your application
- In a production application you should not have the client id or client secret in plain text in your application - ideally this should be held on a server and only exposed to the client app over ssl and never persisted in the client app. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Windows;
using System.Windows.Navigation;

namespace WLTest
{
    public partial class MainPage
    {
        // Constructor
        public MainPage()
        {
            InitializeComponent();
        }

        private void AuthenticateClick(object sender, RoutedEventArgs e)
        {
            var uriParams = new Dictionary<string, string>()
                                {
                                    {"client_id", "<your_client_id>"},
                                    {"response_type", "code"},
                                    {"scope", "wl.signin,wl.basic,wl.offline_access"},
                                    {"redirect_uri", "https://oauth.live.com/desktop"},
                                    {"display", "touch"}
                                };
            StringBuilder urlBuilder = new StringBuilder();
            foreach (var current in uriParams)
            {
                if (urlBuilder.Length > 0)
                {
                    urlBuilder.Append("&");
                }
                var encoded = HttpUtility.UrlEncode(current.Value);
                urlBuilder.AppendFormat("{0}={1}", current.Key, encoded);
            }
            var loginUrl = "https://oauth.live.com/authorize?" + urlBuilder.ToString();

            AuthenticationBrowser.Navigate(new Uri(loginUrl));
            AuthenticationBrowser.Visibility = Visibility.Visible;


        }

        public string AccessToken { get; set; }
        public string RefreshToken { get; set; }

        private void BrowserNavigated(object sender, NavigationEventArgs e)
        {
            if (e.Uri.AbsoluteUri.ToLower().Contains("https://oauth.live.com/desktop"))
            {
                var query = (from pair in e.Uri.Query.Trim('?').Split('&')
                             let bits = pair.Split('=')
                             where bits.Length == 2
                             select new KeyValuePair<string, string>(bits[0], bits[1])).ToArray();

                var code =
                    query.Where(kvp => kvp.Key == "code").Select(kvp => HttpUtility.UrlDecode(kvp.Value)).FirstOrDefault
                        ();
                if (string.IsNullOrEmpty(code))
                {
                    var error =
                        query.Where(kvp => kvp.Key == "error").Select(kvp => HttpUtility.UrlDecode(kvp.Value)).
                            FirstOrDefault();
                    var error_desc =
                        query.Where(kvp => kvp.Key == "error_description").Select(
                            kvp => HttpUtility.UrlDecode(kvp.Value)).FirstOrDefault();
                    MessageBox.Show("Error: " + error + "\n" + error_desc);
                    AuthenticationBrowser.Visibility = System.Windows.Visibility.Collapsed;
                    return;
                }

                var uriParams = new Dictionary<string, string>()
                                    {
                                        {"client_id", "<your_client_id>"},
                                        {"client_secret", "<your_client_secret>"},
                                        {"redirect_uri", "https://oauth.live.com/desktop"},
                                        {"code", HttpUtility.UrlEncode(code)},
                                        {"grant_type", "authorization_code"}
                                    };
                StringBuilder urlBuilder = new StringBuilder();
                foreach (var current in uriParams)
                {
                    if (urlBuilder.Length > 0)
                    {
                        urlBuilder.Append("&");
                    }
                    var encoded = HttpUtility.UrlEncode(current.Value);
                    urlBuilder.AppendFormat("{0}={1}", current.Key, encoded);
                }
                var tokenUri = "https://oauth.live.com/token?" + urlBuilder.ToString();

                var request = HttpWebRequest.CreateHttp(tokenUri);
                request.BeginGetResponse(result =>
                                             {
                                                 var req = result.AsyncState as HttpWebRequest;
                                                 using (var resp = req.EndGetResponse(result))

                                                 using (var strm = resp.GetResponseStream())
                                                 {
                                                     var serializer =
                                                         new DataContractJsonSerializer(
                                                             typeof (WindowsLiveAuthorizationCode));
                                                     var authorization =
                                                         serializer.ReadObject(strm) as WindowsLiveAuthorizationCode;
                                                     AccessToken = authorization.AccessToken;
                                                     RefreshToken = authorization.RefreshToken;

                                                     this.Dispatcher.BeginInvoke(() => MessageBox.Show(
                                                         "Access granted"));
                                                     RequestUserProfile();
                                                 }
                                             }, request);

                AuthenticationBrowser.Visibility = System.Windows.Visibility.Collapsed;
            }
          
        }

 


        private void RequestUserProfile()
        {
            var profileUrl = string.Format("https://apis.live.net/v5.0/me?access_token={0}",
                                           HttpUtility.UrlEncode(AccessToken));
            var request = HttpWebRequest.Create(new Uri(profileUrl));
            request.Method = "GET";
            request.BeginGetResponse(result =>
                                         {
                                             try
                                             {
                                                 var resp = (result.AsyncState as HttpWebRequest).EndGetResponse(result);
                                                 using (var strm = resp.GetResponseStream())
                                                 {
                                                     var serializer =
                                                         new DataContractJsonSerializer(typeof (WindowsLiveProfile));
                                                     var profile =
                                                         serializer.ReadObject(strm) as WindowsLiveProfile;
                                                     this.Dispatcher.BeginInvoke((Action<WindowsLiveProfile>) ((user) => {
                                                                            this.UserIdText.Text = user.Id;
                                                                            this.UserNameText.Text = user.Name;
                                                                                                                   }),
                                                                                 profile);
                                                 }
                                             }
                                             catch (Exception ex)
                                             {
                                                 this.Dispatcher.BeginInvoke(() =>
                                                                             MessageBox.Show("Unable to attain profile information"));
                                             }
                                         }, request);
        }

        [DataContract]
        public class WindowsLiveProfile
        {
            [DataMember(Name = "id")]
            public string Id { get; set; }

            [DataMember(Name = "name")]
            public string Name { get; set; }
        }

        [DataContract]
        public class WindowsLiveAuthorizationCode
        {
            [DataMember(Name = "access_token")]
            public string AccessToken { get; set; }

            [DataMember(Name = "refresh_token")]
            public string RefreshToken { get; set; }

            [DataMember(Name = "scope")]
            public string Scope { get; set; }

            [DataMember(Name = "token_type")]
            public string TokenType { get; set; }

            [DataMember(Name = "expires_in")]
            public string ExpiresIn { get; set; }
        }

        private void RefreshTokenClick(object sender, RoutedEventArgs e)
        {

            var uriParams = new Dictionary<string, string>()
                                {
                                    {"client_id", "<your_client_id>"},
                                    {"client_secret", "<your_client_secret>"},
                                    {"redirect_uri", "https://oauth.live.com/desktop"},
                                    {"refresh_token", RefreshToken},
                                    {"grant_type", "refresh_token"}
                                };
            StringBuilder urlBuilder = new StringBuilder();
            foreach (var current in uriParams)
            {
                if (urlBuilder.Length > 0)
                {
                    urlBuilder.Append("&");
                }
                var encoded = HttpUtility.UrlEncode(current.Value);
                urlBuilder.AppendFormat("{0}={1}", current.Key, encoded);
            }
            var tokenUri = "https://oauth.live.com/token?" + urlBuilder.ToString();

            var request = HttpWebRequest.CreateHttp(tokenUri);
            request.BeginGetResponse(result =>
                                         {
                                             var req = result.AsyncState as HttpWebRequest;
                                             using (var resp = req.EndGetResponse(result))

                                             using (var strm = resp.GetResponseStream())
                                             {
                                                 var serializer =
                                                     new DataContractJsonSerializer(
                                                         typeof (WindowsLiveAuthorizationCode));
                                                 var authorization =
                                                     serializer.ReadObject(strm) as WindowsLiveAuthorizationCode;
                                                 AccessToken = authorization.AccessToken;
                                                 RefreshToken = authorization.RefreshToken;

                                                 this.Dispatcher.BeginInvoke(() => MessageBox.Show(
                                                     "Token refreshed"));
                                             }
                                         }, request);
        }
    }
}

Pingbacks and trackbacks (1)+

Comments are closed