Implementing OAuth2.0 Authorization For Google In ASP.NET

Introduction

Nowadays, security is a major concern for every service provider like Google, Yahoo, Microsoft etc. and that’s why each service provider who is providing some external service to another app is following the protocol defined by OAuth.

I am going to describe here how to implement Google OAuth in an ASP.NET app.

Prerequisites

You should have a basic knowledge of,

  • C#
  • ASP.NET

Steps for implementing

For implementing OAuth, we will have to follow this series of steps.

  • Create a project on Google Console.
  • Get the client id and client secret from the project.
  • Writing the code for authorizing the user by Google.
  • Writing code for exchanging the authorization code for refresh token.
  • Writing code for exchanging refresh token for access token.

Don’t worry about the above steps. I am going to explain these in detail, along with a proper demo.

Let’s Start.

  1. Create a project on Google Console

    Go to the website of Google Console and click on Project -> New Project.

    Google

    After that, a dialog box will appear. Write your project name and click on Create.

    Google

    You have successfully created a project.

  2. Getting client id and client secret

    Follow the steps shown in the screenshot.

    1. Click on Credentials

      Google

    2. Click on down arrow button near the Create Credentials button. Clicking the down arrow button will open a dropdown box . There, click on OAuth Client ID.

      Google

    3. Click on "Configure Consent" screen.

      Google

    4. Fill all the details and click on Save.

      Google

    5. Again, you will be redirected to the credentials screen. Click on Web Application and name your web app. Then, click on "Create" button. For the time being, ignore the other text boxes which I will explain later in this article.

      Google

    6. You will get your client id and client secret in a pop up. Copy the client id and client secret and keep it somewhere safe (Don't worry if you lose this code, you can get this code later from developer console,But if someone will get these data they can misuse this).

  3. Writing code for authorizing the user by Google

    1. Let’s create an ASP.NET project.

      Google

      I am creating a web form project, but you can create any project, like ASP.NET MVC or any other.

    2. Add a web form inside the project with name – GoogleCallBack. This will be used as redirect URL. Don’t worry ! You will understand it later.

      Google

      Google

    3. Add a web form inside the project with name - "Authorize" and paste the following HTML code inside the form.
      1. <form id="form1" runat="server" method="get">  
      2.     <div> Enter User Id: <input type="text" name="UserId"> </div>  
      3.     <p> <button type="submit">Submit</button> </p>  
      4. </form>  
      I am creating a page where I will send the user id to the Server. The Server will determine whether the user is authorized or not and if not authorized, it will authorize the user.

    4. Put the client id and client secret in web.config under “appSettings”, so that you can change this value later and it can be globally available for any page.

      Google

    5. Remember, we have left some setup of project on Google Developer Console. It's time to complete them, otherwise Google will decline the authorization. So, perform the following steps in serial order.
      1. Run the project.

        Google

      2. Open the Google developer console.
      3. Go to Credentials.
      4. Click on "Edit OAuth client".

        Google

      5. Now, copy the project URL and paste in textbox of authorized JavaScript origin. Keep it open.
      6. Open the googlecallback.aspx in browser and copy the URL.
      7. Paste the url in authorized redirect uri.

        Google

        Now, we have completed the settings of the project on Google developer console. It's time to get back to our implementation part.
    6. Create a database with any name and inside the database - create the table having the below design or you can also paste the below code in order to create the table.
      1. create table Member(UserId int primary key, RefreshToken varchar(200), GmailId varchar(200))  
      Google

    7. Add connection string in web.config .Sorry, I am not going to describe this.I assume you know how to do this one.
    8. It is time to write the code for authorizing user.

      For Authorizing the user by Google, you will have to redirect the user to Google Authorizing URL with the following query string

      1. client_id
        You will have to pass the client id that you got from Google console by creating a project.

      2. redirect_uri
        You will have to pass an absolute url. Google will redirect the user to this url after authorization with some value as ‘code’ or ‘error’ if any error occurred.

      3. access_type
        This will be used by Google to know what type of access you want. There are two values – ‘online’ or ‘offline’. Default value is ‘online’, but we set it as ‘offline’ because we want the user to authenticate one time and use the service again and again.

      4. state
        You can set any value.The same value will be returned by Google after authorization. This is used to know the user details or any other things. You will understand this later.

      5. login_hint
        This value will be automatically filled by Google in email text box.

      6. scope
        This value will be used by Google to know what type of service an app is requesting for the user. e.g – Gmail, Calendar, Google or user info etc. You can pass multiple values separated by space.

    So, let's write the code for authorizing user. 

    Copy the below code in authorize.aspx.cs under partial class -

    1. protected void Page_Load(object sender, EventArgs e) {  
    2.         //Get the user id from query string  
    3.         string UserId = Request.QueryString["UserId"];  
    4.         if (UserId != null//check whether user is null or not  
    5.         {  
    6.             if (IsAuthorized(UserId)) //check whether user is already authorized  
    7.             {  
    8.                 string EmailId = GetGmailId(UserId); //Get the email id from database  
    9.                 //show the email id in alert  
    10.                 ClientScript.RegisterClientScriptBlock(this.GetType(), "alert""alert('" + EmailId + "')"true);  
    11.             } else {  
    12.                 AuthorizeUser(UserId); //authorize the user  
    13.             }  
    14.         }  
    15.     }  
    16.     /// <summary>    
    17.     /// Return gmail id from database. it will saved in the database after successful authentication.    
    18.     /// </summary>    
    19.     /// <param name="userId"></param>    
    20.     /// <returns></returns>    
    21. private string GetGmailId(string userId) {  
    22.     SqlConnection Con = new SqlConnection(ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString);  
    23.     string Query = "select GmailId from Member where UserId=" + userId;  
    24.     SqlCommand Cmd = new SqlCommand(Query, Con);  
    25.     Con.Open();  
    26.     string Result = Cmd.ExecuteScalar().ToString();  
    27.     Con.Close();  
    28.     return Result;  
    29. }  
    30. private bool IsAuthorized(string userId) {  
    31.     SqlConnection Con = new SqlConnection(ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString);  
    32.     string Query = "select count(*) from Member where UserId=" + userId;  
    33.     SqlCommand Cmd = new SqlCommand(Query, Con);  
    34.     Con.Open();  
    35.     int Result = (int) Cmd.ExecuteScalar();  
    36.     Con.Close();  
    37.     return Result > 0 ? true : false;  
    38. }  
    39. private void AuthorizeUser(string data) {  
    40.         string Url = GetAuthorizationUrl(data);  
    41.         HttpContext.Current.Response.Redirect(Url, false);  
    42.     }  
    43.     /// <summary>    
    44.     ///     
    45.     /// </summary>    
    46.     /// <param name="data"></param>    
    47.     /// <returns></returns>    
    48. private string GetAuthorizationUrl(string data) {  
    49.     string ClientId = ConfigurationManager.AppSettings["ClientId"];  
    50.     string Scopes = "https://www.googleapis.com/auth/userinfo.email";  
    51.     //get this value by opening your web app in browser.    
    52.     string RedirectUrl = "http://localhost:52403/GoogleCallBack.aspx";  
    53.     string Url = "https://accounts.google.com/o/oauth2/auth?";  
    54.     StringBuilder UrlBuilder = new StringBuilder(Url);  
    55.     UrlBuilder.Append("client_id=" + ClientId);  
    56.     UrlBuilder.Append("&redirect_uri=" + RedirectUrl);  
    57.     UrlBuilder.Append("&response_type=" + "code");  
    58.     UrlBuilder.Append("&scope=" + Scopes);  
    59.     UrlBuilder.Append("&access_type=" + "offline");  
    60.     UrlBuilder.Append("&state=" + data); //setting the user id in state  
    61.     return UrlBuilder.ToString();  
    62. }  
    So, what is the above code doing?
     
    • When the page loads, it will retrieve the user id from query string.
    • If we have found the user id from query string, it will check whether user is already authorized or not.
    • If the user is already authorized, then we will fetch the email from database and show it to the user.
    • If the user is not authorized, we will authorize the user by following the below steps. 

      • We will create an authorization url - this includes creating query string with client id, scope, and all other things that I mentioned earlier.
      • We will redirect the user to the created url.

    Now, we have web page which will authorize the user. Let’s create the page for Saving the data in database after successful authorization. So we will have to write code in GoogleCallBack.aspx.cs

  4. Writing code for exchanging the authorization code

    After successful authorization, Google will call this page with an authorization code which is temporary – that means it will expire after few hour. probably an hour. But we want the user to authenticate one time and use the service again and again.So what to do?

    By using authorization code, we can get a refresh token which is valid for a   lifetime but it is not used for getting any service. So again the question will be, then what is the meaning of refresh token?

    By using refresh token, we can get access token which is used for getting service and again access token is temporary. So every time you want to use any service, you will have to get access token.

    I think now you understand the whole flow.So basically we will have to perform the following steps.

    1. Get the authorization code.
    2. Exchange the authorization code for refresh token.
    3. save the refresh token in database.
    4. Exchange refresh token for access token.
    5. Use access token for getting any service.

    So, let’s write the code.

    Copy the below code in GoogleCallBack.aspx.cs under namespace.

    1. public partial class GoogleCallBack: System.Web.UI.Page {  
    2.     protected void Page_Load(object sender, EventArgs e) {  
    3.         //you will get this, when any error will occur while authorization otherwise null    
    4.         string Error = Request.QueryString["error"];  
    5.         //authorization code after successful authorization    
    6.         string Code = Request.QueryString["code"];  
    7.         if (Error != null) {} else if (Code != null) {  
    8.             //Remember, we have set userid in State    
    9.             string UserId = Request.QueryString["state"];  
    10.             //Get AccessToken    
    11.             int Id = Convert.ToInt32(UserId);  
    12.             string AccessToken = string.Empty;  
    13.             string RefreshToken = ExchangeAuthorizationCode(Id, Code, out AccessToken);  
    14.             //saving refresh token in database    
    15.             SaveRefreshToken(Id, RefreshToken);  
    16.             //Get Email Id of the authorized user    
    17.             string EmailId = FetchEmailId(AccessToken);  
    18.             //Saving Email Id    
    19.             SaveEmailId(UserId, EmailId);  
    20.             //Redirect the user to Authorize.aspx with user id    
    21.             string Url = "Authorize.aspx?UserId=" + UserId;  
    22.             Response.Redirect(Url, true);  
    23.         }  
    24.     }  
    25.     private string ExchangeAuthorizationCode(int userId, string code, out string accessToken) {  
    26.         accessToken = string.Empty;  
    27.         string ClientSecret = ConfigurationManager.AppSettings["ClientSecret"];  
    28.         string ClientId = ConfigurationManager.AppSettings["ClientId"];  
    29.         //get this value by opening your web app in browser.    
    30.         string RedirectUrl = "http://localhost:52403/GoogleCallBack.aspx";  
    31.         var Content = "code=" + code + "&client_id=" + ClientId + "&client_secret=" + ClientSecret + "&redirect_uri=" + RedirectUrl + "&grant_type=authorization_code";  
    32.         var request = WebRequest.Create("https://accounts.google.com/o/oauth2/token");  
    33.         request.Method = "POST";  
    34.         byte[] byteArray = Encoding.UTF8.GetBytes(Content);  
    35.         request.ContentType = "application/x-www-form-urlencoded";  
    36.         request.ContentLength = byteArray.Length;  
    37.         using(Stream dataStream = request.GetRequestStream()) {  
    38.             dataStream.Write(byteArray, 0, byteArray.Length);  
    39.             dataStream.Close();  
    40.         }  
    41.         var Response = (HttpWebResponse) request.GetResponse();  
    42.         Stream responseDataStream = Response.GetResponseStream();  
    43.         StreamReader reader = new StreamReader(responseDataStream);  
    44.         string ResponseData = reader.ReadToEnd();  
    45.         reader.Close();  
    46.         responseDataStream.Close();  
    47.         Response.Close();  
    48.         if (Response.StatusCode == HttpStatusCode.OK) {  
    49.             var ReturnedToken = JsonConvert.DeserializeObject < Token > (ResponseData);  
    50.             if (ReturnedToken.refresh_token != null) {  
    51.                 accessToken = ReturnedToken.access_token;  
    52.                 return ReturnedToken.refresh_token;  
    53.             } else {  
    54.                 return null;  
    55.             }  
    56.         } else {  
    57.             return string.Empty;  
    58.         }  
    59.     }  
    60.     private void SaveRefreshToken(int userId, string refreshToken) {  
    61.         SqlConnection Con = new SqlConnection(ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString);  
    62.         string Query = "insert into Member (UserId,RefreshToken) values(" + userId + ",'" + refreshToken + "')";  
    63.         SqlCommand Cmd = new SqlCommand(Query, Con);  
    64.         Con.Open();  
    65.         int Result = Cmd.ExecuteNonQuery();  
    66.         Con.Close();  
    67.     }  
    68.     private string FetchEmailId(string accessToken) {  
    69.         var EmailRequest = "https://www.googleapis.com/userinfo/email?alt=json&access_token=" + accessToken;  
    70.         // Create a request for the URL.    
    71.         var Request = WebRequest.Create(EmailRequest);  
    72.         // Get the response.    
    73.         var Response = (HttpWebResponse) Request.GetResponse();  
    74.         // Get the stream containing content returned by the server.    
    75.         var DataStream = Response.GetResponseStream();  
    76.         // Open the stream using a StreamReader for easy access.    
    77.         var Reader = new StreamReader(DataStream);  
    78.         // Read the content.    
    79.         var JsonString = Reader.ReadToEnd();  
    80.         // Cleanup the streams and the response.    
    81.         Reader.Close();  
    82.         DataStream.Close();  
    83.         Response.Close();  
    84.         dynamic json = JValue.Parse(JsonString);  
    85.         return json.data.email;  
    86.     }  
    87.     private bool SaveEmailId(string userId, string emailId) {  
    88.         SqlConnection Con = new SqlConnection(ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString);  
    89.         string Query = "update Member set GmailId='" + emailId + "'where UserId='" + userId + "'";  
    90.         SqlCommand Cmd = new SqlCommand(Query, Con);  
    91.         Con.Open();  
    92.         int Result = Cmd.ExecuteNonQuery();  
    93.         Con.Close();  
    94.         return Result > 0 ? true : false;  
    95.     }  
    96. }  
    97. public class Token {  
    98.     public string access_token {  
    99.         get;  
    100.         set;  
    101.     }  
    102.     public string token_type {  
    103.         get;  
    104.         set;  
    105.     }  
    106.     public string expires_in {  
    107.         get;  
    108.         set;  
    109.     }  
    110.     public string refresh_token {  
    111.         get;  
    112.         set;  
    113.     }  
    114. }  
    What is the above code doing ?
     
    • We have two things that will be returned by google either error or code, so we will check first if error is not null. If error is null, that means some error has occurred while authorizing.
    • If error is null then we will check for code and if code is not null that means we have a token and user is authorized successfully.
    • Now, we have got the temporary token, so we will have to use it to get the refresh token. We will call the exchange "authorizationcode" which will exchange the authorization code and will return refreshtoken and access token.(At the time of exchanging authorization code, the google also return access token, so we should use this access token instead of another http call to get the access token.)
    • Save the refresh token in database.
    • Get the email id using access token.
    • Save the email id in database.
    • Redirect the user to authorize.aspx with query string user id and now the user is authorized so we will get an alert with the user gmail id. Now, we have completed the coding part successfully.It’s time to rock.

    Testing Implementation

    Perform the following steps.

    • Run the project.
    • Navigate to Authorize.aspx.
    • Enter any user id which should be a number .

      Google

    • Click on Submit If the user will be present inside the db then you will get the email id of the user in alert box, otherwise the user will be redirected to google authorization site.
    • An allow screen will appear, where user can see what type of access, he/she is giving to the app. Click on Allow button. After clicking on Allow button, the refresh token and Gmail id will be saved to your DB and the user will be redirected to the authorize page.

      Google

    Cool, Isn’t It?

    Now, we have refresh token so the next question will be how to use the refresh token to get the access token?

  5. Writing code for exchanging refresh token

    Let’s create a web form with name GetEmail. It will be the same like Authorize.aspx but instead of getting the email from database, we will fetch the email from Google using access token. So, copy the html code from Authorize.aspx.

    Now, inside the GetEmail.aspx.cs copy the below code under partial class.
    1. protected void Page_Load(object sender, EventArgs e) {  
    2.     string UserId = Request.QueryString["UserId"];  
    3.     if (UserId != null) {  
    4.         string RefreshToken = string.Empty;  
    5.         if (IsAuthorized(UserId, out RefreshToken)) {  
    6.             //Get Access token using Refresh token    
    7.             string AccessToken = GetAccessToken(RefreshToken);  
    8.             //Get Gmail Id  using access token    
    9.             string EmailId = GetGmailId(AccessToken);  
    10.             ClientScript.RegisterClientScriptBlock(this.GetType(), "alert""alert('" + EmailId + "')"true);  
    11.         } else {  
    12.             string ErrorMsg = "You are not authorized";  
    13.             ClientScript.RegisterClientScriptBlock(this.GetType(), "alert""alert('" + ErrorMsg + "')"true);  
    14.         }  
    15.     }  
    16. }  
    17. private bool IsAuthorized(string userId, out string refreshToken) {  
    18.     SqlConnection Con = new SqlConnection(ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString);  
    19.     string Query = "select RefreshToken from Member where UserId=" + userId;  
    20.     SqlCommand Cmd = new SqlCommand(Query, Con);  
    21.     Con.Open();  
    22.     var Result = Cmd.ExecuteScalar();  
    23.     Con.Close();  
    24.     refreshToken = Result != null ? Result.ToString() : string.Empty;  
    25.     return refreshToken.Length > 0 ? true : false;  
    26. }  
    27. private string GetGmailId(string accessToken) {  
    28.     if (accessToken.Length > 0) {  
    29.         string GmailId = FetchUsersEmail(accessToken);  
    30.         return GmailId;  
    31.     } else {  
    32.         return string.Empty;  
    33.     }  
    34. }  
    35. private string GetAccessToken(string refreshToken) {  
    36.     string ClientSecret = ConfigurationManager.AppSettings["ClientSecret"];  
    37.     string ClientId = ConfigurationManager.AppSettings["ClientId"];  
    38.     var Content = "refresh_token=" + refreshToken + "&client_id=" + ClientId + "&client_secret=" + ClientSecret + "&grant_type=refresh_token";  
    39.     WebRequest request = WebRequest.Create("https://accounts.google.com/o/oauth2/token");  
    40.     request.Method = "POST";  
    41.     byte[] byteArray = Encoding.UTF8.GetBytes(Content);  
    42.     request.ContentType = "application/x-www-form-urlencoded";  
    43.     request.ContentLength = byteArray.Length;  
    44.     using(Stream dataStream = request.GetRequestStream()) {  
    45.         dataStream.Write(byteArray, 0, byteArray.Length);  
    46.         dataStream.Close();  
    47.     }  
    48.     var Response = (HttpWebResponse) request.GetResponse();  
    49.     Stream responseDataStream = Response.GetResponseStream();  
    50.     StreamReader reader = new StreamReader(responseDataStream);  
    51.     string ResponseData = reader.ReadToEnd();  
    52.     reader.Close();  
    53.     responseDataStream.Close();  
    54.     Response.Close();  
    55.     if (Response.StatusCode == HttpStatusCode.OK) {  
    56.         var ReturnedToken = JsonConvert.DeserializeObject < Token > (ResponseData);  
    57.         return ReturnedToken.access_token;  
    58.     } else {  
    59.         return string.Empty;  
    60.     }  
    61. }  
    62. private string FetchUsersEmail(string accessToken) {  
    63.     var EmailRequest = @ "https://www.googleapis.com/userinfo/email?alt=json&access_token=" + accessToken;  
    64.     // Create a request for the URL.    
    65.     var Request = WebRequest.Create(EmailRequest);  
    66.     // Get the response.    
    67.     var Response = (HttpWebResponse) Request.GetResponse();  
    68.     // Get the stream containing content returned by the server.    
    69.     var DataStream = Response.GetResponseStream();  
    70.     // Open the stream using a StreamReader for easy access.    
    71.     var Reader = new StreamReader(DataStream);  
    72.     // Read the content.    
    73.     var JsonString = Reader.ReadToEnd();  
    74.     // Cleanup the streams and the response.    
    75.     Reader.Close();  
    76.     DataStream.Close();  
    77.     Response.Close();  
    78.     dynamic json = JValue.Parse(JsonString);  
    79.     return json.data.email;  
    80. }  
    What the above code is doing

    • We will first check whether user is authorized or not. If authorized    the "IsAuthorized" function will return RefreshToken as out parameter.
    • We will use that refresh token to get the access token.
    • After that by using access token, we will get the email id.

    Now, run the project and navigate to GetEmail.aspx. Enter the user id and see the page in action.

    Google

    Finally, We have successfully implemented the OAth2.0 authorization for Google.

    Interesting Points

    Have you noticed, we are getting only email id. So, what is the thing which is saying to Google that the system wants only email id - the scope is the thing that we send at the time of authorization to Google to let Google know that the requesting system wants this type of service. You can also send multiple scopes to get the multiple service e.g - Google contacts, Google calendar etc.

    Want to know how we can get the Google Calendar, Google Contacts or how we can upload the file on GoogleDrive?  Let's wait for my next article.

References

  • https://developers.google.com/identity/protocols/OAuth2WebServer