PREREQUISITE
- Knowledge of Basic .NET and object oriented programming
- Basic Knowledge of API'S
- Basic knowledge of database
- working knowledge of Visual studio
Download Solution from here
OAuth Overview
Google providers you huge list of API'S to access the google resources . This resources can be accesses only after passing the authentication layer successfully.Google APIs use the OAuth 2.0 protocol for authentication and authorization. Google supports common OAuth 2.0 scenarios such as those for web server, installed, and client-side applications.
What is OAuth Protocol?
OAuth is an open-source authorization protocol - or set of rules for authorization that allows a third-party application to access a user’s data without the user needing to share login credentials. It purely works on token-based authorization mechanism.
Roles
OAuth defines four roles:Resource Owner
An entity capable of granting access to a protected resource. When
the resource owner is a person, it is referred to as an end-user.
Resource Server The server hosting the protected
resources, capable of accepting and responding to protected resource
requests using access tokens.
Client
An application making protected resource requests on behalf of the resource owner and with its authorization. The term "client" does not imply any particular implementation characteristics (e.g., whether the application executes on a server, a desktop, or other devices).
Authorization Server
The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization. The interaction between the authorization server and resource server is beyond the scope of this specification. The authorization server may be the same server as the resource server or a separate entity. A single authorization server may issue access tokens accepted by multiple resource servers.
In this article , I am going to explain and implement Google
Calendar Integration with .Net Application(which again can be either windows desktop,console,phone or web application/website
site) which will perform some operations like
retrieving,adding,deleting and updating events in google calendar.Let we assume
and start with console application and then later with web application
Let us quickly go through steps to setup Project in visual studio
SETUP VISUAL STUDIO PROJECT
1) open Visual studio and make sure you have at least 4.0 .Net framework version selected like shown below |
2) Add the following NuGet Package using Google.Apis.Auth.OAuth2; using Google.Apis.Calendar.v3; using Google.Apis.Calendar.v3.Data; using Google.Apis.Services; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; you can do it in two ways. a)USING NGET CONSOLE: just fallow below steps PM> Install-Package Google.Apis.Calendar.v3b)USING VISUAL STUDIO NGET PACKAGE UI: Fallow below steps- |
Congrats , you are done with package installation and visual studio setup.Now you are ready to code but before you start coding you need to setup Google Calendar Service Setup |
SET UP PROJECT IN GOOGLE CONSOLE
To begin or make our application talk to Google Calendar we have to obtain OAuth 2.0 client credentials from the Google Developers Console. Let us fallow below steps1) Go to Google Developers Console . If you don't have account just create new google account. |
2) In Google console fallow below steps |
Great, we have created Google console project successfully and got Client secret key also |
LET US CODE
using Google.Apis.Auth.OAuth2; using Google.Apis.Calendar.v3; using Google.Apis.Calendar.v3.Data; using Google.Apis.Services; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; namespace DotNetGoogleCalendarIntegration { class Program { //I just include calendar scope here because i want to access //user calendar for event retrieval and insertion static string[] Scopes = { CalendarService.Scope.Calendar }; static string Applicationname = "Faith Application"; static void Main(string[] args) { UserCredential credential; CalendarService service; //JSON File created in Google console and added to application folder using (var stream = new FileStream("..\\..\\googleclient_secret.json", FileMode.Open, FileAccess.Read)) { credential = GoogleWebAuthorizationBroker.AuthorizeAsync( GoogleClientSecrets.Load(stream).Secrets, Scopes, Environment.UserName, CancellationToken.None, new DatabaseStore(@"Database Server Name", "database username", "password", "databasename", "tablename")).Result;
//oauth sucks need to get refresh token and save it for persistence and to avoid popup dialog every time for ACCEPT and DENY // Console.WriteLine(credential); } // Create Calendar Service. service = new CalendarService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = Applicationname , }); // Define parameters of request. EventsResource.ListRequest request = service.Events.List("primary"); request.TimeMin = DateTime.Now; request.ShowDeleted = false; request.SingleEvents = true; request.MaxResults = 10; request.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime; Console.WriteLine("Upcoming events for Shabir:"); Console.WriteLine("===================================================="); Events events = request.Execute(); if (events.Items.Count > 0) { foreach (var eventItem in events.Items) { string when = eventItem.Start.DateTime.ToString(); string title = eventItem.Description.ToString(); string organizer = eventItem.Organizer.Email.ToString(); if (String.IsNullOrEmpty(when)) { when = eventItem.Start.Date; } Console.WriteLine("{0}" + Environment.NewLine + "{1}" + Environment.NewLine + " {2}" + Environment.NewLine + " {3}", eventItem.Summary, when, title, organizer); Console.WriteLine("------------------------------------------------------"); } } else { Console.WriteLine("No upcoming events found."); } //ADDING EVENT Event newEvent = new Event(); newEvent.Summary = "Shabir Meeting"; newEvent.Description = "Today going to discuss regarding OAuth Protocol"; newEvent.Start = new EventDateTime(); newEvent.Start.DateTime = DateTime.Now.AddHours(5); newEvent.End = new EventDateTime(); newEvent.End.DateTime = DateTime.Now.AddHours(8); service.Events.Insert(newEvent, "primary").ExecuteAsync(); Console.Read(); } } }Note: In Case you are looking for saving in local file, i mean implementing IFileDataDtore change above function code snippet with
credential = GoogleWebAuthorizationBroker.AuthorizeAsync( GoogleClientSecrets.Load(stream).Secrets, Scopes, Environment.UserName, CancellationToken.None, CancellationToken.None, new FileDataStore(@"D:\CalendarCsharp")).Result;
DatabaseStore.cs
RECAP ALL STEPS
All applications follow a basic pattern when accessing a Google API using OAuth 2.0. At a high level, you follow four steps:1. Obtain OAuth 2.0 credentials from the Google Developers Console.
Visit the Google Developers Console to obtain OAuth 2.0 credentials such as a client ID and client secret that are known to both Google and your application. The set of values varies based on what type of application you are building. For example, a JavaScript application does not require a secret, but a web server application does.2. Obtain an access token from the Google Authorization Server.
Before your application can access private data using a Google API, it must obtain an access token that grants access to that API. A single access token can grant varying degrees of access to multiple APIs. A variable parameter calledscope
controls the set of resources and operations that an access token
permits. During the access-token request, your application sends one
or more values in the scope
parameter. 3. Send the access token to an API.
After an application obtains an access token, it sends the token to a Google API in an HTTP authorization header. Access tokens are valid only for the set of operations and resources described in thescope
of the token request.
For example, if an access token is issued for the Google+ API, it
does not grant access to the Google Calendar API. You can, however,
send that access token to the Google+ API multiple times for similar
operations. 4. Refresh the access token, if necessary.
Access tokens live very short. If your application needs access to a Google API beyond the lifetime of a single access token, it can obtain a refresh token. A refresh token allows your application to obtain new access tokens which you can persist in DataStore by inheriting it from IDataStore. the implementation of IDatastore is shown belowIMPORTANT POINT:
In case above code doesn't work, you can use below method for google aouthorization.
IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow( new GoogleAuthorizationCodeFlow.Initializer { ClientSecrets = GetClientConfiguration().Secrets, DataStore = new DbStore(@"****.21.58.192", "shabihgghgjdy3", "Shab****r@123", "FaithDb", "GoogleUser"), Scopes = new[] { CalendarService.Scope.Calendar } }); var uri = Request.Url.ToString(); var code = Request["code"]; if (code != null) { var token = flow.ExchangeCodeForTokenAsync(txtEmailAddress.Text, code, uri.Substring(0, uri.IndexOf("?")), CancellationToken.None).Result; // Extract the right state. var oauthState = AuthWebUtility.ExtracRedirectFromState( flow.DataStore, txtEmailAddress.Text, Request["state"]).Result; Response.Redirect(oauthState); } else { var result = new AuthorizationCodeWebApp(flow, uri, uri).AuthorizeAsync(txtEmailAddress.Text, CancellationToken.None).Result; if (result.RedirectUri != null) { // Redirect the user to the authorization server. Response.Redirect(result.RedirectUri); } else { // The data store contains the user credential, so the user has been already authenticated. } }
IDataStore Implementation
using Google.Apis.Json; using Google.Apis.Util.Store; using System; using System.Collections.Generic; using System.Configuration; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DotNetGoogleCalendarIntegration { public class DatabaseStore : IDataStore { //database parameters readonly string tableName; readonly string serverName; readonly string loginName; readonly string passWord; readonly string databaseName; private Boolean _IsConnected { get; set; } public Boolean connectionAlive { get { return _IsConnected; } } /// <param name="database">database name</param> public DatabaseStore(String server, string login, string password, string databasename, string tablename) { tableName = tablename; serverName = server; loginName = login; passWord = password; databaseName = databasename; SqlConnection myConnection = this.connectdb(); // Opens a connection to the database. if (_IsConnected) { // check if the Table Exists; try { SqlDataReader myReader = null; SqlCommand myCommand = new SqlCommand("select 1 from GoogleOAuthUser where 1 = 0",myConnection); myReader = myCommand.ExecuteReader(); while (myReader.Read()) {var hold = myReader["Column1"];} } catch { // table doesn't exist we create it SqlCommand myCommand = new SqlCommand("CREATE TABLE [dbo].GoogleOAuthUser( " + " username [nvarchar](100) NOT NULL," + " RefreshToken [nvarchar](4000) NOT NULL," + " Userid [nvarchar](50) NOT NULL" + " ) ON [PRIMARY]", myConnection); myCommand.ExecuteNonQuery(); } } myConnection.Close(); } /// Stores the given value for the given key. It creates a new ///file (named <see cref="GenerateStoredKey"/>) in <typeparam name="T">///type to store in the data store</typeparam> /// <param name="key">The key</param> /// <param name="value">The value to store in the data store</param> public Task StoreAsync<T>(string key, T value) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Key MUST have a value"); } var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value); SqlConnection myConnection = this.connectdb(); if (!_IsConnected) { throw new Exception("Not connected to the database"); } // Try and find the Row in the DB. using (SqlCommand command = new SqlCommand ("select Userid from GoogleOAuthUser where UserName = @username", myConnection)) { command.Parameters.AddWithValue("@username", key); string hold = null; SqlDataReader myReader = command.ExecuteReader(); while (myReader.Read()) { hold = myReader["Userid"].ToString(); } myReader.Close(); if (hold == null) { try { // New User we insert it into the database string insertString = "INSERT INTO [dbo].GoogleOAuthUser (username,RefreshToken,Userid) " + " VALUES (@key,@value,'1' )"; SqlCommand commandins = new SqlCommand(insertString, myConnection); commandins.Parameters.AddWithValue("@key", key); commandins.Parameters.AddWithValue("@value", serialized); commandins.ExecuteNonQuery(); } catch (Exception ex) { throw new Exception("Error inserting new row: " + ex.Message); } } else { try { // Existing User We update it string insertString = "update GoogleOAuthUser " + " set RefreshToken= @value " + " where username = @key"; SqlCommand commandins = new SqlCommand(insertString, myConnection); commandins.Parameters.AddWithValue("@key", key); commandins.Parameters.AddWithValue("@value", serialized); commandins.ExecuteNonQuery(); } catch (Exception ex) { throw new Exception("Error updating user: " + ex.Message); } } } myConnection.Close(); return TaskEx.Delay(0); } /// <summary> /// Deletes the given key. It deletes the <see cref="GenerateStoredKey"/> /// named file in <see cref="FolderPath"/>. /// </summary> /// <param name="key">The key to delete from the data store</param> public Task DeleteAsync<T>(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Key MUST have a value"); } SqlConnection myConnection = this.connectdb(); if (!_IsConnected) { throw new Exception("Not connected to the database"); } // Deletes the users data. string deleteString = "delete GoogleOAuthUser from " + " where username = @key"; SqlCommand commandins = new SqlCommand(deleteString, myConnection); commandins.Parameters.AddWithValue("@key", key); commandins.ExecuteNonQuery(); myConnection.Close(); return TaskEx.Delay(0); } public Task<T> GetAsync<T>(string key) { //Key is the user string sent with AuthorizeAsync if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Key MUST have a value");} TaskCompletionSource<T> tcs = new TaskCompletionSource<T>(); ConnectionStringSettings mySetting = ConfigurationManager.ConnectionStrings["strSqlConnection"]; if (mySetting == null || string.IsNullOrEmpty(mySetting.ConnectionString)) throw new Exception("Fatal error: missing connecting string in web.config file"); string connString = mySetting.ConnectionString; SqlConnection objConn = new SqlConnection(connString); objConn.Open(); // Try and find the Row in the DB. using (SqlCommand command = new SqlCommand("select RefreshToken from GoogleOAuthUser where UserName = @username;", objConn)) { command.Parameters.AddWithValue("@username", key); string RefreshToken = null; SqlDataReader myReader = command.ExecuteReader(); while (myReader.Read()) { RefreshToken = myReader["RefreshToken"].ToString(); } if (RefreshToken == null) { // we don't have a record so we request it of the user. tcs.SetResult(default(T)); } else { try { // we have it we use that. tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(RefreshToken)); } catch (Exception ex) { tcs.SetException(ex); } } } return tcs.Task; } /// <summary> /// Clears all values in the data store. ///This method deletes all files in <see cref="FolderPath"/>. /// </summary> public Task ClearAsync() { SqlConnection myConnection = this.connectdb(); if (!_IsConnected) { throw new Exception("Not connected to the database"); } // Removes all data from the Table. string truncateString = "truncate table [dbo].GoogleOAuthUser "; SqlCommand commandins = new SqlCommand(truncateString, myConnection); commandins.ExecuteNonQuery(); myConnection.Close(); return TaskEx.Delay(0); } /// <summary>Creates a unique stored key based on the key and the class type.</summary> /// <param name="key">The object key</param> /// <param name="t">The type to store or retrieve</param> public static string GenerateStoredKey(string key, Type t) { return string.Format("{0}-{1}", t.FullName, key); } //Handel's creating the connection to the database private SqlConnection connectdb() { ConnectionStringSettings mySetting = ConfigurationManager.ConnectionStrings["strSqlConnection"]; if (mySetting == null || string.IsNullOrEmpty(mySetting.ConnectionString)) throw new Exception("Fatal error: missing connecting string in web.config file"); string connString = mySetting.ConnectionString; SqlConnection myConnection = null; try { myConnection = new SqlConnection(connString); try { myConnection.Open(); if (myConnection.State == System.Data.ConnectionState.Open) { _IsConnected = true; } else { throw new ArgumentException("Error unable to open connection to the database."); } } catch (Exception ex) { throw new ArgumentException("Error opening Connection to the database: " + ex.Message); } } catch (Exception ex) { throw new ArgumentException("Error creating Database Connection: " + ex.Message); } return myConnection; } } }The authorization sequence begins when your application redirects a browser to a Google URL; the URL includes query parameters that indicate the type of access being requested. Google handles the user authentication, session selection, and user consent. The result is an authorization code, which the application can exchange for an access token and a refresh token.
The application should store the refresh token for future use and use the access token to access a Google API. Once the access token expires, the application uses the refresh token to obtain a new one.
RETRIEVING EVENTS FROM GOOGLE CALENDAR
Here's a c# code snippet that returns upcoming events from a Google calendar// // Create Calendar Service. service = new CalendarService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = ApplicationName, }); // Define parameters of request. EventsResource.ListRequest request = service.Events.List("primary"); request.TimeMin = DateTime.Now; request.ShowDeleted = false; request.SingleEvents = true; request.MaxResults = 10; request.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime; Console.WriteLine("Upcoming events for Shabir:"); Console.WriteLine("===================================================="); Events events = request.Execute(); if (events.Items.Count > 0) { foreach (var eventItem in events.Items) { string when = eventItem.Start.DateTime.ToString(); string title = eventItem.Description.ToString(); string organizer = eventItem.Organizer.Email.ToString(); if (String.IsNullOrEmpty(when)) { when = eventItem.Start.Date; } Console.WriteLine("{0}" + Environment.NewLine + "{1}" + Environment.NewLine + " {2}" + Environment.NewLine + " {3}", eventItem.Summary, when, title, organizer); Console.WriteLine("------------------------------------------------------"); } } else { Console.WriteLine("No upcoming events found."); }
INSERTING EVENTS INTO GOOGLE CALENDAR
Here's a c# code snippet that inserts events into Google calendarEvent newEvent = new Event(); newEvent.Summary = "Shabir Meeting"; newEvent.Description = "Today going to discuss regarding OAuth Protocol"; newEvent.Start = new EventDateTime(); newEvent.Start.DateTime = DateTime.Now.AddHours(5); newEvent.End = new EventDateTime(); newEvent.End.DateTime = DateTime.Now.AddHours(8); service.Events.Insert(newEvent, "primary").ExecuteAsync(); Console.Read();
IMPORTANT POINTS
1) There are three types of client IDs, so be sure to get the correct type for your application: There are three types of client IDs, so be sure to get the correct type for your application:- Web application client IDs
- Installed application client IDs
- Service Account client IDs