Click here to Skip to main content
Click here to Skip to main content

Google OAuth2 on Windows Phone

By , 30 Jan 2012
 

Introduction

Google exposes a rich set of RESTful APIs that allow apps to interact with their applications, services and data. While Google supports a number of authentication and authorization mechanisms, they recommend using OAuth2, so since I thought I'd explore how to make that work on WP7.

If you're like me as you approach consuming Google services, you'll read up on OAuth2, go over some example code and hope you can find something to just drop into your app and move on the the interesting stuff. While there is plenty of documentation out there on OAuth2 and examples of how to use it, even a .NET library from Google, there are a few subtleties that need to be built into an app to deal with a number of different use cases. I haven't found any detailed explanation of some of those or good approaches to build them into an app in such a way that they are well encapsulated.

  • OAuth2 for an installed application is a multi-step process. The app doesn't capture a username and password from the user but rather displays an embedded web page for authentication and authorization. On success the app then receives a code which must be exchanged for an access token.
  • Authentication happens synchronously but the communication is asynchronous in nature.
  • The access token comes with an expiration. When it expires it can be refreshed without requiring the user to re authenticate but this does require the app to make the refresh call to Google.
  • The access token can be serialized so that new instances of the application don't need to re-authenticate.
  • The user can revoke permission for the app to access Google services on their behalf at anytime completely outside of the application environment.

My goal with this code was to first understand OAuth2 and Google's use of it, but also to create some code that hides the details of when and how authorization happens from the rest of application. Because the need to re-authenticate or refresh the access token can happen at any time I wanted to make it is a natural as possible for application code to use RESTful services without needing to know about those details.

All this example application does is allow the user to authenticate with Google, authorize the app to access their basic profile data and then display the profile data of the authenticated user. It does however demonstrate the basics of OAuth2 and hopefully present some examples of how to implement it within a WP7 app.

Background

First off, if you've never used a Google API it's good to have a brief understanding of how their services work and a basic understanding of their OAuth2 installed application implementation. If you're going to create an app that uses a Google service you need register it with their API Console. That's where you get the ClientId and secret which are referenced below as well as gain permission for your app to use specific Google services.

The attached code uses:

You can get all of those packages via NuGet. *as of this writing the latest version of RestSharp (102.6) doesn't work with the latest version of JSON.NET (4.0.7) so you may need to make sure to get JSON.NET 4.05)

Using the code

NOTE

In order to run the demo you'll need to register with Google code and get a client id and secret key which you can then enter in the AuthenticationViewModel constructor.
#warning PUT YOUR APP SPECIFIC STUFF HERE
            _process = new AuthenticationProcess()
            {
                ClientId = "YOUR APP CLIENT ID",
                Secret = "YOUR APP SECRET",

                // this specifies which Google APIs your app 
                // intends to use and needs permission for
                Scope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
            };

The OAuth2 Sequence

The basic OAuth2 sequence is a series of request/responses between the user, the app and Google:

nativeflow.png

As you can see the user doesn't actually interact with your application in order to login and authorize it. The user interacts directly with Google but still your app needs to know the result of the interaction in order to proceed.

In this example app, which is a MVVM app, the logic of tieing authentication to the app is with the AuthenticationViewModel class. The communication back and forth to Google is within the AuthenticationProcess class.

One thing you'll notice is that nowhere in the code (outside of the auth view model) will you see code kicking off the login process. The only thing any other view model has to make sure to do is asynchronously get the access code from the auth view model. As long as it does this it can assume that the apps is authenticated, or at least will be when its callback is invoked.

This solves a problem that I've had in other Rest client apps: controlling the entry point such that authentication happens correctly at the right time and making sure that client access is aware of the current authentication state.

OAuth2 In This Example

Since this is an MVVM Light app, it comes with a ViewModelLocator, a MainPage and a MainViewModel. The MainPage is what gets navigated to at application start and part of its UI binds to a property on the MainViewModel named Profile that holds the user's Google profile data. When the UI binds to that property the MainViewModel leads the profile data in the process retrieving the access token from the AuthenticationViewModel. In this example the auth view model is passed to the MainViewModel in its constructor and is the _authProvider member.

public Profile Profile
{
    get
    {
        if(_profile == null)
            _authProvider.GetAccessCode(s => LoadProfile(s));

        return _profile;
    }
    set
    {
        if (_profile != value)
        {
            _profile = value;
            RaisePropertyChanged("Profile");
        }
    }
}                

