Google API OAuth2,服务帐户,“错误”:“invalid_grant”

时间:2014-07-25 01:05:02

标签: oauth google-api google-calendar-api google-oauth google-api-dotnet-client

我正在尝试使用服务帐户将Dynamics CRM软件的日历同步到Google。 在此过程中,我遇到了关于.net的谷歌API缺乏文档,特别是关于授权。由于使用了过时的库和类,大多数Google样本都无法编译。

所以我找到了一些关于interned和receive错误的例子。 有人可以查看我的样本并说出我做错了什么吗?

准备步骤:

  1. 我在我的私人Google帐户中创建了一个项目。
  2. 在项目开发者控制台中,在APIS& AUTH - >证书,我生成了服务帐户。然后单击“生成P12键”并下载.p12文件。
  3. 在APIS& AUTH - > API,打开“Calendar API”
  4. 然后创建了控制台应用并设法安装OAuth和Calendar nuget包。有:

    1. Google API Auth Client Library,Google.Apis.Auth 1.8.1
    2. Google API客户端库,Google.Apis 1.8.1
    3. Google API核心客户端库,Id:Google.Apis.Core 1.8.1
    4. Google.APIs.Calendar.v3客户端库,Google.Apis.Calendar.V3 1.8.1.860
    5. 找到并根据我的需求调整代码:

      using System;
      using System.Security.Cryptography.X509Certificates;
      using Google.Apis.Calendar.v3;
      using Google.Apis.Auth.OAuth2;
      using Google.Apis.Services;
      
      namespace CrmToGoogleCalendar
      {
          class Program
          {
      
              static void Connect()
              {
                  var certificate = new X509Certificate2("My Project-ee7facaa2bb1.p12", "notasecret", X509KeyStorageFlags.Exportable);
      
      
                  var serviceAccountEmail = "506310960175-q2k8hjl141bml57ikufinsh6n8qiu93b@developer.gserviceaccount.com";
                  var userAccountEmail = "<my email>@gmail.com";
                  var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail) 
                      {
                          User = userAccountEmail,
                          Scopes = new[] { CalendarService.Scope.Calendar }
                      }
                      .FromCertificate(certificate));
      
                  var service = new CalendarService(new BaseClientService.Initializer()
                  {
                      ApplicationName = "Test calendar sync app",
                      HttpClientInitializer = credential
      
                  });
      
                  var calList = service.CalendarList.List().Execute().Items;
      
      
                  foreach (var cal in calList)
                  {
                      Console.WriteLine(cal.Id);
                  }
              }
      
      
              static void Main(string[] args)
              {
                  Connect();
              }
          }
      }
      

      我在应用和Fiddler中看到的与Google API的通信是:

      请求:

        

      主持人:HTTPS accounts.google.com,网址:/ o / oauth2 / token
        断言:长二进制字符串
        grant_type:urn:ietf:params:oauth:grant-type:jwt-bearer

      响应:

        

      HTTP / 1.1 400错误请求内容类型:application / json Cache-Control:   no-cache,no-store,max-age = 0,必须重新验证Pragma:no-cache   到期日:1990年1月1日星期五00:00:00 GMT日期:星期四,2014年7月24日06:12:18   GMT X-Content-Type-Options:nosniff X-Frame-Options:SAMEORIGIN   X-XSS-Protection:1; mode = block服务器:GSE备用协议:   443:quic Transfer-Encoding:chunked

           

      1f {“error”:“invalid_grant”} 0

      Fiddler screenshot

      请提前帮助和谢谢!

3 个答案:

答案 0 :(得分:7)

经过一些调查后发现,Google API无法按预期使用您的个人帐户@ gmail.com。您应该以Google @ your_organisation_domain

的格式在Google中拥有组织域帐户

然后,同样令人困惑的是,Google Drive API page上有文档, “委托您的服务帐户的域范围权限” 部分未提及在Calendar API页面。 该部分有7个步骤,需要完成。

BTW与个人帐户管理网站admin.google.com甚至无法使用。因此,使用@ gmail.com帐户执行这7个步骤是不可能的。

然后,当客户在 Google Apps管理控制台&gt;中获得授权时安全&gt;高级设置&gt;管理OAuth客户端访问代码开始工作。

有一个适合我的代码:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Calendar.v3;
using Google.Apis.Calendar.v3.Data;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;

