Google Calendar API Authentication with C# 125


Google Calendar
Have you been trying to connect your website or application to Google Calendar? Would you like to be able to show your users a Google Calendar for your website?   If you are you trying to work with the Google Calendar API in C# .net I might be able to help.   In this tutorial series we will be looking into how to connect to Google Calendar API using OAuth2, as well as a service account. I will show you how to get a list of the users Calendars.   How to add events to a Calendar, and a number of other things as this tutorial series extends.

Google Calendar API – Tutorial Series

  1. Google Calendar API Authentication
  2. Google Calendar API Sample Project

Project Setup

Make sure your project is at least set to .net 4.0.

Add the following NuGet Package

//Nuget package https://www.nuget.org/packages/Google.Apis.Calendar.v3/
PM> Install-Package Google.Apis.Calendar.v3

Usings

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Google.Apis.Auth.OAuth2;
using System.Threading;
using Google.Apis.Util.Store;
using Google.Apis.Calendar.v3;
using Google.Apis.Services;
using System.IO;
using System.Security.Cryptography.X509Certificates;

[wp_ad_camp_3]

Authenticating Google Calendar API With C#

Authentication is the key to everything here. If you want to be able to access data you will need to be authenticated.

The two types of Authentication you can use are:

  • Open Authentication or  OAuth2 – Which is designed to allow you to access other peoples data.   For Example: A user installs your application that will allow them to view there Google Calendar Data, before your application can view there data the user will be asked to authenticate your application giving your application permission to access their data.
  • Service Account Authentication – Which is designed to allow you to access your own data.   For example: You would like to display a Calendar  for  your website.   For this there is no reason to ask permission because you already own this data.

I have created a separate tutorial that explains how to create the different credentials.  Decide which type of authentication you need and please read though that tutorial and come back to continue this one.

Types of Authentication

There are two types of Authentication OAuth2 which will allow you to access another users data, and a service account which can be set up to access your own data.   The code for them is slightly different.

Google Calendar API OAuth2 Authentication

If you want to be able to access data owned by someone else you will need to be authenticated. The code for authentication simply displays a webpage for your user asking them to give you permission to access there data. If the user gives you permission a file is then stored on the machine in the %AppData% directory containing all the information you will need in the future to access there data. For my testing purpose I use Environment.UserName as the name of the user, but if you are doing this web based you could use it as a session var of some kind.

That’s just fine but how does Google know what kind of permission you want to ask the user for? That’s where scopes come in, with scopes you define what permissions you need.

The code below will basically ask for everything, you should only include the scopes you NEED.

 string clientId = "";//From Google Developer console https://console.developers.google.com
 string clientSecret = "";//From Google Developer console https://console.developers.google.com
 string userName = ""//  A string used to identify a user.
 string[] scopes = new string[] {
 	CalendarService.Scope.Calendar, // Manage your calendars
 	CalendarService.Scope.CalendarReadonly // View your Calendars
 };

// here is where we Request the user to give us access, or use the Refresh Token that was previously stored in %AppData%
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets {
 		ClientId = clientId, ClientSecret = clientSecret
 	}, scopes, userName, CancellationToken.None, new FileDataStore("Daimto.GoogleCalendar.Auth.Store")).Result;

Assuming the user clicks Accept and gives you permission you will then have a valid user credential you can build upon.

The main problem you may have with this is that it is useing FileDataStore which stores the data in the users %AppData% directory while this is probably fine for an installed application this is probably not the optimal solution for a web application. If you want to store the data in the database you need to create your own implementation of IDataStore. You are in luck I have a working version of that on GitHub.

Google Calendar API Service Account Authentication

[wp_ad_camp_1]If you are only accessing data that you own there is no reason to ask a user for permission to access it. You can use a Service account. When setting up a service account for use with Google Calendar API you only need to take the Service account email address. Go to the Google Calendar website. Find the Calendar Settings , then go to the Calendars tab, find the calendar you want to access and click on “Shared: Edit settings” add the service account email address like you would a persons email address. This will give the service account the same access as if you where sharing it with any other user.

You will then use the keyfile loaded on the developer console along with the service account email address to get authorization.

 string[] scopes = new string[] {
 	CalendarService.Scope.Calendar, // Manage your calendars
 	CalendarService.Scope.CalendarReadonly // View your Calendars
 };

 var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);

ServiceAccountCredential credential = new ServiceAccountCredential(
 	new ServiceAccountCredential.Initializer(serviceAccountEmail) {
 		Scopes = scopes
 	}.FromCertificate(certificate));

 	

Google Calendar Service

Now that you are authenticated its only a matter of passing your credentials to create a new CalendarService. The service is where all of the magic is. It is though the service that all of the calls will be made to the Google Calendar API.

// Create the service.
 	CalendarService service = new CalendarService(new BaseClientService.Initializer() {
 		HttpClientInitializer = credential,
 		ApplicationName = "Calendar API Sample",
 	});

Conclusion

You should now understand how to access the Google Calendar API with an authenticated user. Join me for the next tutorial where we will look at how to get a list Calendars

If you had any problems with this tutorial you can check the a sample project for working with Google Calendar API on GitHub as part of the Google-Dotnet-Samples project. With in that project you will find a helper class for Authentication with Google Calendar . Authentication.cs


About Linda Lawton

My name is Linda Lawton I have more than 20 years experience working as an application developer and a database expert. I have also been working with Google APIs since 2012 and I have been contributing to the Google .Net client library since 2013. In 2013 I became a a Google Developer Experts for Google Analytics.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

125 thoughts on “Google Calendar API Authentication with C#

  • Ronny

    Hi Linda,

    is there a way to configure the auth-uri before calling the GoogleWebAuthorizationBroker.AuthorizeAsync method?
    I want to append the “login_hint=user@example.com” with a given user email to open the browser-login-process with this user prefilled! I want to prevent that the user authorize the app with the wrong user account.
    Is this possible?

    Cheers,
    Ronny

      • Ronny

        Hi,

        i have looked at the source and I found no option to do that… maybe change the source of the library and use that instead directly from google…

        The important class is the GoogleAuthorizationCodeFlow.Initializer class, because it holds the auth-url. it is defined in GoogleAuthConsts as a const string…

        I tried to subclass GoogleAuthorizationCodeFlow.Initializer and override the getter/setter for AuthorizationServerUrl but the problem is the conversion back to GoogleAuthorizationCodeFlow.Initializer…

        Any ideas?

  • Ronny

    Hi Linda,

    here is my solution:

    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Auth.OAuth2.Flows;
    using Google.Apis.Auth.OAuth2.Requests;
    using Google.Apis.Util.Store;
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;

    namespace MyOAuth2
    {
    //own implementation to append login_hint parameter to uri

    public class MyOAuth2WebAuthorizationBroker : GoogleWebAuthorizationBroker
    {
    public new static async Task AuthorizeAsync(ClientSecrets clientSecrets,
    IEnumerable scopes, string user, CancellationToken taskCancellationToken,
    IDataStore dataStore = null)
    {
    var initializer = new GoogleAuthorizationCodeFlow.Initializer
    {
    ClientSecrets = clientSecrets,
    };
    return await AuthorizeAsyncCore(initializer, scopes, user, taskCancellationToken, dataStore)
    .ConfigureAwait(false);
    }

    private static async Task AuthorizeAsyncCore(
    GoogleAuthorizationCodeFlow.Initializer initializer, IEnumerable scopes, string user,
    CancellationToken taskCancellationToken, IDataStore dataStore = null)
    {
    initializer.Scopes = scopes;
    initializer.DataStore = dataStore ?? new FileDataStore(Folder);
    var flow = new MyAuthorizationCodeFlow(initializer, user);

    // Create an authorization code installed app instance and authorize the user.
    return await new AuthorizationCodeInstalledApp(flow, new LocalServerCodeReceiver()).AuthorizeAsync
    (user, taskCancellationToken).ConfigureAwait(false);
    }
    }

    public class MyAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
    {
    private readonly string userId;

    /// Constructs a new Google authorization code flow.
    public MyAuthorizationCodeFlow(Initializer initializer, string userId)
    : base(initializer)
    {
    this.userId = userId;
    }

    public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(string redirectUri)
    {
    return new GoogleAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl))
    {
    ClientId = ClientSecrets.ClientId,
    Scope = string.Join(” “, Scopes),
    //append user to url
    LoginHint = userId,
    RedirectUri = redirectUri
    };
    }
    }
    }

    • Francesco Guerricchio

      It appears the problem is the async and await keywords, deleting those all work correctly

      following the working code:

      using Google.Apis.Auth.OAuth2;
      using Google.Apis.Auth.OAuth2.Flows;
      using Google.Apis.Auth.OAuth2.Requests;
      using Google.Apis.Util.Store;
      using System;
      using System.Collections.Generic;
      using System.Threading;
      using System.Threading.Tasks;
      using Google.Apis.Json;
      using System.Net.Http;
      using Google.Apis.Auth.OAuth2.Responses;
      using Google.Apis.Requests;
      using Google.Apis.Requests.Parameters;

      namespace MobiSync.Providers.GMAIL.Helpers
      {
      //own implementation to append login_hint parameter to uri

      public class CustomOAuth2WebAuthorizationBroker : GoogleWebAuthorizationBroker
      {
      public new static Task AuthorizeAsync( ClientSecrets clientSecrets,
      IEnumerable scopes, string user, CancellationToken taskCancellationToken,
      IDataStore dataStore = null )
      {
      var initializer = new MyAuthorizationCodeFlow.Initializer
      {
      ClientSecrets = clientSecrets,
      };
      return AuthorizeAsyncCore( initializer, scopes, user, taskCancellationToken, dataStore );
      }

      private new static Task AuthorizeAsyncCore(
      MyAuthorizationCodeFlow.Initializer initializer, IEnumerable scopes, string user,
      CancellationToken taskCancellationToken, IDataStore dataStore = null )
      {
      initializer.Scopes = scopes;
      initializer.DataStore = dataStore ?? new FileDataStore( Folder );
      var flow = new MyAuthorizationCodeFlow( initializer, user );

      // Create an authorization code installed app instance and authorize the user.
      return new AuthorizationCodeInstalledApp( flow, new LocalServerCodeReceiver( ) ).AuthorizeAsync
      ( user, taskCancellationToken );
      }
      }

      public class MyAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
      {
      private readonly string userId;

      /// Constructs a new Google authorization code flow.
      public MyAuthorizationCodeFlow( Initializer initializer, string userId )
      : base( initializer )
      {
      this.userId = userId;
      }

      public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest( string redirectUri )
      {
      System.Diagnostics.Debug.WriteLine( “I’m here”);
      return new GoogleAuthorizationCodeRequestUrl( new Uri( AuthorizationServerUrl ) )
      {
      ClientId = ClientSecrets.ClientId,
      Scope = string.Join( ” “, Scopes ),
      //append user to url
      LoginHint = userId,
      RedirectUri = redirectUri
      };
      }
      }
      }

    • Linda Lawton

      Unfortunately i don’t think that i can. This requires that you have a domain registered. The only domain i own is this one and it runs PHP. I would need access to a domain on a windows server to test this.

    • Linda Lawton

      Unfortunately i don’t think that i can. This requires that you have a domain registered. The only domain i own is this one and it runs PHP. I would need access to a domain on a windows server to test this.

  • Mayuresh

    Hi Linda

    I read your tutorials for Oauth 2 and all found to be very helpful. Thanks for sharing the tutorials. However I am facing one problem. I am accessing calendars using my C# application. Application is authenticate using user credentials. When user consent launches in web browser user close it without allow or cancel. In this scenario Google don’t return any value and application gets hang. How can we come to know that user cancel the user consent?

    Thank you.

    • Linda Lawton

      Add a try catch around the GoogleWebAuthorizationBroker section before you create the service. If the user hits cancel it will return

      InnerException = {“Error:\”access_denied\”, Description:\”\”, Uri:\”\””}

  • Mayuresh

    I am already adding try catch around GoogleWebAuthorizationBroker but not throwing any exception. When user consent tab in web browser is closed (Alt + F4) without clicking on Allow or Cancel application gets stuck.
    Could you please suggest any solution on this?

    try
    {
    GogleWebAuthorizationBroker gWebAuthorizationBroker = new GoogleWebAuthorizationBroker();
    CientSecrets _ClientSecret = new ClientSecrets();
    _ClientSecret.ClientId = ClientId.;
    _ClientSecret.ClientSecret = ClientSecret;
    FileDataStore objFileDataStore = new FileDataStore(applicationPath, true);

    System.Threading.Tasks.Task userCred = GoogleWebAuthorizationBroker.AuthorizeAsync(_ClientSecret,
    new[]{ CalendarService.Scope.Calendar},
    Username,
    System.Threading.CancellationToken.None,
    objFileDataStore);

    UserCredential _userCredentials = await userCred;
    }
    catch (Exception)
    { }

  • Saar

    Hi Linda, is there any way to authenticate the user using user name and password without opening the web browser?
    I would like to build the application where the user inserts his name and password.

    Thanks,
    Saar

  • Mikkel Nilesen

    What is the best way to debug?
    I keep getting an internal error, when trying to create UserCredential – but the error message is not very informative.

    When testing a webservice on localhost, what should the redirect uri be?

    BR, Mikkel

    • Linda Lawton

      When testing a web-service localhost, http://localhost/Oauth2.aspx or create a Client ID for native application but what ever you do don’t release it with the Client ID for native application credentials. I just find it easier to use that then you don’t have to deal with the hole redirect uri nightmare with visual studio adding ports.

      Do a try catch what exactly is the error its coming with?

      • Mikkel Nilesen

        I got it working on my local machine – using http://localhost/authorize/ as redirect uri

        But won’t work on hosted server. According to Fiddler I get these exceptions.

        {“Message”:” at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)\r\n at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)\r\n at System.Threading.Tasks.Task`1.get_Result()\r\n at DSInettetUserControls.CalendarServicesV3.GetFeed(String start, String end, Int32 numberToRetrieve, String calendarID)”,”StackTrace”:” at DSInettetUserControls.CalendarServicesV3.GetFeed(String start, String end, Int32 numberToRetrieve, String calendarID)”,”ExceptionType”:”System.ArgumentException”}

        It really drives me crazy!

        Im using your idatastore btw…works great. Thx 🙂

  • Greg Frazier

    Hi Linda,

    “var certificate = new X509Certificate2(keyFilePath, “notasecret”, X509KeyStorageFlags.Exportable);”

    What is the KeyFilePath? Is this the path of the location of the certificate file on the developer’s computer? Or is it a path to the Google Developers Console? I’m developing apps on a newer laptop than the one I created the service account info from. Do I need to generate a new P12 key? If I do, will it replace the current P12 key or will I have two keys? I plan to use the Service Account authentication.

  • Mikkel Nilesen

    Hi Linda,

    I need someone (you I hope) to point me in the right direction.

    On our company intranet we have a calendar, we use for internal stuff….like booking a meeting room. Behind the calendar there is a google calendar, to which I make some standart api calls like insert, delete, getEntries etc.

    Since the Calendar V3 update it doesn’t work anymore of course, so I need to change my api calls – but Im not sure how.

    What I want to acomplish is:
    I have a google calendar to which I will allow people to insert, delete and change entries via a custom interface that use google api v3 (.Net).
    They (the people) might NOT have a google account on their own.
    They (the people) DON’T know the password or username to the calendar account.
    The calendar stuff is not a secret – I accept low sescurity if necessary.

    As I understand, I can only use “Public API access” to show entries, but not add or delete. Is that correct?

    What kind of auth. would you recommend for a solution like this?

    Best Regards, Mikkel

    • Linda Lawton

      I would go with a service account if i was you. You wont need a login. Drawback no one will be able to see it on the Web version of Google Calendar but that doesn’t sound like it will be an issue for you.

      • Vinay

        Hi,

        I’ve a similar case, where I’m building solution for meeting rooms.
        In my case, I need to show calendar events of multiple google accounts. I want only readonly access to the calendar.

        please suggest, how can I do that.

        Vinay

        • Linda Lawton Post author

          I can think of a few ways of doing that.

          1. Create a service account give the owners of the accounts the service account email address ask them to grant it access to the calendar in question.
          2. Use Oauth2 and request read only access to the users calendars.

  • Lukas Raab

    Hi Linda,
    is it possible to access to any Google Calendar (user enters a public link or username and password)? (readonly)
    I am using the .net library.

    Thanks Lukas!

    • Linda Lawton

      No you cant use Client login (Login and password) to access any of the Google APIs useing the .net client library. As of April 20th 2015 you wont be able to access any of the Google APIs like this.

      You need to use Open Authentication.

  • raj

    I have 100 google calendar user and I need to create event in their calendar. Event is different from one to another. I have all 100 users gmail id. Please suggest me how to achieve this?
    All event related information is available in Database.
    1. Should I need to use OAuth2 ?
    2. How to achieve this 100 users? Do I need to create 100 OAuth2 Authentication?
    Please advice. Thanks.

    • Linda Lawton

      You will need to have each of your users authenticate your application, then you can save the refresh-token somewhere say the database. Once you have there permissions you will be able to add events to there calendar when ever you like. Yes you will need to use Oauth2.

  • richwood

    Hello Linda, I hope you’re there.
    I’m attempting to stand on your shoulders and I keep falling off !
    I have a main goog calendar, while logged in, i created 2 calendars, made them public and shared them to other gmail accounts. The account for ‘main’ has a project, api’s enabled and various credentials,. the important one being the native app creds. CLIENT_ID + CLIENT_SECRET. In your Clndr-Api-dotnet console app, I put in the databasedatastore code from your api auth project. I set the client_id, client secret, calendarid and the constructed dbdatastore, all good. On 1st run, I get the auth challenge, grant it, the token is captured and subsequent runs utilize the refresh token and all is well. Each calendar id was handled in the same way, 1st run auth, capture the token; insert to db as separate row etc. I am using the same client_id and client_secret for all these “child” calendars since “main” is the creator / owner of them
    I’m off in the stix with the following behavior though. For the first child calendarid, the google.apis.calendar.v3.data.events object AccessRole property returns with “owner” ..all good. For the 2nd (and subsequent) calendarid’s the role returns as “reader” ..yikes.. Honest to Pete, these calendars are set up exactly the same way. I’ve been spinning my wheels for more than a few hours, trial and error with no joy. I’m leaning toward the reason being something “dumb” & not fathoming what dumb might be. I’m requesting your opinion. Can you suggest any approach to diagnosis and or fix. Rich in Austin.

  • richwood

    Ok, it’s a “dumb” for sure. In my implementation, the service account is totally adequate for doing crud on various child calendars: the scenario being a “master” calendar where n number of “child” calendars are created and shared to appropriate gmail accounts, Even though each child calendar is shared allowing R/W by the associated gmail user, that is not enough to allow the service account to do anything but read. The appropriate role has to be added. One way to add is to use the “developers.google.com.google-apps/calendar.reference/acl/insert/#example” first. From the top, the steps are 1. create a gmail account. then a calendar (this is your container calendar), 2 Since you are logged in on that gmail account, go on the console.developers.google site, create a project on that account, enable the calendar api, derive the oauth service account creds.. 3. go back over to the calendar, add a child calendar, 4.share that child calendar to a gmail email of your choice, (make sure to accept the invite sent to that email while logged in on that email account) . 4. Go back over and make sure you’re logged into the account that owns the calendar and project, go to the “master” calendar , find the child calendar, got into it’s settings, and copy the calendar id. 5. Go back over to the developers.google.com.google-apps/calendar.reference/acl/insert/#example, turn on oAuth 2.0 (you’ll see the auth window come and go) scroll down to the “try it! ” area and put the calendarid in the calendarid box, put in “writer” in the role box, under “scope” put “user” for the Type and add a value param. In the value param paste in your SERVICE_ACCOUNT_EMAIL. 6. Click Execute. Note the http 200 -or- fix an error. Once this role has been added, the service account will be able to r/w events to the child calendars.
    This worked for me and is easier to deploy than using the native app route. Rich

      • raza

        Thanks for your replay,
        Actually my first application on google.apis
        please help me
        i tried you code from github,
        i tried to develop a simple application to create an event in google calender
        from win forms applications
        by using the service account auth
        am receiving the error
        “Unexpected character encountered while parsing value: <. Path '', line 0, position 0."

        My Code:

        CalendarService service;

        String SERVICE_ACCOUNT_EMAIL = "XXXXXXXXXXXXXXXXX@developer.gserviceaccount.com";
        string SERVICE_ACCOUNT_KEYFILE = @"d:\EVENTREQUEST-a873840e956d.p12";
        service = Authentication.AuthenticateServiceAccount(SERVICE_ACCOUNT_EMAIL, SERVICE_ACCOUNT_KEYFILE);

        // Authenticate Oauth2
        eventdata.Summary = "title";
        eventdata.Location = "location";
        eventdata.Description = "description";
        EventDateTime start = new EventDateTime();
        start.DateTime = DateTime.Now;
        eventdata.Start = start;
        EventDateTime end = new EventDateTime();
        end.DateTime = DateTime.Now.AddHours(1);
        eventdata.End = end;
        eventdata = DaimtoEventHelper.insert(service, "1", eventdata);

  • Darrell Sveistrup

    Hi Linda,

    You example show using the P12 file generated to create the ServiceAccountCredential’s
    Do you know how to do the same with the json file option?

    I’m guessing that you would not call the “.FromCertificate(certificate)”
    And would be setting the key, but not sure what mapping would be or how to convert.

  • keith

    hi I’ve been having some difficulty with creating events onto google calendar with .net, I couldn’t fully make use of your sample codes as I’m really just bad at .net

  • Neeraj Singh

    I need to use Google Calendar Api from ASP.NET Web Api application. Is there a substitute available for RedirectResult as ApiControllers doesn’t have this and Google’s Mvc4 library uses ResultRedirect to get the authorization.

  • Octavio Manzano

    Hi, I have an app that uses Authentication.AuthenticateOauth and works perfectly on localhost: i can get the events of the calendar’s users and insert new events too but when deply my App to production server with IIS 7.5 my app doesn’t work. I have all dll on the bin, can you help me please?

  • Andy

    Just wanted to say thanks for all your examples.
    They helped me immensely in starting to secure some of our applications using Google instead of LDAP.

  • Javier

    Hi Linda! Thanks for the tut, it is great!
    I’m getting this error: “Key MUST have a value” in the credential line, do you know what that is?
    Thx
    Javier

  • Tom Weissert

    Hi Linda,

    Thank you for your excellent work. I have two problems, here is the first.
    I’m trying to use oauth2 to let a user authenticate and then add events, blah blah,
    my authentication code is below.
    The problem is that this page hangs and never returns, the consent screen never appears.

    using System;
    using System.Linq;
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Calendar.v3;
    using Google.Apis.Calendar.v3.Data;
    using Google.Apis.Services;
    using System.Configuration;
    using System.Threading;

    namespace Events
    {
    public partial class Events_newauth2 : System.Web.UI.Page
    {
    CalendarService service;

    protected void Page_Load(object sender, EventArgs e)
    {
    if (!IsPostBack)
    {
    if (service == null) { service = CreateService(); }
    CalendarList result = service.CalendarList.List().Execute();

    foreach (CalendarListEntry calendar in result.Items)
    {
    lblNote.Text += “” + calendar.Id + ” : ” + calendar.Summary;
    }
    }
    }
    protected CalendarService CreateService()
    {
    UserCredential credential;
    try
    {
    credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
    new ClientSecrets
    {
    ClientId = ConfigurationManager.AppSettings[“ClientID”],
    ClientSecret = ConfigurationManager.AppSettings[“ClientSecret”]
    },
    new[] { CalendarService.Scope.Calendar },
    “tweissert@agnesirwin.org”,
    CancellationToken.None,
    new DatabaseDataStore(“Websrv”, “aspnet”, “123”, “CalendarEvents”, “GoogleUser”)).Result;
    }
    catch
    (Exception ex)
    {
    lblNote.Text = ex.Message;
    return null;
    }
    CalendarService theservice = new CalendarService(new BaseClientService.Initializer()
    {
    HttpClientInitializer = credential,
    ApplicationName = “AIS Calendar Tool”,
    });
    return theservice;
    }
    }
    }

  • dantha

    Hi Linda,

    I am using Google Calendar API Service Account Authentication for reservation application.

    I am having below error: “An error occurred while sending the request. —> System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. ”

    it works fine on local pc. but when I upload it into the server it gives an error. (below). please help me to resolve this.

    2015-08-05 15:57:06,018 – Error when Insert to google cal ‘GoogleCalendarSync’ >>An error occurred while sending the request.
    2015-08-05 15:57:06,018 – ex.StackTrace’ >> at Google.Apis.Requests.ClientServiceRequest`1.Execute()
    at WebService.GoogleCalendarSync.GoogleCalendarSyncProcess(Int32 RsvId, String GroupId, String Summary, String Desc, String StartTime, String EndTime, String Location, String OrganizerEmail)
    2015-08-05 15:57:06,018 – ex.Source’ >>System.Net.Http.HttpRequestException: An error occurred while sending the request. —> System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. —> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
    at System.Net.TlsStream.EndWrite(IAsyncResult asyncResult)
    at System.Net.PooledStream.EndWrite(IAsyncResult asyncResult)
    at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)
    — End of inner exception stack trace —
    at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)
    at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
    — End of inner exception stack trace —
    at Google.Apis.Requests.ClientServiceRequest`1.Execute()
    at WebService.GoogleCalendarSync.GoogleCalendarSyncProcess(Int32 RsvId, String GroupId, String Summary, String Desc, String StartTime, String EndTime, String Location, String OrganizerEmail)

    here are my code segments:

    var serviceAccountEmail = “*************nt.com”; // found https://console.developers.google.com
    var keyFilePath = @”D:\Production\apps.p12″; // Downloaded from https://console.developers.google.com
    service = Authentication.AuthenticateServiceAccount(serviceAccountEmail, keyFilePath);

    public static CalendarService AuthenticateServiceAccount(string serviceAccountEmail, string keyFilePath)
    {

    // check the file exists
    if (!File.Exists(keyFilePath))
    {
    log.Error(“AuthenticateServiceAccount >>An Error occurred – Key file does not exist”);
    return null;
    }

    string[] scopes = new string[] {
    CalendarService.Scope.Calendar , // Manage your calendars
    CalendarService.Scope.CalendarReadonly // View your Calendars
    };

    var certificate = new X509Certificate2(keyFilePath, “notasecret”, X509KeyStorageFlags.Exportable);
    try
    {
    ServiceAccountCredential credential = new ServiceAccountCredential(
    new ServiceAccountCredential.Initializer(serviceAccountEmail)
    {
    Scopes = scopes
    }.FromCertificate(certificate));

    // Create the service.
    CalendarService service = new CalendarService(new BaseClientService.Initializer()
    {
    HttpClientInitializer = credential,
    ApplicationName = “Calendar API Sample”,
    });
    return service;
    }
    catch (Exception ex)
    {
    log.Error(“AuthenticateServiceAccount >>” + ex.Message);
    // Console.WriteLine(ex.InnerException);
    return null;

    }
    }

    please help me to solve this issue.

  • Finlay

    Hi Linda,

    Diamto.Google.Authentication/DatabaseDataStore.cs saves refresh token only, how about access token?
    CREATE TABLE dbo.GoogleUser(username nvarchar(4000), RefreshToken nvarchar(4000), Userid nvarchar(4000))

    Based on my experience with Calendar API v2, I suppose access token is useful for
    – health check whether the token is still valid
    – refresh itself
    – revoke a previously permitted access

    Any idea?

    • Linda Lawton Post author

      No real reason to save the access token when it expires after an hour. You can just use the refresh token to get a new access token when ever you need. I am not aware of any limits to the number of times you can request a new access token or how often you can request it.

      – I don’t bother with health check normally i just request a new one.
      – you use the refresh token to revoke it i think not the access token. (But its been a while since i have done that i may be wrong.)

  • Caipigott

    Hello,

    thanks for your great tutorial.
    So far i could make it run, but i’m running into an error “GoogleWebAuthorizationBroker.AuthorizeAsync”

    “The character set provided in ContentType is invalid. Cannot read content as string using an invalid character set.”
    “UTF-8″‘ is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method.\r\nParameter name: name”

    I already search an houre on the internet but without success.
    Do you have any idea what could be the problem?

    Thanks for your help in advance,
    greetings,
    Caipigott

    • Caipigott

      Hi,

      i’m sorry, i just found out that it’s up to the internet connection of my work. I guess it blocks the response, or something.
      No i used the connection of my celular and it worked.

      Greetings,
      Caipigott

  • Greg

    Hi Linda,

    Thanks for this tutorial. Have you done any tutorials yet on how to create an event on google calendar from a C# application in visual Studio?

    • Linda Lawton Post author

      ///

      /// Adds an entry to the user's calendar list.
      /// Documentation:https://developers.google.com/google-apps/calendar/v3/reference/calendarList/insert
      ///

      /// Valid Autenticated Calendar service
      /// Calendar identifier.
      /// an event
      /// event resorce: https://developers.google.com/google-apps/calendar/v3/reference/events#resource
      public static Event insert(CalendarService service, string id, Event body)
      {
      try
      {
      return service.Events.Insert(body,id).Execute();
      }
      catch (Exception ex)
      {
      Console.WriteLine(ex.Message);
      return null;
      }
      }

      • Greg

        Ok, Thank You. I noticed that piece of the code was there after I posted the comment, Sorry. I’m going to give it a try now.

  • Mudasser

    I am wokring on Google calendar API using C#. I am devloping a web page that will access the my clients(specific user) calendar and display just the calendar’s information,events etc on web page. So, I don’t want the user to enter in their Google account information which is what oAuth wants to do.

    i used the following code

    string serviceAccountEmail = “serviceAccountEmail “;
    string filepath = Application.StartupPath + @”\file.p12”;
    var certificate = new X509Certificate2(filepath, “notasecret”,
    X509KeyStorageFlags.Exportable);

    ServiceAccountCredential credential = new ServiceAccountCredential(new
    ServiceAccountCredential.Initializer(serviceAccountEmail)
    {
    Scopes =
    new[] { CalendarService.Scope.Calendar }
    }.FromCertificate
    (certificate));

    BaseClientService.Initializer initializer = new
    BaseClientService.Initializer();
    initializer.HttpClientInitializer = credential;
    initializer.ApplicationName = “Google Calendar Sample”;
    CalendarService calservice = new CalendarService(initializer);

    //Follwoign line did’t get my calendar events
    EventsResource.ListRequest request = calservice.Events.List(“primary”);
    Events events = request.Execute();

    I want to access the calender of specific email and need to show all the events etc .

    Kindly help.

    Thanks

    Regards

    Mudasser

    • Linda Lawton Post author

      Make sure you have taken the Service account email address and added it as a user on the Calendar in the Google Calendar website you can also find the Google Calendar id there, Primary isn’t going to work with a service account.

      • Mudasser

        I added the service account using my Google email,
        lets say my email is “abc@gmail.com” and service id generated is “gooleserviceygx@serviceaa-4789.ibm.gstrviceaccount.com”

        Then i created the public calendar(Calender Name:My events Calender) on using my Google account(abc@gmail.com),then created the Event in that public calender(My events Calender) lets say event is party.
        Then i edit my google calander setting(Shared: Edit settings) and add my service account in Share with specific people section.

        Following code i used
        string serviceAccountEmail = “gooleserviceygx@serviceaa-4789.ibm.gstrviceaccount.com “;
        string filepath = Application.StartupPath + @”\file.p12”;
        var certificate = new X509Certificate2(filepath, “notasecret”,
        X509KeyStorageFlags.Exportable);

        ServiceAccountCredential credential = new ServiceAccountCredential(new
        ServiceAccountCredential.Initializer(serviceAccountEmail)
        {
        Scopes =
        new[] { CalendarService.Scope.Calendar }
        }.FromCertificate
        (certificate));

        BaseClientService.Initializer initializer = new
        BaseClientService.Initializer();
        initializer.HttpClientInitializer = credential;
        initializer.ApplicationName = “Google Calendar Sample”;
        CalendarService calservice = new CalendarService(initializer);

        //Follwoing line did’t get my calendar events
        EventsResource.ListRequest request = calservice.Events.List(“primary”);
        Events events = request.Execute();

        Above line suppose to get event(party) which i added in my public calendar(My events Calender) but it is not getting.

        My requirement is whatever i added or add on my calender it should shows on my public website.

        • Linda Lawton Post author

          A service account is not you, even though you created it with your personal gmail its still not you it has its own google calendar.

          calservice.Events.List(“primary”);

          Primary is the service accounts primary calendar which is not yours.

          You need to go back in the settings and find the calendar id for your personal calendar.

          • Linda Lawton Post author

            If the calendar is public then you can use a public API key. If its someones personal calendar then you cant, you need to be authenticated to access private data.

        • Mudasser

          Can you kindly share some sample (C#) to get the public calender events using public key without being authenticated.

          Thanks in advance for help

        • Mudasser

          thanks for your help.Its so nice of you.
          Kindly just tell me, if i create a new calender by the following code, is it possible by code to make that calender as public.

          Calendar cal = new Calendar();
          cal.Summary = “calander created by code”;
          Calendar createdCalendar = service.Calendars.Insert(cal).Execute();

        • Mudasser

          And also kindly tell is there any way to get the public calendar only or any way to check either calender is public or not

          CalendarList clst = cservice.CalendarList.List().Execute();

          • Linda Lawton Post author

            calendar list just shows you the list of calendars you have added to calendar list. i would do calendar.get that might show if the calendar is public or not.

  • Willem Luijk

    Hello Linda,

    I published the Daimto.GoogleSample.Analytics to azure and got this message:

    2016-04-11T21:06:06 PID[17220] Information System.Net.HttpListenerException (0x80004005): Access is denied
    2016-04-11T21:06:06 PID[17220] Information at Microsoft.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
    2016-04-11T21:06:06 PID[17220] Information at Microsoft.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccess(Task task)
    2016-04-11T21:06:06 PID[17220] Information at Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.d__1.MoveNext()
    2016-04-11T21:06:06 PID[17220] Error
    2016-04-11T21:06:06 PID[17220] Error Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
    2016-04-11T21:06:06 PID[17220] Error at Daimto.Google.Sample.Analytics.DaimtoAnaltyicsManagmentHelper.AccountSummaryList(AnalyticsService service)
    2016-04-11T21:06:06 PID[17220] Error at Daimto.Google.Sample.Analytics.

    Do you have any clue?

    • Mudasser

      Hello,
      I created the sample Asp.net application to get all the calender and add event in a calendar.My Application is working fine when i run it using Visual Studio it successfully load the page,show all calendar info, add event in calendar.But when i deploy the app on IIS, page never load and it gives the “Request timed out” exception.Kindly help

      Following is the code

      private CalendarService InitGoogleService()
      {
      UserCredential credential;
      string path = Server.MapPath(“~”) + @”\GCSc.json”;
      string credPath = Server.MapPath(“~”) + @”\Calendar_Data”;
      credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets
      {
      ClientId = “My Client Id”,
      ClientSecret = “My Client Secret”
      }, Scopes, “user”, CancellationToken.None, new Google.Apis.Util.Store.FileDataStore(credPath)).Result;

      CalendarService service = new CalendarService(new BaseClientService.Initializer()
      {
      HttpClientInitializer = credential,
      ApplicationName = ApplicationName,
      });
      return service;
      }

  • Henry Tehrani

    hello:
    I am trying to import calendar settings into google calendar and followed your instructions, but I get an access error when I publish my code to staging server. codes works great in my local machine, how do I get around this ? any suggestions?
    here is my code:
    String gpath = dataPath + “/credentials/siteAPI.json”;
    UserCredential credential;
    ClientSecrets secrets = new ClientSecrets()
    {
    ClientId = “aaaaa”,
    ClientSecret = “yyyyy”,
    };
    using (var stream = new FileStream(gpath, FileMode.Open, FileAccess.Read))
    {
    //string folder = System.Environment.GetFolderPath( System.Environment.SpecialFolder.ApplicationData);
    //folder = Path.Combine(Environment.GetFolderPath(System.Web.HttpContext.Current.Server.MapPath(“YourFolderName”), folder));
    String g = dataPath + “\\credentials”;

    credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
    //GoogleClientSecrets.Load(stream).Secrets,
    secrets,
    new[] { CalendarService.Scope.Calendar },
    “user”,
    CancellationToken.None ,
    new FileDataStore(g)
    ).Result;
    }
    at this stage I get :
    System.ComponentModel.Win32Exception: Access is denied at Microsoft.Runtime.CompilerServices.TaskAwaiter.T …..

    error
    many thanks
    Henry

    • Linda Lawton Post author

      I would post your question to stackoverflow make sure to tag it google-calendar and google-apis-dotnet-client it will be easier to fix your problem with more eyes on it.

    • Linda Lawton Post author

      You need the client id and client secrete to use the refresh token. You need a client id and client secrete to get an access token. Client id and client secret identify your application to google you will always need that.

      • Ullas

        Hello Linda,
        Recently i have upgraded the .net google calendar API 1.9.0.26011 to Google.Apis… 1.27.1.878
        After that the environment path changed from “C:\Users\LOGONUSER\AppData\Roaming\Calendar.Auth.Store”
        “C:\Windows\system32\config\systemprofile\AppData\Roaming\Calendar.Auth.Store” and shows access error event if we use admin account as impersonate user in web.config file, any idea about this path change?

        • Ullas

          One or more errors occurred.Access to the path ‘C:\Windows\system32\config\systemprofile\AppData\Roaming\Calendar.Auth.Store\Google.Apis.Auth.OAuth2.Responses.TokenResponse-user1@gapps.dddd.com’ is denied.

          • Linda Lawton Post author

            What type of system is this? What type of project are you working with? App data is not normally in windows\system32 the default pat should be something like his C:\Users\HP_User\AppData\Roaming\Calendar.Auth.Store\Google.Apis.Auth.OAuth2.Responses.TokenResponse-user1@gapps.dddd.com

          • ullas

            Thank you very much for your advice. Yes i have noticed this option to provide full path like FileDataStore(@”c:\datastore”,true) and i changed my program to use full path, now it is working fine.

            we have integrated google calendar api with one of our asp.net web application and for the past 3 years it was working perfectly with only store name. but after upgrading the library locally it works fine, but on deployment server we noticed that the store path is like “‘C:\Windows\system32\config\systemprofile\AppData\Roaming\Calendar.Auth.Store\”. Older version on same deployment server still using “C:\Users\LOGONUSER\AppData\Roaming\Calendar.Auth.Store” so that means some changes happened with google calendar api .net libraries. But not able to find any documents on this change.

          • Linda Lawton Post author

            It looks like this is due to a change in how the default FileStore location is determined from v1.25.0 to v1.26.2.

            In v1.25.0 (on Windows Desktop) it uses Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
            In v1.26.2+ it uses Environment.GetEnvironmentVariable(“APPDATA”)

            On my test system these evaluated to identical directories, but it looks like this isn’t the case on the user that asked the question, or perhaps isn’t true generally on ASP.NET deployments.
            The reason for this change is to support FileDataStore on .NET Core (which doesn’t support SpecialFolder), and it was not intended to affect other customers, so this is unfortunate.

            The fix would be to use an #if in the code to continue to use Environment.SpecialFolder.ApplicationData only on the .NETFramework4.5 DLL in the package.
            I could do a 1.27.2 release with just this change, but I think I’d rather not. It’s probably now better just to leave things as they are, seeing as this new behaviour has been released now for about six weeks.

  • Eric

    Hello Linda,
    I have been trying to read calendar events using the calendar service Events.List method within a .Net program.
    Unfortunately, this method retrieves all events.
    Google Calendar supports timeMin and timeMax optional parameters but I do not manage to use then with a .Net client.
    I am quite desperate and not looking forward to use the API with HTTP…
    Did you do use this filtering option?
    Do you have pointers?
    Any ideas?
    And thank you for your usefull posts.
    Eric.

    • Eric

      I found the answer: do not focus on the RequestParameters collection. It describes allowed parameters. It doesn’t contain the parameters values. Those values are in the request object itself:

      var rq = service.Events.List( “primary” );
      rq.TimeMin = aDateTime;
      rq.TimeMax = aDateTime.AddDays(1);
      rq.SingleEvents = true;
      var events = rq.Execute();

      So simple…

      .Net is strongly typed. I should have looked there first.

  • Parag Jayvant Bhatkar

    String Scopes[]= new String[2]
    {
    CalendarService.Scope.Calendar,
    CalendarService.Scope.CalendarReadonly
    };

    I am getting a error in this
    Error 1 Bad array declarator: To declare a managed array the rank specifier precedes the variable’s identifier. To declare a fixed size buffer field, use the fixed keyword before the field type.

  • Bhavesh

    Hi Linda,
    I am facing a strange problem. I’ve created a google app that do the oauth authentication for the user. I’ve created one project that create add/edit and delete events in my application calendar. Once event is created in my application calendar at the same moment it will create an event in the authenticated google calendar.
    one of my friend is using this app and when he authenticate using this app, it authenticates successfully.
    when he create an event in my application, it should create that event to the authenticated google calendar, but it syncs to the other google calendar. In My google calendar account there are multiple calendar added and the event is created in one of the calendar.
    Can you help me out in this.

  • Chirag Pancahl

    Thank you mam for your blog,

    I am trying access google calendar API from my ASP.NET project.
    I am using service account key to access google calendar API.
    My code look similar to your have mention in your blog.
    My asp.net works fine on my local machine but when I upload it to server it start giving me an error.

    I had ask question on Stack Overflow, you can see my question on following link
    https://stackoverflow.com/questions/50599764/c-google-apis-auth-oauth2-responses-tokenresponseexception-errorinvalid-cli

    Mam please guide me what should I do for solve my error.

  • Sajal Pal

    I using the same process but after creating the events the system is unable to send notification mail to all attendees. Below is the code I am using to enable notification.
    String calendarId = “primary”;
    EventsResource.InsertRequest request = service.Events.Insert(newEvent, calendarId);
    request.SendNotifications = true;
    request.SendUpdates = EventsResource.InsertRequest.SendUpdatesEnum.All;
    Event createdEvent = request.Execute();

  • Mars

    Hi, I am using a google service account in c# and I can use CalendarList.List().Execute(), I have a couples of calendar and I can add events fine. But today I tried to add another calendar that I own, I put the service account’s email in the share of the calendar with full access like I did usually, but CalendarList is not giving me the new one, the pageToken is null, it looks like the service account is not accepting the invitation automatically like usualy? Would you know why the share doesn’t work? thanks.

    • Linda Lawton Post author

      Google made a change a while back service accounts only work with GSuite accounts now. YOu need to set up domain wide delegation to the service account and it can only work with users on the domain.

  • Quoc

    Dear Linda,

    I use Service Account to create a calendar event and I got an error:
    ‘Google.Apis.Requests.RequestError
    Not Found [404]
    Errors [
    Message[Not Found] Location[ – ] Reason[notFound] Domain[global]

    Can you help me fix it? My code:
    public class ServiceAccountAuthHelper
    {
    ///
    /// Authenticating to Google using a Service account
    /// Documentation: https://developers.google.com/accounts/docs/OAuth2#serviceaccount
    ///
    /// From Google Developer console https://console.developers.google.com
    /// Location of the .p12 or Json Service account key file downloaded from Google Developer console https://console.developers.google.com
    /// AnalyticsService used to make requests against the Analytics API
    public static CalendarService AuthenticateServiceAccount(string serviceAccountEmail, string serviceAccountCredentialFilePath)
    {
    try
    {
    if (string.IsNullOrEmpty(serviceAccountCredentialFilePath))
    throw new Exception(“Path to the .p12 service account credentials file is required.”);
    if (!File.Exists(serviceAccountCredentialFilePath))
    throw new Exception(“The service account credentials .p12 file does not exist at: ” + serviceAccountCredentialFilePath);
    if (string.IsNullOrEmpty(serviceAccountEmail))
    throw new Exception(“ServiceAccountEmail is required.”);

    // These are the scopes of permissions you need. It is best to request only what you need and not all of them
    string[] scopes = new string[] { CalendarService.Scope.Calendar, CalendarService.Scope.CalendarEvents, CalendarService.Scope.CalendarEventsReadonly }; // View your Google Analytics data

    // For Json file
    if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == “.json”)
    {
    GoogleCredential credential;
    using (var stream = new FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read))
    {
    credential = GoogleCredential.FromStream(stream)
    .CreateScoped(scopes)
    .CreateWithUser(“quocdaica158@gmail.com”);
    }

    // Create the Calendar service.
    return new CalendarService(new BaseClientService.Initializer()
    {
    HttpClientInitializer = credential,
    ApplicationName = “Calendar Authentication Sample”,
    });
    }
    else if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == “.p12”)
    { // If its a P12 file

    var certificate = new X509Certificate2(serviceAccountCredentialFilePath, “notasecret”, X509KeyStorageFlags.Exportable);
    var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
    {
    Scopes = scopes,
    }.FromCertificate(certificate));

    // Create the Calendar service.
    return new CalendarService(new BaseClientService.Initializer()
    {
    HttpClientInitializer = credential,
    ApplicationName = “Calendar Authentication Sample”,
    });

    }
    else
    {
    throw new Exception(“Unsupported Service accounts credentials.”);
    }

    }
    catch (Exception ex)
    {
    Console.WriteLine(“Create service account CalendarService failed” + ex.Message);
    throw new Exception(“CreateServiceAccountCalendarServiceFailed”, ex);
    }
    }

    public static void CreateEvent(CalendarService _service)
    {
    Event body = new Event();
    EventAttendee a = new EventAttendee();
    a.Email = “quocdaica158@gmail.com”;
    List attendes = new List();
    attendes.Add(a);
    body.Attendees = attendes;
    EventDateTime start = new EventDateTime();
    start.DateTime = DateTime.Now;
    EventDateTime end = new EventDateTime();
    end.DateTime = DateTime.Now.AddHours(2);
    body.Start = start;
    body.End = end;
    body.Location = “Sigapore”;
    body.Summary = “Discussion about new Spidey suit”;
    EventsResource.InsertRequest request = new EventsResource.InsertRequest(_service, body, “quocdaica158@gmail.com”);
    Event response = request.Execute();
    }
    }

  • Pingback: [SOLVED] How do I set return_uri for GoogleWebAuthorizationBroker.AuthorizeAsync? – BugsFixing

  • GJ

    Thanks for this tutorial! It helped me (9 years later) to connect my application to Google Calendar and retreive events with just a few lines of code! Great!