Доступ к старым API-интерфейсам GData (API-интерфейсам) с использованием OAuth 2 и учетной записи службы

Короткий вопрос: возможно ли это, и если да, то как?

Структура

У меня есть приложение .NET, которое в настоящее время использует учетную запись службы для доступа к информации в домене Google Apps с помощью API Google Диска. Это отлично работает с помощью google-api-dotnet-client library и кода в тех же строках как показано в примерах здесь - которые в настоящее время являются очень хорошим базовым примером того, что я делаю.

Теперь я хочу расширить его так же, как используя эти API, предоставленные в "новой" библиотеке google-api-dotnet-client, которая использует более старые библиотеки "GData", как это предусмотрено старше google-gdata library, в частности API таблиц (и, возможно, еще больше).

Проблема

Здесь возникает трудность. Бывшая библиотека делает именно то, что я хочу, о чем свидетельствует вторая ссылка в первом абзаце выше, - и тот факт, что я ее сам делаю. ОДНАКО... хотя вторая библиотека была обновлена ​​для поддержки OAuth 2.0 в дополнение к OAuth 1.0 и другим старым методам auth, это не так, насколько я могу судить по обширным сайтам Googling и trail- и-error - разрешить операцию "учетная запись службы от имени всех моих пользователей", которая мне нужна.

Мой вопрос заключается в том, что я что-то упустил (возможно, трудно найти или не документировал что-то), что позволило бы мне делать то, что я хочу. В противном случае, могу ли я заставить это поведение и заставить эти две библиотеки работать бок о бок?

Идеальное решение

В идеале мне бы хотелось, чтобы экземпляр Google.GData.Spreadsheets.SpreadsheetsService мог использовать экземпляр Google.Apis.Authentication.Auth2Authenticator<AssertionFlowClient>, который я уже использую... как-то. Возможно ли такое колдовство? Я пропущу очевидное?

В противном случае я с удовольствием сделаю весь "клиент потока утверждения" OAuth2 "снова", если мне придется, в некотором роде, справиться с более старой библиотекой.

Справка

Другие мысли

Я рассмотрел и отклонил на данный момент - возможность начать с нуля и написать свою собственную библиотеку, чтобы это произошло. Это происходит по двум причинам:

  • Библиотека gdata уже существует и была разработана многими людьми, более умными, чем я. Я не настолько высокомерен, что считаю, что могу сделать лучше.
  • Я не уверен, что OAuth2 с подходом к учетной записи службы поддерживается даже на этих более старых API-интерфейсах.

Альтернативный подход, который я надеялся избежать, но, возможно, придется отпасть в зависимости от ответов здесь, будет использовать двухсторонний OAuth 1.0 для части этого. Я бы предпочел не делать этого, поскольку части приложения полагаются на один старый метод auth, в то время как другие части делают это, хороший новый способ просто мне не нравится. И там гораздо больше, чтобы пойти не так...


Обновление

Я рассмотрел возможность подкласса GDataRequestFactory и GDataRequest, чтобы я мог сделать свой собственный запрос factory, и у него есть экземпляр Google.Apis.Authentication.Auth2Authenticator<AssertionFlowClient> (ну, экземпляр Google.Apis.Authentication.IAuthenticator в любом случае), который мог бы выполнить аутентификацию запрос перед его вызовом. Однако... конструктор для GDataRequest является внутренним, что остановило меня.

Это похоже на то, что это не должно быть.

Ответ 1

Для других людей, сталкивающихся с этим вопросом (теперь, когда решение, связанное с принятым ответом, использует устаревший код), вот как я его решил:

Сначала начните с земли "нового API" (используйте пакет Google.Apis.Auth nuget), настроив ServiceAccountCredential после Google Пример учетной записи службы:

//In the old api, this accessed the main api accounts' sheets, not anymore
//** Important ** share spreadsheets with the Service Account by inviting the "serviceAccountEmail" address to the sheet
string serviceAccountEmail = "[email protected]";

var certificate = new X509Certificate2(@"key.p12", "notasecret", X509KeyStorageFlags.Exportable);

ServiceAccountCredential credential = new ServiceAccountCredential(
   new ServiceAccountCredential.Initializer(serviceAccountEmail)
   {
       Scopes = new[] { "https://spreadsheets.google.com/feeds", "https://docs.google.com/feeds" }
   }.FromCertificate(certificate));

