Once
upon a time, back in the Web 1.0 days, Web sites were silos of content
to be read and nothing more. As Web 2.0 buzzed into development shops,
Web sites became online services with APIs used by developers to mix and
match components, data and functionality. Now, mashups allow developers
to access rich content libraries without the overhead of housing the
data in their server rooms.
With the Windows Runtime (WinRT), you
can bring the power of mashups to your next Windows Store app. Whether
you’re managing data with XmlHttpRequest (XHR) or authenticating to a
remote service with the WebAuthenticationBroker class, Windows Library
for JavaScript (WinJS) and the Windows Runtime make mashing online
services with your app easy.
Contoso Photo Finish
In this
article I’m going to build a mashup called Contoso Photo Finish. It lets
runners track their miles and post a picture from their runs. Many
runners like to share information such as distance and location of their
runs on social networks. Contoso Photo Finish lets users tell their
friends about their runs with comments and pictures on Facebook. This
app will connect to two different services:
- Windows SkyDrive to retrieve a picture from the run
- Facebook to post the picture for their friends to view
Contoso
Photo Finish will combine these services to provide a connected
experience for the user. This article assumes you have Visual Studio
2012 open with the JavaScript | Windows Store | Blank App template ready
for you to start coding.
Mashup Hurdles: Authorization and Authentication
If
an app (Web or Windows Store) wants to post content to Facebook, the
first hurdle to overcome is authentication. Facebook needs to know who’s
connecting to it. When users try to log in to apps, they claim an
identity (usually in the form of a username) and a credential (such as a
password, security token, biometric device and so on) to prove they
should have access to the requested identity. Authentication is the
process of validating a user’s identity through a credential.
After
authenticating the user, the mashup has another challenge: determining
what the user can do in the system. Authorization is the process of
permitting or denying actions an identity is attempting to perform based
on some attribute or security role membership. As an example, in
Twitter my identity is not permitted to delete all the tweets of all
users. I’m not authorized to perform that action because I’m not a
member of the security role with permission to do that. Together,
authentication and authorization (A&A) represent the question: User,
who are you and what can you do?
Mashups amplify these challenges
because the developer building the mashup doesn’t have access to where
the identities, credentials and security roles are stored (often
referred to as a credential store). So if I can’t verify who’s who and
what they can do, how can I build a mashup for apps such as Facebook?
OAuth to the rescue!
OAuth: Accessing Resources, Not Apps
OAuth
addresses the challenge of A&A for mashups. Imagine App X wants to
access content from Online Service Y. Instead of the user authenticating
to App X, the user authenticates to Online Service Y because the user’s
credentials are stored in Online Service Y’s credential store. The user
then permits App X to access specified resources from Online Service Y
for a limited time. Permission to access Online Service Y resources is
returned to App X as an access token (sometimes referred to as just a
“token”).
In traditional Web A&A models, two participants work
together to determine a user’s access: the app and the user (or the
server and client). In OAuth a third participant is introduced: the
resource server. Resource servers are the third parties who have a
resource (such as a photo) stored on the server that a client needs to
access. In Contoso Photo Finish, Facebook is a resource server. The
resource that Contoso Photo Finish wants to access is the user’s status,
in order to post a message.
OAuth Clients and Their Authentication Processes
There
are two types of clients in OAuth, and which type to use is decided by
the client’s level of trust. Confidential clients can keep their
credentials secure and are meant for highly trusted environments.
Examples of confidential clients are server-side Web apps where the
client secret can be maintained in a controlled, secure environment.
Confidential clients use the Authorization Code process to obtain a
security token by providing the client secret to the resource server as a
means of authenticating the client.
Public clients can’t keep
their credentials secure because they run in a hostile environment.
Example public clients would be user-agent apps (that is, JavaScript Web
apps) or native apps (such as Windows Store apps). Public clients use
the “implicit grant” process to obtain a security token because the
client secret can’t be stored in a secure manner within a hostile
environment that’s outside the developer’s control.
Windows Store
apps can be configured to use either implicit grant or authorization
code processes for OAuth (you can read more about implicit grant and
authorization code processes at
bit.ly/yQjyQZ). It’s a security best practice to use implicit grant any time an app is out of the developer’s control.
In
Figure 1
I examine an implicit grant process where the client starts the
conversation by attempting to determine a user’s identity with the
resource server.
Figure 1 Windows Store App Implicit Grant Conversation
The steps illustrated in
Figure 1 are explained here:
- The Windows Store app needs to perform some functionality that requires access to the Facebook API.
- The
user connects to the resource server through a URI that includes
information about the Windows Store app trying to access the Facebook
API. This is usually in the form of an app ID or client ID code. The
user provides a username and password to log in to Facebook.
- Assuming successful login, Facebook provides the Windows Store app with an access token.
- The
Windows Store app can now provide the user with data from the Facebook
API using the access token provided by Facebook to get the user’s feed,
post pictures and so on.
Making this conversation happen is surprisingly easy through the new Windows.Security.Authentication.Web namespace.
WebAuthenticationBroker
In Windows Store apps the WebAuthenticationBroker class (
bit.ly/RW8czT)
is the component that will communicate with the resource server,
provide the login controls and respond to a successful login, all
without needing the Windows Store app to know anything about the user’s
credentials. For the sample app, Contoso Photo Finish needs to post
pictures to Facebook. This requires the user to authenticate to Facebook
and receive an access token.
Add a new page control to the
Contoso Photo Finish project called input.html. By default Visual Studio
provides a lot of markup for you. Replace “<p>Content goes
here.</p>” in the Main content section with the following button:
- <input type="button" id="btnAddRun" value="Add Run" />
This is the button the user will click to add a run to Photo Finish. Now open input.js and add the following function:
- function btnAddRun_Click(e) {
- var facebookOauthUrl = "https://www.facebook.com/dialog/oauth";
- var facebookClientId = "[YOUR CLIENT ID]";
- var redirectUrl = "https://www.facebook.com/connect/login_success.html";
- var requestUri = Windows.Foundation.Uri(facebookOauthUrl +
- "?client_id=" + facebookClientId +
- "&redirect_uri=" + encodeURIComponent(redirectUrl) +
- "&response_type=" +
- "token&scope=read_stream,publish_actions&display=popup");
- var callbackUri = Windows.Foundation.Uri(redirectUrl);
-
- }
This
code establishes the variables that will be used in the authentication
request. The first variable is the URL of the Facebook OAuth service.
Client ID is the app identifier used by Facebook to identify Contoso
Photo Finish as the app with which the Facebook API will interact. An
identifier such as this is normally assigned to an app when it’s
registered with the resource server. Some resource servers refer to the
identifiers as client id, app id or just id. Which id to use will be
found in the resource server’s API documentation.
The redirectUrl
parameter determines where the app will go after the user authenticates
and approves the app’s access to specified resources. In this case the
redirectUrl is set to a Facebook standard that’s available to all
Facebook API apps. Some services require the Windows Store app’s URI to
be identified with the resource server when the app is registered. The
app’s URI can be found using the Web authentication broker’s
getCurrentApplicationCallbackUri method. This will return the app’s
local context URI (beginning with “ms-app://”). Some resource servers
don’t support ms-app:// as a valid protocol for a redirectUrl, in which
case you should check for a default redirect address such as what
Facebook provides.
Next, the callbackUri is defined. This is the
address that informs the Web authentication broker when authentication
is complete and returns control back to the Windows Store app. The
broker never actually goes to this URL; it simply watches for the
resource server to call for this page and then returns the callbackUri
with any query string or hash parameters that were appended. In this
case, the hash parameter “access_token” will provide Contoso Photo
Finish the token needed to interact with the API.
The
WebAuthenticationBroker class uses the authenticateAsync method to
connect to and complete the authentication process with the resource
server. When authenticateAsync is called, the app opens a pop-up that
displays the resource server’s login screen, as shown in
Figure 2. Once authentication is complete or the callbackUri is encountered, the pop-up will close.
Figure 2 Resource Server’s Login Pop-up from authenticateAsync
A
key advantage to using this pop-up is that the Windows Store app never
handles or needs to know the user’s credentials for the resource
manager. All the app knows is the access token that’s returned by the
resource server. This separation keeps the resource server’s credentials
isolated from the app, which avoids the security risk of the app
storing the credentials. On top of the security benefits, the developer
doesn’t have to code anything to get this interface; it’s built in to
the authenticateAsync method. When developers call the method, the
interface comes with it.
Now, back to code. Replace the “Web authentication broker will go here” comment with the following code:
- Windows.Security.Authentication.Web.WebAuthenticationBroker.
- authenticateAsync(Windows.Security.Authentication.Web.
- WebAuthenticationOptions.none, requestUri, callbackUri)
- .done(
- function (result) {
-
- },
- function (ex) {
- Log(ex);
- }
- );
The authenticateAsync method takes three parameters (the third is optional):
- WebAuthenticationOptions:
This is used to provide instructions to the Web authentication broker
on how to render the authentication dialog or what data to return in the
response. In the preceding example, the app uses “none” to show a
common implementation that passes no options to the broker using a
default setup.
- requestUri: This is the login entry point for the
resource server. In this case, Contoso Photo Finish is connecting to
Facebook’s OAuth service. RequestUri must be over a secure connection
using the HTTPS protocol.
- callbackUri: This is the page that,
when navigated to, returns control back to the Web authentication broker
as described earlier. This argument is optional, but if the resource
server can’t (or won’t) redirect to ms-app://, this parameter is how the
app will escape the resource server’s control. For example, in the
earlier code when https://www.facebook.com/connect/login_success.html is
navigated to after a successful login, the Web authentication broker
will take control of the app from the resource server by closing the
authentication dialog and processing the success of the promise.
CallbackUri doesn’t have to be in the immediate next page; it could be
after a wizard or some other process that must occur on the resource
server’s site. This URI will normally be the same as the redirectUrl but
provides the flexibility to extend the authentication process, if
necessary.
If the Web authentication broker connects to the
resource server, the promise succeeds. Detecting the result of the
authentication process is done through the WebAuthenticationResult
object’s ResponseStatus property. In the preceding code, the result
argument is a WebAuthenticationResult object with three properties:
Response Data (data from the resource server), ResponseErrorDetail (if
something went wrong, what was it?) and ResponseStatus (what’s the
status of the authentication?). Replace the “Check the response status
here” comment with the code shown in
Figure 3.
Figure 3 Working with the Result of the Authentication Process
- switch (result.responseStatus) {
- case Windows.Security.Authentication.Web.WebAuthenticationStatus.success:
- var fragment = Windows.Foundation.Uri(result.responseData).fragment;
- if (fragment.indexOf("#access_token=") != -1) {
- var token = fragment.substring(
- new String("#access_token=").length,
- fragment.indexOf("&expires_in="));
-
- }
- break;
- case Windows.Security.Authentication.Web.WebAuthenticationStatus.userCancel:
- Log(window.toStaticHTML(result.responseData));
- Display("User cancelled the authentication to Facebook.");
- break;
- case Windows.Security.Authentication.Web.WebAuthenticationStatus.errorHttp:
- Log(window.toStaticHTML(result.responseData));
- Display("An error occurred while communicating with Facebook.");
- break;
- }
In the
Figure 3
code, each status is checked, with the Log method recording the
information from the resource server and the Display method telling the
user what has occurred. For error messages, remember to display
user-friendly messages to improve usability and reduce accidental
exposure of sensitive information from a system-generated error message.
If the authentication is successful, the URI fragment returned from
Facebook is parsed and stored in the token variable for use in the API
call (see the “Getting and Posting Information via XHR” section for
implementation details).
With the btnAddRun_Click function complete, connect it to the btnAddRun object in the WinJS.UI.Pages.define { ready } function:
- var btnAddRun = document.getElementById("btnAddRun");
- if (null != btnAddRun)
- btnAddRun.addEventListener("click", btnAddRun_Click, false);
At
this point the app has an access token showing that the user is
authenticated to the resource server. In the last section the app will
execute API commands to send data, but first the app needs something to
send to Facebook.
Getting the Picture
Windows 8 provides a
variety of contracts that allow apps to talk with each other, such as
search, share and file picker. These contracts can turn any app into a
mashup with just a few lines of code. Contoso Photo Finish is going to
tap into the power of the file picker contract to find an image for the
user’s run.
One of the many things I love about my Windows Phone
is the integration with SkyDrive. I can upload my photos instantly to
storage in the cloud so the next time I break my phone (which is often),
my pictures are waiting for me online. The SkyDrive app for Windows 8
provides data to the file picker, making the selection of files from my
SkyDrive account as simple as selecting them from the picture library.
The next part of the Contoso Photo Finish mashup will consume data from
the SkyDrive app through the file picker. To do this, input.html needs
some, well . . . inputs.
Replace the btnAddRun button with the code shown in
Figure 4.
This code includes the input fields for the user to provide content for
Contoso Photo Finish. The btnSelectPhoto button will use the file
picker to select which file on the system to use. Add a new function to
input.js that will be the click handler for btnSelectPhoto:
- function btnSelectPhoto_Click(e) {
- var imgSelectedPhoto = document.getElementById("imgSelectedPhoto");
- var filePicker = new Windows.Storage.Pickers.FileOpenPicker();
- filePicker.fileTypeFilter.replaceAll([".jpg", ".jpeg", ".png"]);
- filePicker.suggestedStartLocation =
- Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
- filePicker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;
-
- }
Figure 4 Input Fields to Provide Content for the App
- <p>
- <label>Distance</label>
- <input type="number" min="0" max="15" id="txtDistance"
- required /> miles
- </p>
- <p>
- <label>Comment</label>
- <input type="text" min="0" max="15" id="txtComment" />
- </p>
- <p>
- <label>Photo</label>
- <input id="btnSelectPhoto" value="Select Photo" type="button" />
- <img src="" id="imgSelectedPhoto" alt="Selected Photo" />
- </p>
- <p>
- <input type="button" id="btnAddRun" value="Add Run" />
- </p>
The
function begins by setting up the imgSelectedPhoto variable, which will
be used to display the selected photo to the user. Next, the code
creates a file picker object. The file picker object allows Contoso
Photo Finish to choose files or folders (in this case just files) on the
system or in other apps participating in the file picker contract to
open and interact within the app. By using the file type filter, the
code limits which file extensions are accessible to the file picker. The
app can only load images to Facebook (by design), so limiting the file
picker to only work with specified image file types keeps the user from
selecting files that have invalid extensions that don’t meet the desired
functionality. Also, due to the app’s focus on images, the file
picker’s start location is set to the picture library. This could be a
variety of default locations (such as music library, document library,
home group and so on), but when dealing with pictures, the picture
library is a common-sense starting point. The last setting for the file
picker is to set the viewMode to thumbnail. This displays a preview of
the file and is ideal for image selection.
With the options set,
it’s time to select the file to use for the run. First, add these two
variable declarations directly below the “use strict” statement:
- var selectedPhotoStream = null;
- var selectedPhotoFile = null;
These
will hold the file and stream values for the btnAddRun_Click function
when the data is loaded to Facebook. Now replace the “Pick file here”
comment with the code shown in
Figure 5.
Figure 5 Picking a File
- filePicker.pickSingleFileAsync().then(
- function (storageFile) {
- if (storageFile) {
- selectedPhotoFile = storageFile;
- selectedPhotoFile.openAsync(
- Windows.Storage.FileAccessMode.read).then(
- function (stream) {
- selectedPhotoStream = stream;
- document.getElementById("imgSelectedPhoto").src =
- URL.createObjectURL(selectedPhotoFile);
- },
- function (ex) {
- Log(ex);
- Display("An error has occurred while reading the file.");
- });
- }
- else {
- Display("File was not selected");
- }
- });
Figure 5 looks like a lot of code, but it boils down to three actions:
- Select the file that the app will be using via the file picker (pickSingleFileAsync).
- Open the file stream to be read (openAsync).
- Store the stream and file into variables for later use.
All
of the code is standard for working with files and streams with one
exception: URL.createObjectURL takes an object and builds a URL for the
object to be displayed through an image object. The createObjectURL
method works with many object types including Stream, StorageItem and
MediaCapture. In general it’s used to display media content (image,
audio or video) in a Windows Store app. One thing to keep in mind when
using createObjectURL is that when you’re finished using the URL, make
sure to dispose of it through the URL.revokeObjectURL method. This will
ensure optimal memory usage and prevent the Windows Store app from
getting bogged down with too many temporary URLs. For more information
about createObjectURL, check out the MSDN documentation at
bit.ly/XdhzOm.
Finally,
associate the btnSelectPhoto_Click event with the btnSelectPhoto
object. Add the following code in the WinJS.UI.Pages.define { ready }
function:
- var btnSelectPhoto = document.getElementById("btnSelectPhoto");
- if (null != btnSelectPhoto)
- btnSelectPhoto.addEventListener(
- "click", btnSelectPhoto_Click, false);
At
this point, Contoso Photo Finish has content to post and I have the
mechanism to authenticate to Facebook for posting. Now the app just
needs to interact with the API and throw the content online.
Getting and Posting Information via XHR
Remember
when AJAX was new and exciting? XHR came on to the scene in Internet
Explorer 5.5 and made Web developers start to rethink how Web apps were
being made. Time passed and AJAX grew with many different libraries
(such as jQuery) into an easy-to-understand and (more important)
easy-to-implement solution. WinJS.xhr continues this tradition with a
simple API to get and post data to online services.
Return to the btnAddRun_Click function and replace the “Add API calls here” comment with the code shown in
Figure 6.
Figure 6 Generating a Blob Object from selectedPhotoStream
- var fileBlob = MSApp.createBlobFromRandomAccessStream(
- selectedPhotoFile.contentType,
- selectedPhotoStream);
- var message = "I just ran " + document.getElementById(
- "txtDistance").value + " miles with PhotoFinish! " +
- document.getElementById("txtComment").value;
- var data = new FormData();
- data.append("source", fileBlob);
- data.append("filename", selectedPhotoFile.name);
- data.append("access_token", token);
- data.append("message", window.toStaticHTML(message));
- WinJS.xhr({
- type: "POST",
- url: "https://graph.facebook.com/me/photos",
- data: data,
- }).then(
- function (photoid_response) {
- ProcessResponse(photoid_response);
- },
- function (ex) {
- Display("An error occurred while posting the photo.");
- Log(ex);
- });
Earlier,
the app stored the StorageFile and Stream into the selectedPhotoFile
and selectedPhotoStream variables.
MSApp.createBlobFromRandomAccessStream takes the selectedPhotoStream and
generates a Blob object (
bit.ly/Stfu9z)
that the app will later pass to Facebook as a POST parameter. This is a
useful feature for converting WinRT objects into a format that can be
transferred via HTTP.
Next, the code uses the FormData object (new
to HTML5) to create the parameters that will be sent in the HTTP POST.
FormData is a key/value pair object with just one method: append. Using
the append method, developers can build form fields dynamically and
submit them with a POST as if the form’s submit event was called.
FormData also provides the parameters as if they belonged to a form with
multipart/form-data encoding. This allows developers to post any input
type (including files) to the target server.
Notice in the FormData.append method call that I use toStaticHTML (
bit.ly/ZRKBka)
to ensure that the message content is safe for delivery. Using
toStaticHTML will remove any event attributes or script content from the
message variable prior to adding it to the FormData object. While I
imagine Facebook is good about preventing cross-site scripting attacks
(among others), as a mashup developer, I want to provide clean content
to my fellow apps. The Internet is a big place, so we all need to watch
out for each other.
The rest of the code block is the WinJS.xhr call. This time around, the code uses some more attributes of XHR, including:
- type:
This sets the HTTP method to be used. By default type is set to GET.
This code uses POST because the app is sending content to the Facebook
API.
- data: This consists of parameters to pass with the POST.
When
the promise is returned in the success method, Contoso Photo Finish
processes the photo ID for later retrieval. If an error occurs, the
standard error message is displayed and the exception is logged.
WinJS.xhr
is a lot like other XHR wrappers. If you’re familiar with JavaScript
libraries such as jQuery, you’ll be able to pick up WinJS.xhr easily.
One gotcha that you could encounter is that, unlike XHR, there’s no
timeout option on the WinJS.xhr method. Setting a timeout is
accomplished by wrapping the WinJS.xhr call with a WinJS.Promise.timeout
method (
bit.ly/Qgtx7a). By adding the following code to the beginning of the WinJS.xhr call, I set a timeout of 10 seconds to the POST:
- WinJS.Promise.timeout(1000, WinJS.xhr({ ... });
If
the WinJS.xhr promise doesn’t complete within 10 seconds, the promise
will timeout and be handled through the timeout promise’s error
function.
First Steps in Mashing Your App
In this article I
examined authenticating, getting files and sending data with WinJS and
the Windows Runtime. These foundation skills can be built upon to design
Windows Store apps that are limited only by your imagination and
developer keys. Take the material in this article and explore your
favorite online service. Using WinJS.xhr, your Windows Store app can
interact with the countless APIs available online. With the Web
authentication broker, your app can connect users with their online
personalities, content and communities using OAuth or OpenID. WinJS and
the Windows Runtime give you all the tools to easily build an app that’s
greater than the sum of its online services.
Tim Kulp leads the development team at FrontierMEDEX in Baltimore. You can find Kulp on his blog at seccode.blogspot.com or on Twitter at Twitter.com/seccode, where he talks code, security and the Baltimore foodie scene.
Thanks to the following technical experts for reviewing this article: Sunil Gottumukkala and Jeremy Viegas