Calendar .NET谷歌日历API v3:多帐户身份验证

Calendar .NET谷歌日历API v3:多帐户身份验证,calendar,google-api-dotnet-client,Calendar,Google Api Dotnet Client,我正在尝试编写一个程序,它将在CRM和几个Google帐户的Google日历之间同步事件 这是一个Windows控制台应用程序 对于每个Google帐户,我在API控制台中启用了日历API,创建了一个名为“UpdateSync”(type:native)的应用程序,然后下载了JSON文件 我使用帐户邮件地址(即email1@gmail.com.json) 接下来,我编写了下面的代码: /// <summary> /// Loads updated event from

我正在尝试编写一个程序,它将在CRM和几个Google帐户的Google日历之间同步事件

这是一个Windows控制台应用程序

对于每个Google帐户,我在API控制台中启用了日历API,创建了一个名为“UpdateSync”(type:native)的应用程序,然后下载了JSON文件

我使用帐户邮件地址(即email1@gmail.com.json)

接下来,我编写了下面的代码:

    /// <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;
        }

        // [...]
    }
//
///在上次同步日期后从Google日历加载更新的事件
/// 
///我们希望获得约会的用户
公共列表加载约会(用户)
{
Console.WriteLine(“LoadAppointments({0})”,user.Email;
列表约会=新列表();
用户凭证;
使用(var stream=newfilestream(string.Format(“credentials\\{0}.json”、user.Email)、FileMode.Open、FileAccess.Read))
{
尝试
{
credential=GoogleWebAuthorizationBroker.AuthorizationAsync(GoogleClientSecrets.Load(stream).Secrets,新[]{CalendarService.Scope.Calendar},“用户”,CancellationToken.None,新文件数据存储(“UpdayesSync.Credentials”)。结果;
}
捕获(例外e)
{
控制台写入线(e.Message);
返回null;
}
}
BaseClientService.Initializer Initializer=新的BaseClientService.Initializer();
initializer.HttpClientInitializer=凭证;
initializer.ApplicationName=“UpdateSync”;
服务=新日历服务(初始值设定项);
EventsResource.ListRequest req=service.Events.List(“主”);
req.TimeMin=DateTime.Now.AddMonths(-2).ToString(“o”);
req.showdeled=true;
req.updateMin=XmlConvert.ToString(LastSync,XmlDateTimeSerializationMode.Utc);
req.SingleEvents=true;
事件;
尝试
{
events=req.Execute();
}
捕获(例外e)
{
控制台写入线(e.Message);
返回null;
}
// [...]
}
这段代码是为每个JSON文件调用的

第一次调用时,要同步的帐户为“email1@gmail.com". Google Chrome自动关闭,并使用我自己的帐户自动登录(email2@gmail.com)并要求我允许我的程序访问日历API

我接受了

此弹出窗口不再显示

对任何帐户所做的任何更改都是在我自己的日历上完成的,而不是JSON文件所指的日历

怎么了?如何使用一个独特的程序访问多个Google帐户

这个程序将作为计划任务在服务器上运行,我不能打开任何窗口,因为没有人会查看服务器的屏幕。我不能要求用户对自己进行身份验证,因为没有用户


但是我可以在任何用户的Google帐户上做任何事情(启用API、创建密钥、授予应用程序等)

您应该为每个用户创建一个新的CalendarService(轻量级组件)。CalendarService和所有其他Google服务都实现为不可变的(为了简单和支持线程安全操作)

为每个用户创建一个新服务,并使用不同的userId(“user1”、“user2”等)作为GoogleWebAuthorizationBroker.AuthorizationAsync的参数

更新:

您还应该创建自己的流类:

然后呢,

将您对GoogleWebAuthorizationBroker的调用替换为以下代码:


你好据我所知,我就是这么做的:我有一个类“GoogleExtractor”,带有一个Load方法(源代码类似于上面的一个)。接下来,我有另一个类在循环中实例化GoogleExtractor对象,每个用户一个。我试图通过用户登录(GMail地址)在GoogleWebAuthorizationBroker.AuthorizationAsync()调用中删除“user”常量,但它没有改变任何东西:我总是在与我的Google Chrome关联的Google帐户上连接,即使我的程序不应该调用此帐户。@StringBuilder,在不需要任何api密钥生成的情况下,最好使用Google客户端sdk。对我来说,使用GoogleWebAuthorizationBroker与使用GoogleWebAuthorizationBroker没有任何不同。问题是,一旦我在浏览器中登录谷歌,即使指定了不同的用户ID/电子邮件,浏览器也只会显示我自己帐户的同意屏幕。没有提示指定的帐户。对于日历,唯一的解决方案似乎是使用服务帐户并与服务帐户共享日历(公平地说,这是谷歌建议的)更新-在最后的评论上展开。如果我在GoogleAuthorizationCodeRequestUrl中将LoginHint设置为电子邮件,那么它会有所帮助,但不会强制将其设置为该电子邮件。然后用户可以输入他们喜欢的任何内容。如果他们有一个已登录的现有帐户,他们可以单击该帐户。在我的例子中,这个问题是我可以看到它会引起一些用户的困惑。但更糟糕的是,如果用户电子邮件无效/未在Google上注册,它将只使用当前经过身份验证的用户。
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",
        };
    }
}
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);