Archive

Archive for the ‘Windows Phone 8’ Category

Google OAuth 2.0 on Windows Phone

March 11, 2013 6 comments

Introduction

OAuth is a protocol for authorization. It allows desktop, mobile and web applications to access web resources (mostly REST services) on behalf of a user. The protocol permits this without the user having to share its credentials (typically, a username and password pair) with the application. OAuth is a widely implemented protocol. Various companies (like Facebook, Twitter, Google, Microsoft, Dropbox …) use it to protect their APIs.

In this article, I will explain how we can implement support for Google OAuth in a Windows Phone application.

How does OAuth work

In a nutshell when developing a mobile application, a developer must register its application to the vendor of the service (in our case Google) who will assign a clientId and a clientSecret to the application.

The login flow starts with the application opening an embedded web browser control. This web control must load a specific Google login page. In the query string, the clientId of the application and the requested scopes are sent to the login page. Scopes are the actions the application wants to perform on behalf of the user. Google will handle the user authentication and consent, but at the end an authorization code is sent to the web browser control (using the “title” of the final HTML page).

OAuth Windows Phone authorization flow

After having received the authorization code, the application can exchange this for an access token and a refresh token. The access token can be used by the application to perform the necessary operations on behalf of the user. The refresh token must be stored for future use; it allows to request a new access token when the previous one expired.

Implementing Google OAuth on Windows Phone

You will have to register your application at https://code.google.com/apis/console#access. This will provide you a clientId and clientSecret that you can use to identity your application to the user when performing the OAuth flow.

My implementation consists of 2 big components, a class called “OAuthAuthorization” and a Windows Phone Page called “LogingPage”.

When the developer calls the method “Authorize” of the OAuthAuthorization class, the login page is opened on the screen. This page will show the embedded web browser control full screen (an important detail is that scripting support for this embedded must be activated; by default it is disabled). The Google login page is opened and navigated event handler is attached to the browser control. When the web browser control is redirected to the success page of the login flow, the authorization code is extracted and the login page is closed.


public partial class LoginPage : PhoneApplicationPage
{
    public LoginPage()
    {
        InitializeComponent();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);

        IDictionary parameters = this.NavigationContext.QueryString;

        string authEndpoint = parameters["authEndpoint"];
        string clientId = parameters["clientId"];
        string scope = parameters["scope"];

        string uri = string.Format("{0}?response_type=code&client_id={1}&redirect_uri={2}&scope={3}",
            authEndpoint,
            clientId,
            "urn:ietf:wg:oauth:2.0:oob",
            scope);

        webBrowser.Navigate(new Uri(uri, UriKind.Absolute));
    }

    private void webBrowser_Navigated(object sender, NavigationEventArgs e)
    {
        string title = (string)webBrowser.InvokeScript("eval", "document.title.toString()");

        if (title.StartsWith("Success"))
        {
            string authorizationCode = title.Substring(title.IndexOf('=') + 1);
            PhoneApplicationService.Current.State["MobileOauth.AuthorizationCode"] = authorizationCode;

            NavigationService.GoBack();
        }
    }
}

Using the client id and the client secret, the OAuthAuthorization class will call a Google REST API to exchange the authorization code for an access token and a refresh token.

public class OAuthAuthorization
{
    private readonly string authEndpoint;
    private readonly string tokenEndpoint;
    private readonly PhoneApplicationFrame frame;

    public OAuthAuthorization(string authEndpoint, string tokenEndpoint)
    {
        this.authEndpoint = authEndpoint;
        this.tokenEndpoint = tokenEndpoint;
        this.frame = (PhoneApplicationFrame)(Application.Current.RootVisual);
    }

    public async Task Authorize(string clientId, string clientSecret, IEnumerable scopes)
    {
        string uri = string.Format("/MobileOauth;component/LoginPage.xaml?authEndpoint={0}&clientId={1}&scope={2}",
                                    authEndpoint,
                                    clientId,
                                    string.Join(" ", scopes));

        SemaphoreSlim semaphore = new SemaphoreSlim(0, 1);

        Observable.FromEvent(
            h => new NavigatingCancelEventHandler(h),
            h => this.frame.Navigating += h,
            h => this.frame.Navigating -= h)
                    .SkipWhile(h => h.EventArgs.NavigationMode != NavigationMode.Back)
                    .Take(1)
                    .Subscribe(e => semaphore.Release());

        frame.Navigate(new Uri(uri, UriKind.Relative));

        await semaphore.WaitAsync();

        string authorizationCode = (string)PhoneApplicationService.Current.State["MobileOauth.AuthorizationCode"];

        return await RequestAccessToken(authorizationCode, clientId, clientSecret);
    }

    public async Task RefreshAccessToken(string clientId, string clientSecret, string refreshToken)
    {
        HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(tokenEndpoint);
        httpRequest.Method = "POST";
        httpRequest.ContentType = "application/x-www-form-urlencoded";

        using (Stream stream = await httpRequest.GetRequestStreamAsync())
        {
            using (StreamWriter writer = new StreamWriter(stream))
            {
                writer.Write("refresh_token=" + Uri.EscapeDataString(refreshToken) + "&");
                writer.Write("client_id=" + Uri.EscapeDataString(clientId) + "&");
                writer.Write("client_secret=" + Uri.EscapeDataString(clientSecret) + "&");
                writer.Write("grant_type=refresh_token");
            }
        }

        using (WebResponse response = await httpRequest.GetResponseAsync())
        {
            using (StreamReader streamReader = new StreamReader(response.GetResponseStream()))
            {
                string result = streamReader.ReadToEnd();
                TokenPair tokenPair = JsonConvert.DeserializeObject(result);
                tokenPair.RefreshToken = refreshToken;

                return tokenPair;
            }
        }
    }

    private async Task RequestAccessToken(string authorizationCode, string clientId,
                                            string clientSecret)
    {
        HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(tokenEndpoint);
        httpRequest.Method = "POST";
        httpRequest.ContentType = "application/x-www-form-urlencoded";

        using (Stream stream = await httpRequest.GetRequestStreamAsync())
        {
            using (StreamWriter writer = new StreamWriter(stream))
            {
                writer.Write("code=" + Uri.EscapeDataString(authorizationCode) + "&");
                writer.Write("client_id=" + Uri.EscapeDataString(clientId) + "&");
                writer.Write("client_secret=" + Uri.EscapeDataString(clientSecret) + "&");
                writer.Write("redirect_uri=" + Uri.EscapeDataString("urn:ietf:wg:oauth:2.0:oob") + "&");
                writer.Write("grant_type=authorization_code");
            }
        }

        using (WebResponse response = await httpRequest.GetResponseAsync())
        {
            using (StreamReader streamReader = new StreamReader(response.GetResponseStream()))
            {
                string result = streamReader.ReadToEnd();
                return JsonConvert.DeserializeObject(result);
            }
        }
    }
}

Using the OAuth implementation

The implemented framework is simple in usage. The following example shows how to use it:

// Request an access token
OAuthAuthorization authorization = new OAuthAuthorization(
    "https://accounts.google.com/o/oauth2/auth", 
    "https://accounts.google.com/o/oauth2/token");
TokenPair tokenPair = await authorization.Authorize(
    ClientId,
    ClientSecret,
    new string[] {GoogleScopes.CloudPrint, GoogleScopes.Gmail});

// Request a new access token using the refresh token (when the access token was expired)
TokenPair refreshTokenPair = await authorization.RefreshAccessToken(
    ClientId,
    ClientSecret,
    tokenPair.RefreshToken);

The access token must be sent using the Authorization: Bearer HTTP header when calling a Google API.

Conclusion

Implementing OAuth support on Windows Phone is quite simple to do. The authentication and user consent logic is handled by Google and integrated into an app by embedding the web browser control. Once a token is obtained, the application can perform the requested actions on behalf of the user until the user revokes the access for the application.

I have put the source code on GitHub as a reusable framework. The OAuth framework should also be usable with the various other OAuth providers.

You can find it at: https://github.com/pieterderycke/MobileOAuth.

Categories: REST, Windows Phone 8

Why Windows Phone 8 will succeed

November 8, 2012 1 comment

Windows Phone 7 was a great operating system, but the adaption rates were not superbly good. Microsoft had to abandon its old Windows Mobile operating system and had to play catch-up with IOS and Android who appeared years earlier on the mobile OS scene. This resulted in a niche market share for Microsoft.

At the BUILD conference, Microsoft unveiled its newest mobile OS: Windows Phone 8. It looks like an incremental update of the platform point of view GUI, but under the hoods it is a revolutionary update. The internals of WP8 are completely different: based on a NT kernel instead of the CE kernel.

In this article I will explain why I believe WP8 will be able to succeed were WP7 failed to deliver. Why it will be the OS that will allow Microsoft to retake market share in the mobile OS scene.

Let’s start by looking at the Metro GUI … euhm I mean: “Microsoft design language”. Metro is a refreshing and intuitive new design language. When WP7 was released, it was the only platform were Metro was used. Today it has become commonplace, Metro is used on the desktop, on the server, on tablets, on the phone, on the Xbox, on the web, … All of Microsoft offerings are standardizing on the Metro look & feel, offering an integrated end-user experience.

If we look under the hoods, we see the same kernel and the same Windows RT APIs as for the desktop and the tablets. This makes it easier for developers to build applications targeting all platforms. And although a lot of people claim to end of the PC is near, PCs are still used a lot and Windows is the OS that powers most of these machines. In the next couple of months all new PCs will start shipping with Windows 8 installed. Making Windows 8 an important platform for any serious App developer. Windows Phone 8 will also benefit from this.

We should also not forget the enormous catalogue of games available for Windows and powered by DirectX. The availability of C++ and DirectX on WP8 unlocks this catalogue for easy porting to phones. This is a great opportunity for WP8. In the end, the type of applications that matter the most are games. They are the best-selling applications for all (mobile) platforms. And who would not want to play the games of his teenage years like Grand Theft Auto Vice City, Call of Duty 2, Oblivion, … on his mobile when they would become available?

Finally, I am noticing that the media is very positive about the various Windows Phone devices being released. The designs of the new devices are very nice looking and making it great devices to own.

Will my predictions be correct? Only the future can tell 🙂

Categories: Windows Phone 8