API di Google OAuth2, account di servizio, “errore”: “invalid_grant”

Sto cercando di utilizzare l’account di servizio per sincronizzare i calendari dal software Dynamics CRM a Google. Durante questo ho dovuto affrontare la mancanza di documentazione su google API per .net, in particolare per quanto riguarda l’authorization. La maggior parte degli esempi di Google non può essere nemmeno compilata a causa di librerie e classi obsolete utilizzate.

Quindi ho trovato un esempio di internato e ricevo errori. Qualcuno potrebbe per favore guardare il mio campione e dire cosa sto sbagliando?

Fasi preparatorie:

  1. Ho creato un progetto nel mio account Google privato.
  2. Nella console di sviluppo del progetto, sotto APIS e AUTH -> Credenziali, ho generato l’account di servizio. Quindi fatto clic su “Genera P12 key” e scaricato il file .p12.
  3. Sotto APIS & AUTH -> API, triggersto “Calendar API”

Quindi ha creato l’app per la console ed è riuscita a installare i pacchetti di nuget OAuth e Calendar. Ci sono:

  1. Libreria client Auth di Google API, Google.Apis.Auth 1.8.1
  2. Libreria client API di Google, Google.Apis 1.8.1
  3. Libreria Core Client API Google, Id: Google.Apis.Core 1.8.1
  4. Google.APIs.Calendar.v3 Libreria client, Google.Apis.Calendar.V3 1.8.1.860

C’è un codice trovato e adattato alle mie esigenze:

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 = "[email protected]account.com"; var userAccountEmail = "@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(); } } } 

La comunicazione con l’API di Google che vedo nell’app e Fiddler è:

Richiesta :

Host: HTTPS accounts.google.com, URL: / o / oauth2 / token
Asserzione: lunga stringa binaria
grant_type: urn: ietf: params: oauth: grant-type: jwt-bearer

Risposta:

HTTP / 1.1 400 Contenuto della richiesta non valido: applicazione / json Cache-Control: no-cache, no-store, max-age = 0, must-revalidate Pragma: no-cache Scadenza: ven, 01 gen 1990 00:00: 00 GMT Data: Gio, 24 Jul 2014 06:12:18 GMT X-Content-Type-Options: nosniff X-Frame-Opzioni: SAMEORIGIN X-XSS-Protection: 1; mode = block Server: GSE Alternate-Protocol: 443: quic Transfer-Encoding: chunked

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

Screenshot Fiddler

Si prega di aiutare e grazie in anticipo!

Dopo alcune indagini, ho scoperto che l’API di Google non funziona come previsto con il tuo account personale @ gmail.com. Dovresti disporre di un account di dominio dell’organizzazione su Google in formato @ your_organisation_domain

Inoltre, ciò che è anche piuttosto confuso, è la documentazione nella pagina dell’API di Google Drive , con la sezione “Delega l’autorità a livello di dominio al tuo account di servizio” non menzionata nella pagina API di Calendar. Ci sono 7 passaggi nella sezione, devono essere eseguiti.

BTW con il sito di amministrazione degli account personali admin.google.com non è nemmeno disponibile. Quindi è imansible eseguire questi 7 passaggi con l’account @ gmail.com.

Quindi, quando il client è autorizzato in Admin Console di Google Apps> Sicurezza> Impostazioni avanzate> Gestisci accesso client OAuth, il codice inizia a funzionare.

C’è un codice che funziona per me:

 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 = "[email protected]account.com"; var userAccountEmail = "[email protected]"; 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( 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(); } } } 

L’output prodotto sembra così:

 Calendar via OAuth2 Service Account Sample Created event Id: jkits4dnpq6oflf99mfqf1kdo0 calendar [email protected] Event ID: 1logvocs77jierahutgv962sus, ICalUID: [email protected] Name: test event Description: test description2 Status: confirmed Color: Attendees: Kind: calendar#event Location: location2 Organizer: [email protected] 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: [email protected] Name: test event updated Description: test description2 Status: confirmed Color: Attendees: Kind: calendar#event Location: location2 Organizer: [email protected] Recurrence: no recurrence Start: 2014-08-14 End: 2014-08-15 Reminders: Not defailt, email:10 ===================== Event ID: 974hqdhh8jhv5sdobkggmdvvd8, ICalUID: [email protected] 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: [email protected] 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: [email protected] Name: Calendar event via API Description: Programmatically created Status: confirmed Color: 6 Attendees: Kind: calendar#event Location: Organizer: [email protected] Recurrence: no recurrence Start: 2/08/2014 12:30:50 PM End: 2/08/2014 1:30:50 PM Reminders: Not defailt, popup:60 ===================== 

Il primo evento è una serie di eventi settimanali giornalieri ricorrenti. Il secondo è l’evento singolo aggiornato del primo (ha lo stesso UID). Terzo è un singolo evento per un’ora. L’ultimo è creato dal codice sopra.

Spero che questo aiuti gli altri a risparmiare tempo.

Non sono sicuro di cosa c’è che non va nel tuo codice, penso che sia la parte in cui stai caricando il file chiave. Potrebbe anche essere il fatto che non è necessario inviare Utente con ServiceAccountCredential poiché l’account di servizio è l’utente a cui si sta effettuando l’accesso. Non sono sicuro del motivo per cui stai inviando email Gmail a qualcuno.

 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 = "[email protected]eaccount.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; } } } 

Questo codice è testato e mostra come creare il calendario iniziale per l’account di servizio. Senza creare un calendario restituirebbe 0 calendari, è necessario ricordarsi di crearne uno per primo.

Stavo avendo lo stesso problema stava funzionando bene sul server remoto, ma sul server locale ho avuto a che errore, storia lunga poco dopo ore di mal di testa ho scoperto il problema era dal mio orologio di sistema, basta passare attraverso questi passaggi

1. Fare clic sull’orologio nel sistema

  • Questo farà apparire il calendario e l’ora

2. Fai clic su Modifica impostazioni data e ora …

  • Apparirà la finestra di dialogo Data e ora

3. Fare clic sulla scheda Ora Internet

4. Fare clic su Cambia impostazioni

  • Apparirà la finestra di dialogo Impostazioni ora Internet.

quindi finalmente aggiorna il tuo orologio con uno dei time server