it-swarm.dev

Google API OAuth2, conta de serviço, "error": "invalid_grant"

Estou tentando usar a conta de serviço para sincronizar calendários do software Dynamics CRM para o Google. Durante esse período, enfrentei falta de documentação no google API for .net, especialmente em relação à autorização. A maioria das amostras do Google não pode ser compilada por causa de bibliotecas e classes desatualizadas usadas.

Então eu encontrei algum exemplo sobre internado e recebo erro. Alguém poderia por favor olhar na minha amostra e dizer o que estou fazendo errado?

Etapas preparatórias:

  1. Eu criei um projeto na minha conta do Google privada.
  2. No console do desenvolvedor do projeto, em APIS & AUTH -> Credentials, geramos a conta de serviço. Em seguida, clicou em "Gerar chave P12" e baixei o arquivo .p12.
  3. Em APIS & AUTH -> APIs, ativada "Calendar API"

Em seguida, criei o aplicativo de console e consegui instalar os pacotes nuget OAuth e Calendar. Tem:

  1. Biblioteca de clientes de autenticação de APIs do Google, Google.Apis.Auth 1.8.1
  2. Biblioteca de clientes de APIs do Google, Google.Apis 1.8.1
  3. Biblioteca de clientes principais das APIs do Google, Id: Google.Apis.Core 1.8.1
  4. Biblioteca cliente Google.APIs.Calendar.v3, Google.Apis.Calendar.V3 1.8.1.860

Existe um código encontrado e adaptado às minhas necessidades:

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 = "<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();
        }
    }
}

A comunicação com a API do Google, que vejo no aplicativo e no Fiddler, é:

Pedido:

Anfitrião: HTTPS accounts.google.com, URL:/o/oauth2/token
Afirmação: longa cadeia binária
grant_type: urn: ietf: parâmetros: oauth: grant-type: jwt-portador

Resposta:

HTTP/1.1 400 Pedido incorreto Tipo de conteúdo: aplicativo/json Cache-Control: Sem cache, sem armazenamento, max-age = 0, deve-revalidar Pragma: no-cache Expira : Sex, 01 Jan 1990 00:00:00 GMT Data: quinta, 24 de julho de 2014 06:12:18 GMT Opções X-Content-Type: nosniff X-Frame-Opções: SAMEORIGIN X-XSS-Protection: 1; mode = block Servidor: Protocolo alternativo de GSE: 443: quic Codificação de transferência: em pedaços

1f {"error": "invalid_grant"} 0

Fiddler screenshot

Por favor, ajudem e obrigado antecipadamente!

12
Dimitry

Depois de algumas investigações, descobri que a API do Google não funciona como esperado com sua conta pessoal @ gmail.com. Você deve ter uma conta de domínio da organização no Google no formato you @ your_organisation_domain

Então, o que também é bastante confuso, há documentação na página da API do Google Drive , com"Delegar autoridade de domínio à sua conta de serviço"seção não mencionada no Google Agenda Página da API. Há 7 etapas na seção, são necessárias para ser feito. 

BTW com o site de administração de conta pessoal admin.google.com está indisponível. Portanto, é impossível realizar essas 7 etapas com a conta @ gmail.com. 

Então, quando o cliente tiver autorização em Admin Console do Google Apps> Segurança> Configurações avançadas> Gerenciar acesso ao Cliente OAuth o código começa a funcionar. 

Existe um código que funciona para mim:

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<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();
        }
    }
}

A saída produzida parece assim:

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
=====================

O primeiro evento é uma série recorrente semanal de dias inteiros. O segundo é um único evento atualizado do primeiro (tem o mesmo UID). Terceiro é um evento único por uma hora. é criado pelo código acima.

Espero que isso ajude os outros a economizar tempo.

6
Dimitry

eu estava tendo o mesmo problema que estava funcionando bem no servidor remoto, mas no servidor local eu cheguei a esse erro, longa história curta depois de horas de dor de cabeça eu descobri problema foi do meu relógio do sistema, basta passar por estes passo 

1. Clique no relógio no seu sistema

  • Isso trará o calendário e o tempo 

2. Clique em Alterar configurações de data e hora ...

  • A caixa de diálogo Data e hora será exibida

3. Clique na guia Horário da Internet

4. Clique em Alterar configurações

  • A caixa de diálogo Configurações de horário da Internet será exibida.

então finalmente atualize seu relógio com um dos servidores de tempo

0
Abolfazl

Eu não tenho certeza do que está errado com o seu código Eu acho que é parte de como você está carregando o arquivo de chave. Também pode ser o fato de que você não precisa enviar User com ServiceAccountCredential , já que a conta de serviço é o Usuário no qual você está efetuando login. Não sei por que você está enviando emails do Gmail para alguém. 

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;

        }
    }
}

Este código é testado e mostra como criar o calendário inicial para a conta de serviço. Sem criar um calendário, ele retornaria 0 calendários, você precisa lembrar de criar um calendário primeiro.

0
DaImTo