namespace CrmToGoogleCalendar
{
    class Program
    {

        static void Connect()
        {
            Console.WriteLine("Calendar via OAuth2 Service Account Sample");

            var certificate = new X509Certificate2("My MC Project-3f38defdf4e4.p12", "notasecret", X509KeyStorageFlags.Exportable);
            var serviceAccountEmail = "795039984093-c6ab1mknpediih2eo9cb70mc9jpu9h03@developer.gserviceaccount.com";
            var userAccountEmail = "me@testdomain.com"; 
            var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail) 
                {
                    User = userAccountEmail,
                    Scopes = new[] { CalendarService.Scope.Calendar }
                }
                .FromCertificate(certificate));

            var service = new CalendarService(new BaseClientService.Initializer()
            {
                ApplicationName = "Test calendar sync app",
                HttpClientInitializer = credential

            });

            /* Get list of calendars */
            var calList = service.CalendarList.List().Execute().Items;
            var myCalendar = calList.First(@c => @c.Id == userAccountEmail);

            /* CREATE EVENT */
            var event1 = new Event()
                {
                    Kind = "calendar#event",
                    Summary = "Calendar event via API",
                    Description = "Programmatically created",
                    Status = "confirmed",
                    Organizer = new Event.OrganizerData() {
                        Email = userAccountEmail
                    },
                    Start = new EventDateTime()
                        {
                            DateTime = DateTime.Now.AddDays(1)
                        },
                    End = new EventDateTime()
                    {
                        DateTime = DateTime.Now.AddDays(1).AddHours(1)
                    },
                    ColorId = "6",
                    Reminders = new Event.RemindersData()
                        {
                            UseDefault = false,
                            Overrides = new List<EventReminder>(
                                new [] {
                                    new EventReminder()
                                        {
                                            Method = "popup",
                                            Minutes = 60
                                        }
                                })
                        }
                };

            event1 = service.Events.Insert(event1, myCalendar.Id).Execute();
            Console.WriteLine("Created event Id: {0}", event1.Id);


            /* ENLIST EVENTS */
            Console.WriteLine("calendar id={0}", myCalendar.Id);
            var events = service.Events.List(myCalendar.Id).Execute();
            foreach (var @event in events.Items)
            {
                Console.WriteLine("Event ID: {0}, ICalUID: {1}", @event.Id, @event.ICalUID);
                Console.WriteLine("  Name: {0}", @event.Summary);
                Console.WriteLine("  Description: {0}", @event.Description);
                Console.WriteLine("  Status: {0}", @event.Status);
                Console.WriteLine("  Color: {0}", @event.ColorId);
                Console.WriteLine("  Attendees: {0}", @event.Attendees == null ? "" : @event.Attendees.Select(a => a.Email).ToString());
                Console.WriteLine("  Kind: {0}", @event.Kind);
                Console.WriteLine("  Location: {0}", @event.Location);
                Console.WriteLine("  Organizer: {0}", @event.Organizer.Email);
                Console.WriteLine("  Recurrence: {0}", @event.Recurrence == null ? "no recurrence" : String.Join(",", @event.Recurrence));
                Console.WriteLine("  Start: {0}", @event.Start.DateTime == null ? @event.Start.Date : @event.Start.DateTime.ToString());
                Console.WriteLine("  End: {0}", @event.End.DateTime == null ? @event.End.Date : @event.End.DateTime.ToString());
                Console.WriteLine("  Reminders: {0}", @event.Reminders.UseDefault.Value ? "Default" : "Not defailt, " + 
                    (@event.Reminders.Overrides == null ? "no overrides" : String.Join(",", @event.Reminders.Overrides.Select(reminder => reminder.Method + ":" + reminder.Minutes)))
                    );
                Console.WriteLine("=====================");
            }

            Console.ReadKey();
        }


        static void Main(string[] args)
        {
            Connect();
        }
    }
}

产生的输出看起来如此:

Calendar via OAuth2 Service Account Sample
Created event Id: jkits4dnpq6oflf99mfqf1kdo0
calendar id=me@testdomain.com
Event ID: 1logvocs77jierahutgv962sus, ICalUID: 1logvocs77jierahutgv962sus@google.com
  Name: test event
  Description: test description2
  Status: confirmed
  Color: 
  Attendees: 
  Kind: calendar#event
  Location: location2
  Organizer: me@testdomain.com
  Recurrence: RRULE:FREQ=WEEKLY;BYDAY=TH
  Start: 2014-07-31
  End: 2014-08-01
  Reminders: Not defailt, email:10,popup:10
=====================
Event ID: 1logvocs77jierahutgv962sus_20140814, ICalUID: 1logvocs77jierahutgv962sus@google.com
  Name: test event updated
  Description: test description2
  Status: confirmed
  Color: 
  Attendees: 
  Kind: calendar#event
  Location: location2
  Organizer: me@testdomain.com
  Recurrence: no recurrence
  Start: 2014-08-14
  End: 2014-08-15
  Reminders: Not defailt, email:10
=====================
Event ID: 974hqdhh8jhv5sdobkggmdvvd8, ICalUID: 974hqdhh8jhv5sdobkggmdvvd8@google.com
  Name: One hour event
  Description: test description
  Status: confirmed
  Color: 7
  Attendees: 
  Kind: calendar#event
  Location: Meeting Room Hire, Broadway, 255 The Bdwy, Broadway, NSW 2007, Australia
  Organizer: me@testdomain.com
  Recurrence: no recurrence
  Start: 1/08/2014 10:00:00 AM
  End: 1/08/2014 11:00:00 AM
  Reminders: Default
=====================
Event ID: jkits4dnpq6oflf99mfqf1kdo0, ICalUID: jkits4dnpq6oflf99mfqf1kdo0@google.com
  Name: Calendar event via API
  Description: Programmatically created
  Status: confirmed
  Color: 6
  Attendees: 
  Kind: calendar#event
  Location: 
  Organizer: me@testdomain.com
  Recurrence: no recurrence
  Start: 2/08/2014 12:30:50 PM
  End: 2/08/2014 1:30:50 PM
  Reminders: Not defailt, popup:60
=====================

首次活动是每周一次的全日活动系列。 第二个是第一个更新的单个事件(具有相同的UID)。 第三是一小时的单一事件。 最后一个是由上面的代码创建的。

希望这能帮助他人节省时间。

答案 1 :(得分:0)

我不确定您的代码有什么问题我认为它是如何加载密钥文件的。也可能是因为服务帐户是您登录的用户,因此您无需使用 ServiceAccountCredential 发送用户。我不确定你为什么要发送某人的Gmail电子邮件。

using Google.Apis.Auth.OAuth2;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Calendar.v3;
namespace GoogleAnalytics.Service.Account
{
    class Program
    {
        static void Main(string[] args)
        {
            //Install-Package Google.Apis.Calendar.v3
            string serviceAccountEmail = "1046123799103-6v9cj8jbub068jgmss54m9gkuk4q2qu8@developer.gserviceaccount.com";
            var certificate = new X509Certificate2(@"C:\Users\HP_User\Documents\GitHub\Google-Analytics-dotnet-ServiceAccount\GoogleAnalytics.Service.Account\Diamto Test Everything Project-bc63fd995bd7.p12", "notasecret", X509KeyStorageFlags.Exportable);

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

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

            // define the new Calendar
            Google.Apis.Calendar.v3.Data.Calendar calendar = new Google.Apis.Calendar.v3.Data.Calendar();
            calendar.Description = "New Calendar";
            calendar.Summary = "New Calendar Summary";
            // Insert the Calendar
            service.Calendars.Insert(calendar).Execute();
            // List The Calendar
            var calList = service.CalendarList.List().Execute().Items;

        }
    }
}

此代码经过测试,它向您展示了如何为服务帐户创建初始日历。如果不创建日历,它将返回0个日历,您需要记住先创建一个日历。

答案 2 :(得分:0)

我遇到了同样的问题它在远程服务器上运行正常但是在本地服务器上我得到了那个错误,长时间的故事在经过几个小时的头痛后我发现问题来自我的系统时钟,只是通过这些步骤

<强> 1。点击系统中的时钟

  • 这会显示日历和时间

<强> 2。点击更改日期和时间设置...

  • 将出现日期和时间对话框

第3。单击“Internet时间”选项卡

<强> 4。点击更改设置

  • 将显示“Internet时间设置”对话框。

然后最终使用其中一个时间服务器更新您的时钟

相关问题