Скажите учетным данным, чтобы запросить токен доступа:

credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Wait();

Теперь пришло время вернуться на "старый интерфейс API" (используйте пакет Google.GData.Spreadsheets nuget). Начните с построения SpreadsheetsService (похоже на Google пример):

SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");

Чтобы использовать аутентификацию учетной записи службы, мы создадим экземпляр GDataRequestFactory и настроим пользовательский заголовок Authorization:

var requestFactory = new GDataRequestFactory("My App User Agent");
requestFactory.CustomHeaders.Add(string.Format("Authorization: Bearer {0}", credential.Token.AccessToken));

Наконец, установите для свойства SpreadsheetsService RequestFactory этот новый factory:

service.RequestFactory = requestFactory;

И идите и используйте SpreadsheetsService, как и вы, с помощью любой другой техники. ( Подсказка: обмениваются электронными таблицами с учетной записью службы, приглашая адрес serviceAccountEmail на лист)

Ответ 2

Мне удалось решить это путем подкласса GDataRequestFactory и создания моей собственной реализации интерфейсов, реализованных GDataRequest. Эта реализация завершает экземпляр GDataRequest, созданный с помощью отражения, и добавляет в необходимый код для выполнения проверки подлинности с использованием экземпляра IAuthenticator (в моем случае Auth2Authenticator).

Я написал сообщение в блоге и добавил пример как Gist:

Не стесняйтесь использовать это, если это вам поможет (лицензия BSD).

Ответ 3

Эй просто споткнулся по той же проблеме и произвел другое решение:

Кто-нибудь когда-либо обращался к написанию параметров из объекта credentials непосредственно в OAuth2Parameters-Object?

Я сделал это, и он работал красиво:

public class OAuthTest
{  
    OAuth2Parameters param = new OAuth2Parameters();

    public OAuthTest()
    {
        Debug.WriteLine("Calling: AuthGoogleDataInterface()");
        bool init = AuthGoogleDataInterface();
        if (init)
        {
            GOAuth2RequestFactory requestFactory = new GOAuth2RequestFactory(null, "My App User Agent", this.param);
            //requestFactory.CustomHeaders.Add(string.Format("Authorization: Bearer {0}", credential.Token.AccessToken));
            var service = new SpreadsheetsService("MyService");
            service.RequestFactory = requestFactory;
            SpreadsheetQuery query = new SpreadsheetQuery();

            // Make a request to the API and get all spreadsheets.
            SpreadsheetFeed feed = service.Query(query);

            // Iterate through all of the spreadsheets returned
            foreach (SpreadsheetEntry entry in feed.Entries)
            {
                // Print the title of this spreadsheet to the screen
                Debug.WriteLine(entry.Title.Text);
            }
        }
        Debug.WriteLine(m_Init);
    }

    private bool AuthGoogleDataInterface()
    {
        bool b_success;
        try
        {
            Console.WriteLine("New User Credential");
            // New User Credential
            UserCredential credential;
            using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
            {
                GoogleClientSecrets GCSecrets = GoogleClientSecrets.Load(stream);
                string[] ArrScope = new[] { "https://spreadsheets.google.com/feeds", "https://docs.google.com/feeds" };
                credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GCSecrets.Secrets,
                    ArrScope,
                    "user", CancellationToken.None,
                new FileDataStore("My.cal")).Result;
                // put the Information generated for the credentials object into the OAuth2Parameters-Object to access the Spreadsheets
                this.param.ClientId = GCSecrets.Secrets.ClientId; //CLIENT_ID;
                this.param.ClientSecret = GCSecrets.Secrets.ClientSecret; //CLIENT_SECRET;
                this.param.RedirectUri = "urn:ietf:wg:oauth:2.0:oob"; //REDIRECT_URI;
                this.param.Scope = ArrScope.ToString();
                this.param.AccessToken = credential.Token.AccessToken;
                this.param.RefreshToken = credential.Token.RefreshToken;
            }

            Debug.WriteLine("AuthGoogleDataInterface: Success");
            b_success = true;
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.ToString());
            b_success = false;
        }
        return b_success;
    }
}