Obtaining access token from Google+ API in non SPA web application

How to get access token in C# backend from Google+ API

  • Share

Recently I was trying to use Google+ OAuth api for authenticating with Google+ and fetch some user basic info such as name and email.

Note

Visual Studio 2013 comes with a templates for social network authentication which can be used out of the box. This article is useful for those who are not using latest technologies or you have to introduce social netowork login to an existing application.

I found very useful article how to do this, but the sample was doing it with JavaScript. Since I had to be able to do the same thing even if location is accessed from mobile I could not go with JavaScript and pop-up opening approach.

The easiest thing to do was to go to Google+ authentication page, login, and get redirected to my callback page. I used URL and parameters from this http://www.c-sharpcorner.com/UploadFile/vdtrip/google-plus-authentication-in-Asp-Net-and-C-Sharp/ page and everything went smooth until I actually had to read token from the URL value on my callback page.

string authUrl = string.Format("https://accounts.google.com/o/oauth2/auth?client_id={0}&redirect_uri={1}&response_type=token&scope={2}",
            "1234152436123-ashjdajshdguyfnalaejfwoeh.apps.googleusercontent.com", //Client ID
            "https://mydomain.com/googleplus/oauth2callback", //Your callback page URL
            "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email" //Scope you want to access with token
            );
            Response.Redirect(authUrl);
    
Note

How to create an app for Google+ API using please check Google+ API documentation or access Google API Console https://code.google.com/apis/console

The problem begun when I got URL like this

https://mydomain.com/googleplus/oauth2callback#access_token=ya29.gwATuaf3M4IpReolpHucJwJfqTNajh16FrOfi6PAL7dnsfussnahnR-s&token_type=Bearer&expires_in=3600

In case you did not spot it, querystring does not start with question mark character, but with hash.

The problem is that hash is not reckognized as data and everything after hash is not passed to a backend. In short, you cannot read values from url after hash in your backend C# code. Google did this intensionally to make it easier for AngularJS SPA web applications.

Unfortunately not all web applications are SPA. For those which are still using full page load there is a catch to acquire a token. It is well described in Google documentation at https://developers.google.com/accounts/docs/OAuth2Login#exchangecode

Apparently, for regular pages you need to acquire code first and based on that code you make another request to acquire token with which you will invoke API methods to get Google+ user data. in our initial request we made in code snippet above we need to change response_type parameter to code

string authUrl = string.Format("https://accounts.google.com/o/oauth2/auth?client_id={0}&redirect_uri={1}&response_type=code&scope={2}",
            "1234152436123-ashjdajshdguyfnalaejfwoeh.apps.googleusercontent.com", //Client ID
            "https://mydomain.com/googleplus/oauth2callback", //Your callback page URL
            "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email" //Scope you want to access with token
            );
            Response.Redirect(authUrl);
    

This will redirect you to Google+ to authenticate, and then back to your callback page but with different parameters in url

https://mydomain.com/googleplus/oauth2callback?code=Wjhgf7SAfddfg.KhuysU98N8zdkI3hj

This value (code) can be fetched from query string but, unfortunately this is not access token and we cannot use it for getting Google+ user data. We have to create another web request with this code in order to get token.

The catch is that this new request needs to be post and we need to store data we are going to send in a request inside request body. for this purpose we will have to write our data to RequestStream before we try to get a Response

 var url = string.Format("code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type=authorization_code",
                Request["code"], //Code returned from previous request
                "1234152436123-ashjdajshdguyfnalaejfwoeh.apps.googleusercontent.com", //Client ID
                "Fsk8Nj0HdS89", //Client secret obtained from google API console
                "https://mydomain.com/googleplus/oauth2callback" //Your callback page
                );
            var req = WebRequest.Create("https://accounts.google.com/o/oauth2/token");
            req.Method = "POST";
            req.ContentType = "application/x-www-form-urlencoded";
            ASCIIEncoding encoder = new ASCIIEncoding();
            byte[] data = encoder.GetBytes(url);
            req.ContentLength = data.Length;
          req.GetRequestStream().Write(data, 0, data.Length);
    

All you have to do now is to read JSON result with stream reader. Stream reader will read values to string

new StreamReader(req.GetResponse().GetResponseStream()).ReadToEnd()
    

You certainly do not want to parse manually, instead it is easier to deserialize it to a simple POCO class. I used json2csharp.com to generate POCO class out of JSON response I got from Google and called it TokenResponse.

public class TokenResponse
    {
        public string access_token { get; set; }
        public string token_type { get; set; }
        public int expires_in { get; set; }
        public string id_token { get; set; }
    }
    

Now, with just two lines we can read the response, deserialize and get access_token string property

TokenResponse tokenResponse = new JavaScriptSerializer().Deserialize(new StreamReader(req.GetResponse().GetResponseStream()).ReadToEnd(), typeof(TokenResponse)) as TokenResponse;
            var accessToken = tokenResponse.access_token;
    

We can now use this token to invoke Google+ API to obtain used data and use it in our application.

References

  • Share

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.

Comments for this article

comments powered by Disqus