C++

Using the C++ REST SDK in Windows Store Apps

Sridhar Poduri

Download the Code Sample

In my previous article ( msdn.microsoft.com/magazine/dn342869) I introduced the C++ REST SDK and how it can be used in Win32/MFC applications. In this article, I’ll discuss how the C++ REST SDK can be integrated within Windows Store apps. One of my original goals in using the C++ REST SDK and the OAuth authentication class was to use as much standard C++ as possible and only interface with platform-specific APIs where necessary. Here’s a quick recap of the previous article:

  1. Code used in the OAuth authentication class uses standard C++ types and no Windows-specific types.
  2. Code used for making Web requests to the Dropbox REST service uses the types from the C++ REST SDK.
  3. The only platform-specific code is the function to launch Internet Explorer and complete the application authentication and approval on the Dropbox Apps console portal.

I retained the same goals for my Windows Store app to support authentication and upload a file to Dropbox. I strived to provide the maximum amount of portable C++ code as possible and to interface with the Windows Runtime (WinRT) only where necessary. The example code downloads for both articles can be found at archive.msdn.microsoft.com/mag201308CPP.

Problems with the Win32 Solution

One of the big drawbacks from the previous Win32 application was the need to launch an external application to complete the OAuth authorization process. This meant I had to launch Internet Explorer (you could launch your favorite browser as well), log in to Dropbox using my credentials and then complete the workflow. This is shown in Figures 1 and 2.

Logging in to Dropbox Using My Credentials Before Authorizing Application Access
Figure 1 Logging in to Dropbox Using My Credentials Before Authorizing Application Access

Successful Authorization for My Application on the Dropbox Portal
Figure 2 Successful Authorization for My Application on the Dropbox Portal

As you can see, launching an external application and asking users to complete the workflow through the external application takes focus away from my app. As a developer, I also have no standard mechanism through which my app can be notified when the workflow is complete. With my focus on asynchronous programming and with the C++ REST SDK designed to support asynchronous task-based programming, having to launch an external program is clearly a bummer for me. I explored using named pipes, memory-mapped files and so on, but all these approaches require another application to be written to host a Web control instance and then write the successful value back either via a named pipe, shared memory or a memory-mapped file. I finally settled on using the browser to do the task, as I didn’t want to write another application that wrapped the browser control.

Integrating with the Windows Runtime

As I began designing my app to support the Windows Runtime, I considered a few options. I’ll briefly list them here and then discuss the chosen approach in more detail:

  1. Use protocol activation and let the system launch the right process to handle the protocol by calling the Windows::System::Launcher::LaunchUriAsync function. This means that for an HTTPS-based URI, the OS will launch the default browser. This is similar to launching Internet Explorer from the Win32 sample, but with a double whammy: My Windows Store app will be pushed to the background, the default browser will launch full-screen, and—in the worst-case scenario—my app would get suspended by the time the workflow is completed by the user. A big no-no.
  2. Integrate the WebView control in my app. Using the XAML WebView control allows me to embed the entire workflow navigation within the context of my app. Theoretically, I can also be notified if the process has been completed by listening to the window.external.notify event fired by the WebView control. The reality, though, is that the event is fired only if the Web page fires the notification event. However, in my case, the Dropbox page where the authorization process is completed doesn’t fire the event. Bummer!
  3. Use the WebAuthenticationBroker in my app. As I kept digging around the Windows Runtime, I chanced upon the WebAuthenticationBroker class. It looked like it could help me complete the authorization process, and with a few lines of code I was able to get the complete functionality working. Before I dig through code, let me explain the WebAuthenticationBroker in some detail.

The WebAuthenticationBroker

In a connected-app world, asking for user credentials via a trusted and secure mechanism is important to gain user consent and approval for an app. No one wants to be the developer whose app leaks user credentials or is the subject of a stealth attack to hijack user information. The Windows Runtime includes a series of APIs and necessary technologies that allow a developer to seek user credentials in a secure and trustworthy manner. The WebAuthenticationBroker is one of the tools that allow for Windows Store apps to use Internet-based authentication and authorization protocols such as OAuth and OpenID. So how does this work in my Dropbox sample app? Here’s how:

  1. I make the initial asynchronous request to Dropbox, which returns a token and token secret for my app. This initial request is made via the function oAuthLoginAsync.
  2. Once the oAuthLoginAsync function returns, in continuation of the sequence, I construct the URI where the authorization process should begin. In my sample, I’ve defined the initial URI as a constant string:
