Home > REST, Windows Phone 8 > Google OAuth 2.0 on Windows Phone

Google OAuth 2.0 on Windows Phone

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.

About these ads
Categories: REST, Windows Phone 8
  1. thunderluca
    June 27, 2013 at 07:01

    Great article, I couldn’t find anything better!

    I have only one question, I note that this application is write for Windows Phone 8 (.NET 4.5), because of use of SemaphoreSlim class that is incompatible with Windows Phone 7.x, how can I resolve this code lack in that case?

    • thunderluca
      December 15, 2013 at 11:42

      After months, I found a solution for an “universal” access to Google oAuth2 from any version of Windows Phone (both 7.x & 8.x).

      First request is to use the RestSharp library, that simplify http requests and some code lines, then you can only use a generic RestClient (in this case, “http://accounts.google.com”) and two RestRequest (“/o/auth2/auth/” & “/o/auth2/token”), first to retrieve access_token and refresh token and second for retrieve new access token after that expires. :)

  2. August 6, 2013 at 05:35

    This helped me so much. Thank you!!

  3. Veejay
    August 27, 2013 at 06:23

    Thanks for the great article … and just curious to know that how we can add a feature to fetch user contact list with some basic details, please share your valuable thoughts.

  4. karthikeyan
    December 14, 2013 at 14:36

    can u tell me what is authEndpoint ?

    • thunderluca
      December 15, 2013 at 11:38

      authEndPoint is refered to oAuth request type that is posted after request domain (in this case, “http://accounts.google.com/” ). As in Google oAuth, for first token authorization, the authEndPoint used is “/o/auth2/auth”, instead for every request about access token or refresh token you must use “/o/auth2/token” ;)

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 28 other followers

%d bloggers like this: