StringBuilder
StringBuilder

Reputation: 3

.NET Google Calendar API v3 : mutilple account authentification

I'm trying to write a program that will synchronize events between a CRM and Google Calendar of several Google accounts.

It's a Windows Console application.

For each Google account, in the API Console I enabled the Calendar API, created an application named "UpdateSync" (type : native) then downloaded the JSON file.

I renamed those files using the account mail address (ie [email protected])

Next, I wrote the code bellow :

    /// <summary>
    /// Loads updated event from Google Calender after last synchonization date
    /// </summary>
    /// <param name="user">The user we want to get appointments</param>
    public List<Appointment> LoadAppointments(User user)
    {
        Console.WriteLine("LoadAppointments({0})", user.Email);
        List<Appointment> Appointments = new List<Appointment>();

        UserCredential credential;
        using (var stream = new FileStream(string.Format("credentials\\{0}.json", user.Email), FileMode.Open, FileAccess.Read))
        {
            try
            {
                credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets, new[] { CalendarService.Scope.Calendar }, "user", CancellationToken.None, new FileDataStore("UpdayeSync.Credentials")).Result;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return null;
            }
        }

        BaseClientService.Initializer initializer = new BaseClientService.Initializer();
        initializer.HttpClientInitializer = credential;
        initializer.ApplicationName = "UpdateSync";
        service = new CalendarService(initializer);

        EventsResource.ListRequest req = service.Events.List("primary");
        req.TimeMin = DateTime.Now.AddMonths(-2).ToString("o");
        req.ShowDeleted = true;
        req.UpdatedMin = XmlConvert.ToString(LastSync, XmlDateTimeSerializationMode.Utc);
        req.SingleEvents = true;

        Events events;
        try
        {
            events = req.Execute();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            return null;
        }

        // [...]
    }

This code is called for each JSON file.

At the first call, the account to synchronize was "[email protected]". Google Chrome automatically oppened, and auto-logged with my own account ([email protected]) and asked me to allow my program to access Calendar API.

I accepted.

This popup never shown again.

Any change done for any account was done on my own calendar, not the one the JSON files refer to.

What's wrong ? How to access several Google accounts with a unique program ?

This program will run on a server as a scheduled task, and I must not open any window, as noone will look at the server's screen. I can't ask the user to authentificate himself, as there is not user.

But I can do anything on the Google account of any of the users (enabling API, creating keys, granting application, etc.)

Upvotes: 0

Views: 1444

Answers (1)

peleyal
peleyal

Reputation: 3512

You should create a new CalendarService (lightweight component) per user. CalendarService and all other Google services are implemented as immutables (for simplicity and to support thread-safe operations).

Create a new service for each user and use a different userId ("user1", "user2", etc.) as a parameter to GoogleWebAuthorizationBroker.AuthorizeAsync.

UPDATE:

You should also create your own class of flow:

class ForceUIGoogleAuthorizationCodeFlow : AuthorizationCodeFlow
{
    public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(string redirectUri)
    {
        return new GoogleAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl))
        {
            ClientId = ClientSecrets.ClientId,
            Scope = string.Join(" ", Scopes),
            RedirectUri = redirectUri,
            ApprovalPrompt = "force",
        };
    }
}

and then,

replace the your call to GoogleWebAuthorizationBroker with the following code:

var initializer = new GoogleAuthorizationCodeFlow.Initializer
{
       ClientSecrets = clientSecrets,
       Scopes = [SCOPED_HERE];
       initializer.DataStore = new FileDataStore("Credentials");
};
var flow = new ForceUIGoogleAuthorizationCodeFlow(initializer);
// Create authorization code installed app instance and authorize the user.
crdentials = await new AuthorizationCodeInstalledApp(flow, 
      new LocalServerCodeReceiver()).AuthorizeAsync
      ("USER_ID_HERE", taskCancellationToken).ConfigureAwait(false);

Upvotes: 2

Related Questions