The call into AuthenticationViewModel::GetAccessCode is where the logic of authentication, authorization, refresh etc is encapsulated.

private Queue<Action<string>> _queuedRequests = new Queue<Action<string>>();
public void GetAccessCode(Action<string> callback)
{
    lock (_sync)
    {
        if (_isAuthenticating)
        {
            _queuedRequests.Enqueue(callback);
        }
        else if (HasAuthenticated)
        {
            if (!_process.AuthResult.IsExpired)
            {
                callback(_process.AuthResult.access_token);
            }
            else
            {
                _isAuthenticating = true;
                _queuedRequests.Enqueue(callback);
                _process.RefreshAccessToken();
            }
        }
        else
        {
            _isAuthenticating = true;
            _queuedRequests.Enqueue(callback);

            ((PhoneApplicationFrame)App.Current.RootVisual).Navigate(new Uri("/AuthenticationPage.xaml", UriKind.Relative));
            AuthUri = _process.AuthUri;
        }
    }
}

There's a number of things going on in that method:

  • First if an authentication is already in process, queue the call back and return
  • If the user is authenticated and the access token is valid, just invoke the callback
  • If the user is authenticated but the access token is expired, queue the callback and refresh the token
  • If the user hasn't authenticated successfully, queue the callback and navigate to the authentication page

Following the flow in the final block, which will be the case on the first launch of the app, the user will be navigated to the authentication page that display a webrowser pointed at the AuthUri property of the auth view model.

The AuthUri is the Google page where the user logs in and will look something like this https://accounts.google.com/o/oauth2/auth?response_type=code&redirect_uri=http://localhost&scope=https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile&client_id=YYYY

login.PNG

After authenticating with Google, they will be asked to authorize your app to access a defined set of APIs, as defined respectively by the client_id and scope arguments in the above Uri.

authorize.PNG

After the user presses "Allow access" Google will redirect the browser to the redirect_uri that we passed in the starting address. When the AutenticationPage codebehind sees the web browser going to the host specified in the redirect_uri argument of the original address, we know that Google is passing us back an access code which we can then exchange for an access token. The access code is part of the query string of the redirect address. We don't ever actually navigate to the redirect_uri but rather use it as sentinel value so we know when the auth is successful.

    private void webBrowser1_Navigating(object sender, NavigatingEventArgs e)
    {
        if (e.Uri.Host.Equals("localhost")) // in our case we used localhost as the redirect_uri
        {
            webBrowser1.Visibility = Visibility.Collapsed;
            e.Cancel = true;
            int pos = e.Uri.Query.IndexOf("=");

            // setting this ui element text will bind it back to the view model
            codeBlock.Text = pos > -1 ? e.Uri.Query.Substring(pos + 1) : null;
        }
    }

Once the code is sent back to the auth view model (in this case because we have a hidden textblock with two way binding) the view model exchanges it for an access token:

    private string _code;
    public string Code
    {
        get
        {
            return _code;
        }
        set
        {
            _code = value;
            _process.ExchangeCodeForToken(Code);
        }
    }
 
    ...

    class AuthenticationProcess
    {
        public void ExchangeCodeForToken(string code)
        {
            if (string.IsNullOrEmpty(code))
            {
                OnAuthenticationFailed();
            }
            else
            {
                var request = new RestRequest(this.TokenEndPoint, Method.POST);
                request.AddParameter("code", code);
                request.AddParameter("client_id", this.ClientId);
                request.AddParameter("client_secret", this.Secret);
                request.AddParameter("redirect_uri", "http://localhost");
                request.AddParameter("grant_type", "authorization_code");

                client.ExecuteAsync<AuthResult>(request, GetAccessToken);
            }
        }

        void GetAccessToken(IRestResponse<AuthResult> response)
        {
            if (response == null || response.StatusCode != HttpStatusCode.OK
                || response.Data == null || string.IsNullOrEmpty(response.Data.access_token))
            {
                OnAuthenticationFailed();
            }
            else
            {
                Debug.Assert(response.Data != null);
                AuthResult = response.Data;
                OnAuthenticated();
            }
        }

    }