const std::wstring DropBoxAuthorizeURI = 
  L"https://www.dropbox.com/1/oauth/authorize?oauth_token=";
  1. I then build the HTTP request URI by appending the token returned by Dropbox.
  2. As an additional step, I construct the callback URI parameter by calling the WebAuthenticationBroker::GetCurrent­ApplicationCallbackUri function. Notice that I didn’t use the callback URI parameter in my desktop application because the callback parameter is optional, and I was relying on Internet Explorer to do the authorization task.
  3. Now my request string is ready and I can place my request. Instead of using either the C++ REST SDK http_client class or IHttpWebRequest2 interface to make Web service calls, I call the WebAuthenticationBroker::AuthenticateAsync function.
  4. The WebAuthenticationBroker::AuthenticateAsync function accepts two parameters: a WebAuthenticationOptions enumeration and a URI. An overloaded instance of the same function accepts the WebAuthenticationOptions enumeration and two URIs, one each for the beginning URI where the authentication process starts and the end URI where the authentication process ends.
  5. I use the first version of the AuthenticateAsync function and pass a None value for the WebAuthenticationOptions enumeration. For the URI, I pass the URI I’ve crafted for my Web request.
  6. The WebAuthenticationBroker sits between my app and the system. At the point where I call AuthenticateAsync, it creates a system modal dialog that’s a modal to my app.
  7. The broker attaches a Web host window to the modal dialog box it has created.
  8. The broker then selects a dedicated app container process that’s separate from the app container in which my app is executing. It also clears any persisted data from my app.
  9. The broker then starts the authentication process in this newly selected app container and navigates to the URI as specified in the AuthenticateAsync function.
  10. As users interact with the Web pages, the broker keeps checking each URL for the callback URI that has been specified.
  11. Once a match is found, the Web host ends navigation and sends a signal to the broker. The broker takes down the dialog box, clears any persisted cookies created by the Web host from the app container and returns the protocol data back to the app.

Figure 3 illustrates the Web­AuthenticationBroker modal dialog in my sample Dropbox app after the Web host has navigated to the initial URI. As Dropbox expects users to be signed in before the authorization page can be displayed, the Web host redirects navigation to the Dropbox sign-in page.

The Sign-in Page of Dropbox as Displayed in the Modal Dialog
Figure 3 The Sign-in Page of Dropbox as Displayed in the Modal Dialog

Once a user has signed in to Dropbox, the Web host then navigates to the actual authorization URI. This is shown in Figure 4. It’s clear from both Figure 3 and Figure 4 that the dialog is overlaid on top of my app UI. The UI also remains consistent irrespective of the source app calling the WebAuthenticationBroker::Authen­ticateAsync method. Because the entire experience remains consistent, users can provide credential information without worrying about apps handling credential information and such information leaking accidentally.

User Consent Being Asked for Application Authorization from Dropbox
Figure 4 User Consent Being Asked for Application Authorization from Dropbox

One important thing I didn’t mention is the need to call the WebAuthenticationBroker::AuthenticateAsync function from the UI thread. All C++ REST SDK Web requests are made on a background thread, and I can’t display the UI from a background thread. Instead, I use the system dispatcher and call its member function, RunAsync, to display the modal UI, as shown in Figure 5.

Figure 5 Using the System Dispatcher to Display the Modal UI

auto action = m_dispatcher->RunAsync(
  Windows::UI::Core::CoreDispatcherPriority::Normal,
  ref new Windows::UI::Core::DispatchedHandler([this]()
  {
    auto beginUri = ref new Uri(ref new String(m_authurl.c_str()));
    task<WebAuthenticationResult^> authTask(WebAuthenticationBroker::
      AuthenticateAsync(WebAuthenticationOptions::None, beginUri));
      authTask.then([this](WebAuthenticationResult^ result)
      {
        String^ statusString;
        switch(result->ResponseStatus)
        {
          case WebAuthenticationStatus::Success:
          {
            auto actionEnable = m_dispatcher->RunAsync(
              Windows::UI::Core::CoreDispatcherPriority::Normal,
              ref new Windows::UI::Core::DispatchedHandler([this]()
              {
                UploadFileBtn->IsEnabled = true;
              }));
          }
        }
      });
}));

Once the authorization process is completed, I run the dispatcher again to enable the “Upload File” button on my main UI. This button remains in a disabled state until my users have authenticated and authorized my app to access Dropbox.

Chaining Asynchronous Web Requests

Putting it all together is now easy. In all of the functions that don’t interface with the Windows Runtime, I’ve reused the same code from my desktop application. There are no major code changes except for this: the decision to use the WinRT StorageFile object versus a C++ iostream in the UploadFileToDropboxAsync function.

When writing apps for the Windows Store, there are some limitations that you need to live with. One of the limitations is the need to use WinRT StorageFile objects instead of using C++ streams to read and write data from files. When developing a Windows Store app and using the C++ REST SDK, all operations relating to files expect the developer to pass a StorageFile object rather than a C++ stream object. With that one minor change, I’m able to reuse all of my standard C++ code supporting OAuth and Dropbox authorization code in my Windows Store sample app.

Here’s my pseudo-code flow (I’ll discuss the individual functions after the pseudo-code):

On clicking the SignIn Button
  Call oAuthLoginAsync function
    Then call WebAuthenticationBroker::AuthenticateAsync
    Then enable the "Upload File" button on my UI
On clicking the "Upload File" button
   Call the Windows::Storage::Pickers::FileOpenPicker::
     PickSingleFileAsync function
    Then call oAuthAcquireTokenAsync function
    Then call UploadFileToDropboxAsync function

In the SignInBtnClicked button event handler shown in Figure 6, I first perform simple parameter validation to make sure that non-empty string values are passed for the ConsumerKey and ConsumerSecret parameters that I pass to Dropbox for authentication. Next I obtain an instance of the Dispatcher object associated with the CoreWindow’s current thread and store it in a member variable of the MainPage class. A Dispatcher is responsible for processing the window messages and dispatching the events to the app. Next I create an instance of the OnlineIdAuthenticator class. The OnlineIdAuthenticator class contains helper functions that enable me to pop up an app modal dialog box and complete the secure authorization workflow. This removes the need to launch a browser instance and switch app focus to the browser.

Figure 6 The SignInBtnClicked Function

void MainPage::SignInBtnClicked(Platform::Object^ sender, 
  RoutedEventArgs^ e)
{
  if ((ConsumerKey->Text == nullptr) || 
    (ConsumerSecret->Text == nullptr))
  {
    using namespace Windows::UI::Popups;
    auto msgDlg = ref new MessageDialog(
      "Please check the input for the Consumer Key and/or Consumer Secret tokens");
    msgDlg->ShowAsync();
  }
  m_dispatcher =
     Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher;
  m_creds = std::make_shared<AppCredentials>();
  m_authenticator = ref new OnlineIdAuthenticator();
  consumerKey = ConsumerKey->Text->Data();
  consumerSecret = ConsumerSecret->Text->Data();
  ConsumerKey->Text = nullptr;
  ConsumerSecret->Text = nullptr;
  OAuthLoginAsync(m_creds).then([this]
  {          
    m_authurl = DropBoxAuthorizeURI;               
    m_authurl += 
      utility::conversions::to_string_t(this->m_creds->Token());
    m_authurl += L"&oauth_callback=";
    m_authurl += WebAuthenticationBroker::
      GetCurrentApplicationCallbackUri()->AbsoluteUri->Data();
    auto action = m_dispatcher->RunAsync(
      Windows::UI::Core::CoreDispatcherPriority::Normal,
      ref new Windows::UI::Core::DispatchedHandler([this]()
    {
      auto beginUri = ref new Uri(ref new String(m_authurl.c_str()));
      task<WebAuthenticationResult^>authTask(
        WebAuthenticationBroker::AuthenticateAsync(
        WebAuthenticationOptions::None, beginUri));
      authTask.then([this](WebAuthenticationResult^ result)
      {
        String^ statusString;
        switch(result->ResponseStatus)
        {
          case WebAuthenticationStatus::Success:
          {
            auto actionEnable = m_dispatcher->RunAsync(
              Windows::UI::Core::CoreDispatcherPriority::Normal,
              ref new Windows::UI::Core::DispatchedHandler([this]()
              {
                UploadFileBtn->IsEnabled = true;
              }));
          }
        }
      });
    }));
}

I then call the OAuthLoginAsync function, which performs the login operation to Dropbox. Once the async function returns, I use the Dispatcher object’s RunAsync function to marshal the call back to the UI thread from my async task’s background thread. The RunAsync function takes two parameters: a priority value and a DispatchedHandler instance. I set the priority value to Normal and pass a lambda function to the DispatchedHandler instance. Within the lambda body, I call the static function AuthenticateAsync of the WebAuthenticationBroker class, which then displays the app modal dialog and helps complete the secure authorization.

Once the workflow is completed, the dialog is taken down and the function returns either a successful completion or any error conditions encountered. In my case, I just handle the WebAuthenticationStatus::Success return type and once again use the dispatcher object to enable the UploadFile Button on the app UI. Because all the functions I’m calling are async in nature, I need to use the dispatcher object to marshal calls to the UI thread if I want to access any UI elements.

Figure 7 shows the UploadFileBtnClicked event handler. There’s not much code in the handler itself. I make a call to the FileOpenPicker::PickSingleFileAsync function that allows for selecting a single text file through the picker interface. I then call the OAuthAcquireTokenAsync function as shown in Figure 8 and, upon successful completion, make a call to the UploadFileToDropBoxAsync function shown in Figure 9.

Figure 7 The UploadFileBtnClicked Function

void MainPage::UploadFileBtnClicked(  Platform::Object^ sender, 
  RoutedEventArgs^ e)
{
  using namespace Windows::Storage::Pickers;
  using namespace Windows::Storage;
  auto picker = ref new FileOpenPicker();
  picker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary;
  picker->FileTypeFilter->Append(".txt");
  task<StorageFile^> (picker->PickSingleFileAsync())
    .then([this](StorageFile^ selectedFile)
  {
    m_fileToUpload = selectedFile;
    OAuthAcquireTokenAsync(m_creds).then([this](){
      UploadFileToDropBoxAsync(m_creds);
    });
  });         
}

Figure 8 The OAuthAcquireTokenAsync Function

task<void> MainPage::OAuthAcquireTokenAsync(
  std::shared_ptr<AppCredentials>& creds)
{
  uri url(DropBoxAccessTokenURI);
  std::shared_ptr<OAuth> oAuthObj = std::make_shared<OAuth>();
  auto signatureParams =
    oAuthObj->CreateOAuthSignedParameters(url.to_string(),
    L"GET",
    NULL,
    consumerKey,
    consumerSecret,
    creds->Token(),
    creds->TokenSecret()
    );
  std::wstring sb = oAuthObj->OAuthBuildSignedHeaders(url);
  http_client client(sb);   
  // Make the request and asynchronously process the response.
  return client.request(methods::GET)
    .then([&creds](http_response response)
  {
    if(response.status_code() != status_codes::OK)
    {
      auto stream = response.body();                    
      container_buffer<std::string> inStringBuffer;
      return stream.read_to_end(inStringBuffer)
        .then([inStringBuffer](pplx::task<size_t> previousTask)
      {
        UNREFERENCED_PARAMETER(previousTask);
        const std::string &text = inStringBuffer.collection();
        // Convert the response text to a wide-character string.
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,
           wchar_t> utf16conv;
        std::wostringstream ss;
        ss << utf16conv.from_bytes(text.c_str()) << std::endl;
        OutputDebugString(ss.str().data());
        // Handle error cases.                   
        return pplx::task_from_result();
      });
    }
    // Perform actions here reading from the response stream.
    istream bodyStream = response.body();
    container_buffer<std::string> inStringBuffer;
    return bodyStream.read_to_end(inStringBuffer)
      .then([inStringBuffer, &creds](pplx::task<size_t> previousTask)
    {
      UNREFERENCED_PARAMETER(previousTask);
      const std::string &text = inStringBuffer.collection();
      // Convert the response text to a wide-character string.
      std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, 
        wchar_t> utf16conv;
      std::wostringstream ss;
      std::vector<std::wstring> parts;
      ss << utf16conv.from_bytes(text.c_str()) << std::endl;
      Split(ss.str(), parts, '&', false);
      unsigned pos = parts[1].find('=');
      std::wstring token = parts[1].substr(pos + 1, 16);
      pos = parts[0].find('=');
      std::wstring tokenSecret = parts[0].substr(pos + 1);
      creds->SetToken(token);
      creds->SetTokenSecret(tokenSecret);
    });
  });
}

Figure 9 The UploadFileToDropBoxAsync Function

task<void> MainPage::UploadFileToDropBoxAsync(
  std::shared_ptr<AppCredentials>& creds)
{
  using concurrency::streams::file_stream;
  using concurrency::streams::basic_istream;
  uri url(DropBoxFileUploadURI);
  std::shared_ptr<oAuth> oAuthObj = std::make_shared<oAuth>();
  auto signatureParams =
    oAuthObj->CreateOAuthSignedParameters(url.to_string(),
    L"PUT",
    NULL,
    consumerKey,
    consumerSecret,
    creds->Token(),
    creds->TokenSecret()
  );          
  std::wstring sb = oAuthObj->OAuthBuildSignedHeaders(url);
  return file_stream<unsigned char>::open_istream(this->m_fileToUpload)
    .then([this, sb, url](pplx::task<basic_istream<unsigned char>> previousTask)
  {
    try
    {
      auto fileStream = previousTask.get();
      // Get the content length, used to set the Content-Length property.
      fileStream.seek(0, std::ios::end);
      auto length = static_cast<size_t>(fileStream.tell());
      fileStream.seek(0, 0);
      // Make HTTP request with the file stream as the body.
      http_request req;
      http_client client(sb);
      req.set_body(fileStream, length);
      req.set_method(methods::PUT);
      return client.request(req)
        .then([this, fileStream](pplx::task<http_response> previousTask)
      {
        fileStream.close();
        std::wostringstream ss;
        try
        {
          auto response = previousTask.get();
          auto body = response.body();                  
          // Log response success code.
          ss << L"Server returned status code "
          << response.status_code() << L"."
          << std::endl;
          OutputDebugString(ss.str().data());
          if (response.status_code() == web::http::status_codes::OK)
          {
            auto action = m_dispatcher->RunAsync(
              Windows::UI::Core::CoreDispatcherPriority::Normal,
              ref new Windows::UI::Core::DispatchedHandler([this]()
              {
                using namespace Windows::UI::Popups;
                auto msgDlg = ref new MessageDialog(
                  "File uploaded successfully to Dropbox");
                msgDlg->ShowAsync();
              }));
          }
        }
        catch (const http_exception& e)
        {
          ss << e.what() << std::endl;
          OutputDebugString(ss.str().data());
        }
      });           
    }                         
    catch (const std::system_error& e)
    {
      // Log any errors here.
      // Return an empty task.
      std::wostringstream ss;
      ss << e.what() << std::endl;
      OutputDebugString(ss.str().data());
      return pplx::task_from_result();
    }
  });
}

The OAuthAcquireTokenAsync function performs the action to acquire the actual token associated with the Dropbox account. I first build the required access string and the HTTP request headers and call the Dropbox service for performing the credential validation. This HTTP request is of type GET and the response is returned as a stream of characters. I parse the stream and split the character stream to obtain the actual token and token secret values. These are then stored in the AppCredentials class instance.

Once I’ve successfully obtained the actual token and token secret values from Dropbox, it’s time to put them to use by uploading a file to Dropbox. As is the norm with any Dropbox Web endpoint access, I first build the parameter string and HTTP headers. I then call the Dropbox service endpoint associated with uploading files. This HTTP request is of type PUT because I’m attempting to place content on the service. Before I place content, I also need to let Dropbox know about the size of the content. This is specified by setting the content_length property of the HTTP_request::­set_body method to the size of the file being uploaded. Once the PUT method returns successfully, I use the dispatcher object to display a success message to the user.

Linux Is Next

Integrating the C++ REST SDK in Windows 8 applications (both Windows Store and desktop) is simple and straightforward. Add the benefits of writing code that can be shared between both the platforms,  of using modern C++ programming idioms, and the fact the code is portable across both Windows and non-Windows apps, and you have a winner. You can stop worrying about platform-specific intricacies as they’re related to network APIs and instead use your time to think about features that your application should support. In this simple example, I’ve used the C++ REST SDK to authenticate a user with Dropbox and then upload a file to the Dropbox cloud. For more information about the Dropbox REST API, you can refer to the documentation at bit.ly/10OdTD0. In a future article, I’ll show you how to accomplish this same task from a Linux client.


Sridhar Poduri is a program manager on the Windows team at Microsoft. A C++ aficionado and author of the book, “Modern C++ and Windows Store Apps” (Sridhar Poduri, 2013), he blogs regularly about C++ and the Windows Runtime at sridharpoduri.com.

Thanks to the following Microsoft technical experts for reviewing this article: Niklas Gustaffson, Sana Mithani and Oggy Sobajic

 

Rate: