In general, rich client apps use OAuth flow to obtain authorization tokens to a web service. However, that flow may not be available from the authentication servers. In the following I am using an alternative approach: WS-Federation, well supported by ASP.NET web services. The solution consists of a web service, often used to provide REST services to rich client apps (Windows Store or .NET WPF, WinForms, etc.) and an authentication server (STS), e.g. Windows Azure Active Directory.

The basic approach is to:

  1. Establish WS-Federation trust between the STS
    and the web service (e.g. http://msdn.microsoft.com/en-us/library/windowsazure/dn151790.aspx).
  2. Host a web browser control in the rich client
    application, use it to allow passive WS-Federation redirection between the web
    service and the authentication service (STS).
  3. Detect the final redirection from the
    authentication server to the web service and verify that it includes
    authentication token
  4. Re-use authentication cookies returned by the
    web service in any subsequent REST calls to the web service.

For the Windows Store 8.1 app I used the WebView control for the WS-Federation passive redirection exchange. Simplified code-behind used to detect completion of the authentication flow is shown below (errors and exception are ignored; the xaml page contains a WebView control called _login and a TextBlock called _result). The GetValues method shows a call to the web service.

        string _webApp =
  "http://localhost:60582/"; 
  // web service

        protected override void
  OnNavigatedTo(NavigationEventArgs e)

        {

            _login.Source = new
  Uri(_webApp);  // navigate to web
  service, which should start ws-fed flow

        }

        bool _isAuthenticated =
  false;

        private async void
  HandleNavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs
  args)

        {

            if
  (args.Uri.ToString() == _webApp) // navigating back to web service

            {

                if
  (args.IsSuccess) // assuming authentication completed

                {

                   
  _login.Visibility = Visibility.Collapsed;

                   
  _isAuthenticated = true;

                    await
  GetValues();  // call some service
  requiring prior authentication

                }

            }

        }

        async Task GetValues()

        {

            var client = new Microsoft.Web.Http.HttpClient();  // NOT System.Net.Http.HttpClient

            var values = await
  client.GetStringAsync(new Uri(_webApp + "api/values"));

            _result.Text =
  values;

        }

Unlike any other .NET http client Microsoft.Web.Http.HttpClient shares its cookie store with other WinINet based code in your app, in this case with the browser control. Hence the GetValues method REST call will include the FedAuth cookies returned earlier during the authentication exchange through the WebView control.

In a .NET client (WPF or WinForms) http clients (HttpClient, WebClient, HttpWebRequest) do not share cookies with each other or other clients (browser controls) and therefore additional code needs to be provided to copy the cookies returned as part of the passive browser authentication to the http client making subsequent REST calls. Following code shows such an implementation in a WPF client:

public partial class MainWindow : Window

{

   
  [DllImport("WinInet.dll")]

    static extern bool
  InternetGetCookieEx(string pchURL, string pchCookieName, StringBuilder
  pchCookieData, ref System.UInt32 pcchCookieData, int dwFlags, IntPtr
  lpReserved);

    static int
  INTERNET_COOKIE_HTTPONLY = 0x00002000;

   
  [DllImport("Kernel32.dll")]

    static extern int
  GetLastError();

 

    public MainWindow()

    {

        InitializeComponent();

    }

    private void
  HandleNavigated(object sender, NavigationEventArgs args)

    {

        if (args.Uri.ToString()
  == _webApp)

        {

           
  _progressBar.Visibility = System.Windows.Visibility.Collapsed;

           
  _progressBar.IsIndeterminate = false;

           
  _authCookies.Add(GetCookie(_webApp, "FedAuth"));

           
  _authCookies.Add(GetCookie(_webApp, "FedAuth1"));

            _login.Source = new
  Uri(_webApp + "api/values");

        }

    }

    Cookie GetCookie(string
  url, string cookieName)

    {

        UInt32 length = 2024;

        StringBuilder cookie =
  new StringBuilder((int)length);

        if
  (!InternetGetCookieEx(_webApp, cookieName, cookie, ref length,
  INTERNET_COOKIE_HTTPONLY, IntPtr.Zero))

        {

            var error =
  GetLastError();

            throw new
  Exception("COM error: " + error.ToString());

        }

        var cookieValue =
  cookie.Remove(0, cookieName.Length + 1); // remove leading 'FedAuth='

        return new
  System.Net.Cookie(cookieName, cookieValue.ToString()) { HttpOnly = true };

    }

    string _webApp =
  "http://localhost/MultiTenantRichClient/";

    CookieCollection
  _authCookies = new CookieCollection();

    async Task GetValues()

    {

        var handler = new
  WebRequestHandler();

       
  handler.CookieContainer.Add(new Uri(_webApp), _authCookies);

        handler.UseCookies =
  true;

        var client = new
  HttpClient(handler);

        string values =
  String.Empty;

        try

        {

            values = await
  client.GetStringAsync(new Uri(_webApp + "api/values"));

        }

        catch (Exception ex)

        {

           
  MessageBox.Show(ex.Message);

        }

        _result.Text = values;

    }

    private void
  HandleLogon(object sender, RoutedEventArgs e)

    {

        _progressBar.Visibility
  = System.Windows.Visibility.Visible;

        _login.Source = new
  Uri(_webApp);

    }

 

    private async void
  HandleCallAPI(object sender, RoutedEventArgs e)

    {

        _login.Visibility =
  Visibility.Collapsed;

        await GetValues();

    }

}

 

The xaml page for the WPF app looks as follows:

<Window x:Class="WpfTestClient.MainWindow"

       
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

       
  Title="MainWindow" Height="500"
  Width="600">

    <Grid>

        <StackPanel
  Orientation="Horizontal" VerticalAlignment="Top"
  HorizontalAlignment="Left">

            <Button
  Content="Logon" Click="HandleLogon" Width="100"
  Height="40"/>

            <Button
  Content="Call API" Click="HandleCallAPI"
  Width="100" Height="40"/>

        </StackPanel>

        <Border
  Background="Gray" Margin="120">

            <TextBlock
  x:Name="_result" TextWrapping="Wrap" />

        </Border>

        <WebBrowser
  x:Name="_login" Margin="40"
  Navigated="HandleNavigated" />

        <ProgressBar
  x:Name="_progressBar" IsIndeterminate="True"
  Opacity="0.4" Visibility="Collapsed"/>

    </Grid>

</Window>

 The approach, as outlined allows for the initial authentication flow. It should be expanded to include token renewal and logout support.