At which point the AuthenticationProcess class signals to the auth view model that authentication is successful, the view model invokes any queued callbacks, tidies up a bit and the process is complete.

    void _process_Authenticated(object sender, EventArgs e)
    {
        _isAuthenticating = false;

        while (_queuedRequests.Count > 0)
            _queuedRequests.Dequeue()(_process.AuthResult.access_token);

        ViewModelLocator.SaveSetting("auth", _process.AuthResult);        

        RaisePropertyChanged("HasAuthenticated");
    }

And then the callback, all the way back on the MainViewModel, is invoked with a valid access token and we can move on with doing what we came here to do: invoke a Google API passing the access token to a RestSharp authenticator

    private void LoadProfile(string access_token)
    {
        Debug.WriteLine("loading profile");

        RestClient client = new RestClient("https://www.googleapis.com");
        client.Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator(access_token);
        var request = new RestRequest("/oauth2/v1/userinfo", Method.GET);
        client.ExecuteAsync<Profile>(request, ProfileLoaded);
    }
    private void ProfileLoaded(IRestResponse<Profile> response)
    {
        Profile = response.Data;
    }
profile.PNG

A similar sequence exists when the access token needs to be refreshed but without the need for a UI.

Conclusion

I can certainly see the advantage of OAuth2 from a security perspective. At no time does that app have the user's credentials. The entire exchange happens between the user and Google. No password is stored on the client and never does the app pass a password to the Google API.

It does make things more complicated than the simple, ask the user for credentials and include them in every call approach but hopefully this article provides an example of how to begin building around OAuth2.

History

  • 1/29/2012 - Initial upload

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Don Kackman
Team Leader Starkey Laboratories
United States United States
Member
The first computer program I ever wrote was in BASIC on a TRS-80 Model I and it looked something like:
10 PRINT "Don is cool"
20 GOTO 10
It only went downhill from there.
 
Hey look, I've got a blog

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5mvpKanasz Robert21 Sep '12 - 1:38 
This article is really helpful. A couple weeks ago I had a task to implement OAUTH2 into out application. Now I have a task to implement this into WP7 app. Smile | :) thank's for sharing.
QuestionMainPage - Cannot create an instance of "ViewModelLocator"membertony oldfield8 May '12 - 20:14 
Hi, Thanks for the great sample, makes OAuth2 authentication look easy - which I know its not after a sleepless night trying to get it up and running from scratch for a new project!
 
The sample works fine when running in the emulator and I now want to modify it further to fit my needs. However when opening the MainPage.xaml file in VS2010 I get an error in the console:
 
Cannot create an instance of ViewModelLocator (from app.xaml line 5).
 
and a popout in the designer view saying: Unhandled exception: Object reference not set to an instance of an object (from ViewModelLocator.cs line 19).
 
These errors seem to block any editing of the mainPage using the designer and generally have gotten me stuck! There are lots of posts about the errors on goggle but as a new comer to silverlight/Mvvm its all going a bit over my head. Only changes I have made to the project are adding my client id & secret.
 
Any help from the author or other users who have encountered these errors would be greatly appreciated.
 
Using Mvvm-Light V3.0.3.19 & RestSharp 102.7 (which now seem to come bundled with the newtonsoft.json dlls - so not using a seperate json download).
AnswerRe: MainPage - Cannot create an instance of "ViewModelLocator"memberDon Kackman10 May '12 - 7:02 
I didn't open the designer once when working on this project so that's just a big ol bug in the code.
 
Try this in the ViewModelLocator contructor.
 
 15          public ViewModelLocator()
 16          {
 17              Instance = this;
 18  
                 if (!ViewModelBase.IsInDesignModeStatic)
 19                    DispatcherHelper.Initialize();
 20              CreateAuth();
 21              CreateMain();
 22          }
 
If that doesn't work include more stuff in the block of code that doesn't get executed when in design mode.
10 PRINT "Software is hard. - D. Knuth"
20 GOTO 10

GeneralTimely articlememberMember 86976877 May '12 - 7:22 
Thanks for taking the effort and writing this article. It got me upto speed quickly.
BTW, if anyone gets "invalid_request" errors, try adding
 
request.AddParameter("grant_type", "authorization_code");

QuestionProblem with the code - help needed!membergong00182 Mar '12 - 21:15 
Hi Don I'm a starter of win phone app designing and I need the same function like you implemented in this program - signing in with google. I tried to use restsharp but no MVVM architecture in my app but im not sure why after i get the auth code and try to exchange it for the access token, the line of code:
 
client.ExecuteAsync(request, GetAccessToken);
 
it never redirects me to the method GetAccessToken(). I don't know whether I missed something. Can you give me some hints on how this is realized? and one more, how is the JSON Array response be parsed to type AuthResult?
 
Thank you soooo much!
AnswerRe: Problem with the code - help needed!memberDon Kackman3 Mar '12 - 4:43 
gong0018 wrote:
and one more, how is the JSON Array response be parsed to type AuthResult?

 
This chunk of code:
client.ExecuteAsync<AuthResult>(request, GetAccessToken);
tells ReshSharp to parse the JSON response into an AuthResult instance and send it to the callback. RestSharp does this internally using JSON.NET so the app code is not doing an JSON parsing directly.
 
The AuthResult instance is returned to the app by restsharp as the Data property on the response object passed to the callback:
        void GetAccessToken(IRestResponse<AuthResult> response)
        {
            if (response == null || response.StatusCode != HttpStatusCode.OK
                || response.Data == null || string.IsNullOrEmpty(response.Data.access_token))
            {
                OnAuthenticationFailed();
            }
            else
            {
                Debug.Assert(response.Data != null);
                AuthResult = response.Data;
                OnAuthenticated();
            }
        }
 
If you don't use the generic version of ExecuteAsyn it will just pass a response with the JSON text. Supplying a generic type allows you to not worry so much about the parsing.
 
As for why your callback isn't being invoked that is a hard one to answer. It should invoke that method ieven if you request isn't correct with approariate http failure codes and messages etc. Are you certain the callback isn't being invoked (you've tried breakpoints, Debug.Writelines, MessageBoxes)?
10 PRINT "Software is hard. - D. Knuth"
20 GOTO 10

GeneralRe: Problem with the code - help needed!membergong00183 Mar '12 - 17:55 
Hi thank you for the prompt response! I think I know where my code was wrong now, it's kind of a stupid mistake - I must make the program to wait for the callback being invoked. In my case, the callback didn't have a chance to do so. If I make the
ExchangeCodeForToken(Code)
as the last line of the method, then everything goes correctly. This is not a smart way to solve the prob but the fastest I can come out.
 
For the parsing, the response from google should be something like
{
  "access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
  "expires_in":3920,
  "token_type":"Bearer",
  "refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
Does this means as long as I have the four variables
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
public string refresh_token { get; set; }
in class AuthResult, RestSharp will auto match and assign the value to the variable for me?
GeneralRe: Problem with the code - help needed!memberDon Kackman4 Mar '12 - 8:18 
Yup. Prwttymuch. Rest sharp is pretty smart about trying to deserialize Jason into properties. If you you poke around other documentation you can find a good explanation of how they try to match Jason fields to properties.
10 PRINT "Software is hard. - D. Knuth"
20 GOTO 10

GeneralRe: Problem with the code - help needed!membergong00185 Mar '12 - 21:17 
Ok I see that. Thank you so much for the explaining! Big Grin | :-D
QuestionNicemvpSacha Barber7 Feb '12 - 4:44 
I lost much black hair (which was replaced with grey hair and I am only 20 years old (er ok I'm not)) getting OpenId (related field() to work, so know this was painful. Good stuff Don, useful. 5
Sacha Barber
  • Microsoft Visual C# MVP 2008-2012
  • Codeproject MVP 2008-2011
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

QuestionSpring.NET SocialmemberMaRuXeLo7930 Jan '12 - 12:12 
You should take a look to Spring.NET Social to write your own Client:
http://www.springframework.net/social/[^]
 
Check the OAuth2 examples from the 'examples' directory of the distribution.
QuestionRunning the app [modified]memberDon Kackman30 Jan '12 - 4:07 
Just a note if you want to run the app (having an issue updating the article text at the moment)
 
In order to run the demo you'll need to register with Google code and get a client id and secret key which you can then enter in the AuthenticationViewModel constructor.
#warning PUT YOUR APP SPECIFIC STUFF HERE
            _process = new AuthenticationProcess()
            {
                ClientId = "YOUR APP CLIENT ID"
                Secret = "YOUR APP SECRET",
 
                // this specifies which Google APIs your app
                // intends to use and needs permission for
                Scope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
            };
10 PRINT "Software is hard. - D. Knuth"
20 GOTO 10


modified 30 Jan '12 - 10:22.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 30 Jan 2012
Article Copyright 2012 by Don Kackman
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid