Google+ API: как использовать RefreshTokens, чтобы не запрашивать доступ каждый раз при запуске моего приложения?

Я пытаюсь использовать API Google+ для доступа к информации для аутентифицированного пользователя. Я скопировал некоторый код из одного из примеров, который отлично работает (ниже), однако у меня возникли проблемы с его работой, чтобы я мог повторно использовать токен при запуске приложений.

Я попытался захватить свойство «RefreshToken» и использовать provider.RefreshToken() (среди прочего) и всегда получаю ответ 400 Bad Request.

Кто-нибудь знает, как это сделать, или знает, где я могу найти образцы? сайт Google Code, похоже, не распространяется на это: -(

class Program
{
    private const string Scope = "https://www.googleapis.com/auth/plus.me";

    static void Main(string[] args)
    {
        var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description);
        provider.ClientIdentifier = "BLAH";
        provider.ClientSecret = "BLAH";
        var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthentication);

        var plus = new PlusService(auth);
        plus.Key = "BLAH";
        var me = plus.People.Get("me").Fetch();
        Console.WriteLine(me.DisplayName);
    }

    private static IAuthorizationState GetAuthentication(NativeApplicationClient arg)
    {
        // Get the auth URL:
        IAuthorizationState state = new AuthorizationState(new[] { Scope });
        state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
        Uri authUri = arg.RequestUserAuthorization(state);

        // Request authorization from the user (by opening a browser window):
        Process.Start(authUri.ToString());
        Console.Write("  Authorization Code: ");
        string authCode = Console.ReadLine();
        Console.WriteLine();

        // Retrieve the access token by using the authorization code:
        return arg.ProcessUserAuthorization(authCode, state);
    }
}

person Danny Tuppeny    schedule 17.09.2011    source источник
comment
Вы видите ошибку, даже если токен обновления используется до истечения срока действия? Если да, возможно ли, что вы пытаетесь использовать один и тот же токен обновления более одного раза?   -  person Chris Sears    schedule 20.09.2011
comment
Я действительно не знаю, как я должен его использовать - я просто случайным образом брал токен Refresh из запроса, а затем использовал метод RefreshToken. На самом деле я не могу найти никакой документации о том, как это должно работать, поэтому, возможно, я использовал его неправильно. Вот и пытаюсь разобраться :-(   -  person Danny Tuppeny    schedule 20.09.2011
comment
@DannyTuppeny Вы решили это? Если нет, то вы видели мой ответ? У меня была та же проблема, что и у вас, и я не нашел хорошей документации в Google, но я думаю, что решил проблему.   -  person poplitea    schedule 30.08.2012
comment
@poplitea Я видел ваш ответ (выглядит хорошо), но у меня не было возможности попробовать его (или другие здесь) :(   -  person Danny Tuppeny    schedule 30.08.2012


Ответы (5)


Вот пример. Обязательно добавьте строковый параметр RefreshToken и ссылку на System.Security или найдите другой способ безопасного хранения токена обновления.

    private static byte[] aditionalEntropy = { 1, 2, 3, 4, 5 };

    private static IAuthorizationState GetAuthorization(NativeApplicationClient arg)
    {
        // Get the auth URL:
        IAuthorizationState state = new AuthorizationState(new[] { PlusService.Scopes.PlusMe.GetStringValue() });
        state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);

        string refreshToken = LoadRefreshToken();
        if (!String.IsNullOrWhiteSpace(refreshToken))
        {
            state.RefreshToken = refreshToken;

            if (arg.RefreshToken(state))
                return state;
        }

        Uri authUri = arg.RequestUserAuthorization(state);

        // Request authorization from the user (by opening a browser window):
        Process.Start(authUri.ToString());
        Console.Write("  Authorization Code: ");
        string authCode = Console.ReadLine();
        Console.WriteLine();

        // Retrieve the access token by using the authorization code:
        var result = arg.ProcessUserAuthorization(authCode, state);

        StoreRefreshToken(state);
        return result;
    }

    private static string LoadRefreshToken()
    {
        return Encoding.Unicode.GetString(ProtectedData.Unprotect(Convert.FromBase64String(Properties.Settings.Default.RefreshToken), aditionalEntropy, DataProtectionScope.CurrentUser));
    }

    private static void StoreRefreshToken(IAuthorizationState state)
    {
        Properties.Settings.Default.RefreshToken = Convert.ToBase64String(ProtectedData.Protect(Encoding.Unicode.GetBytes(state.RefreshToken), aditionalEntropy, DataProtectionScope.CurrentUser));
        Properties.Settings.Default.Save();
    }
person Lars Truijens    schedule 20.09.2011
comment
Спасибо, это очень помогло. Я также использовал класс Google StoredStateClient из примера по этой ссылке, поэтому я могу получить информацию о пользователе с тем же ответом авторизации. developers.google.com/drive/credentials - person IPValverde; 01.11.2012

Общая идея такова:

  1. Вы перенаправляете пользователя на конечную точку авторизации Google.

  2. Вы получаете недолговечный код авторизации.

  3. Вы немедленно обмениваете код авторизации на токен доступа с длительным сроком действия, используя конечную точку токена Google. Токен доступа поставляется с датой истечения срока действия и токеном обновления.

  4. Вы делаете запросы к API Google, используя токен доступа.

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

См. также:

person dtb    schedule 17.09.2011
comment
См. stackoverflow.com/questions/7454930/ - person Lars Truijens; 20.09.2011

У меня также были проблемы с работой «автономной» аутентификации (т.е. получение аутентификации с помощью токена обновления), и я получил HTTP-ответ 400 Bad request с кодом, аналогичным коду OP. Однако я заставил его работать со строкой client.ClientCredentialApplicator = ClientCredentialApplicator.PostParameter(this.clientSecret); в Authenticate-методе. Это необходимо для получения работающего кода — я думаю, что эта строка заставляет clientSecret отправляться на сервер в качестве POST-параметра (вместо основного HTTP-параметра аутентификации).

Это решение предполагает, что у вас уже есть идентификатор клиента, секрет клиента и токен обновления. Обратите внимание, что вам не нужно вводить токен доступа в код. (Кратковременный код доступа получается "под капотом" с сервера Google при отправке долгоживущего токена обновления со строкой client.RefreshAuthorization(state);. Этот токен доступа хранится как часть переменной auth, откуда он используется для авторизации API-вызовов «под капотом».)

Пример кода, который работает для меня с Google API v3 для доступа к моему календарю Google:

class SomeClass
{

    private string clientID         = "XXXXXXXXX.apps.googleusercontent.com";
    private string clientSecret     = "MY_CLIENT_SECRET";
    private string refreshToken     = "MY_REFRESH_TOKEN";
    private string primaryCal       = "MY_GMAIL_ADDRESS";

    private void button2_Click_1(object sender, EventArgs e)
    {
        try
        {
            NativeApplicationClient client = new NativeApplicationClient(GoogleAuthenticationServer.Description, this.clientID, this.clientSecret);
            OAuth2Authenticator<NativeApplicationClient> auth = new OAuth2Authenticator<NativeApplicationClient>(client, Authenticate);

            // Authenticated and ready for API calls...

            // EITHER Calendar API calls (tested):
            CalendarService cal = new CalendarService(auth);
            EventsResource.ListRequest listrequest = cal.Events.List(this.primaryCal);
            Google.Apis.Calendar.v3.Data.Events events = listrequest.Fetch();
            // iterate the events and show them here.

            // OR Plus API calls (not tested) - copied from OP's code:
            var plus = new PlusService(auth);
            plus.Key = "BLAH";  // don't know what this line does.
            var me = plus.People.Get("me").Fetch();
            Console.WriteLine(me.DisplayName);

            // OR some other API calls...
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error while communicating with Google servers. Try again(?). The error was:\r\n" + ex.Message + "\r\n\r\nInner exception:\r\n" + ex.InnerException.Message);
        }
    }

    private IAuthorizationState Authenticate(NativeApplicationClient client)
    {
        IAuthorizationState state = new AuthorizationState(new string[] { }) { RefreshToken = this.refreshToken };

        // IMPORTANT - does not work without:
        client.ClientCredentialApplicator = ClientCredentialApplicator.PostParameter(this.clientSecret);

        client.RefreshAuthorization(state);
        return state;
    }
}
person poplitea    schedule 27.08.2012
comment
Я не могу это скомпилировать - версия API DLL, которую я только что скачал, не имеет ClientCredentialApplicator в NativeWebApplication (у меня даже нет возможности найти секретный ключ в консоли API?!) - person Danny Tuppeny; 01.09.2012
comment
В вашем классе должен быть using DotNetOpenAuth.OAuth2;, а также вы должны загрузить DotNetOpenAuth отсюда: dotnetopenauth.net . Secret Key называется Client Secret (если его там нет, может быть, вам нужно еще раз добавить свой клиент в консоль API?) - person poplitea; 02.09.2012
comment
Я действительно получил эту работу, используя ответ Ларса Труйенса; с первого раза получилось отлично :-) - person Danny Tuppeny; 06.09.2012
comment
@DannyTuppeny Отлично :-) Но вам приходилось использовать строку client.ClientCredentialApplicator = ClientCredentialApplicator.PostParameter(this.clientSecret);? - person poplitea; 06.09.2012
comment
я этого не делал; Я просто взял код из быстрого запуска Google Диска (developers.google.com/drive/quickstart). ) затем вызвал материал RefreshToken, который Ларс добавил в свой ответ; могу вкратце изложить, если будет полезно - person Danny Tuppeny; 06.09.2012
comment
@DannyTuppeny Хм, ладно, эта строка решила мою проблему. Не знаю, чем мой код без этой строки отличается от кода Ларса, за исключением сохранения/извлечения секрета, а не его жесткого кодирования в коде. Может быть, моя проблема была вызвана кодировкой символов? Я вижу, что Ларс работает с Unicode... В любом случае, хорошо, что ваша проблема решена. - person poplitea; 06.09.2012
comment
Мой код такой же, как в примере с Диска, но изменен на G+ и использует методы RefreshToken в ответе Ларса (на самом деле я не копировал код из него). Вот суть моего полного кода: gist.github.com/3658795 - person Danny Tuppeny; 06.09.2012
comment
Это решение сработало для меня. Раньше я получал ошибку при отправке прямого сообщения или получении ответа 404 Bad Request error. Спасибо. - person Gautam Jain; 29.11.2013

Спецификация OAuth 2.0 еще не завершена, и существует небольшое количество реализаций спецификаций для различных клиентов и служб, которые вызывают появление этих ошибок. Скорее всего, вы все делаете правильно, но используемая вами версия DotNetOpenAuth реализует другой проект OAuth 2.0, чем Google в настоящее время реализует. Ни одна из частей не является «правильной», так как спецификация еще не завершена, но это делает совместимость чем-то вроде кошмара.

Вы можете проверить, что используемая вами версия DotNetOpenAuth является последней (в случае, если это поможет, что может), но в конечном итоге вам, возможно, придется либо сидеть сложа руки, пока не будут завершены спецификации и все их реализуют. правильно или самостоятельно прочитайте документы Google (которые предположительно описывают их версию OAuth 2.0) и реализуйте ту, которая специально нацелена на их черновую версию.

person Andrew Arnott    schedule 18.09.2011

Я бы порекомендовал посмотреть проект «SampleHelper» в решении «Примеры» клиентского API Google .NET:

В этом файле показано, как использовать защищенные данные Windows для хранения токена обновления, а также показано, как использовать локальный сервер замыкания на себя и различные методы для захвата кода доступа вместо того, чтобы пользователь вводил его вручную.

Один из примеров в библиотеке, использующих этот метод авторизации, можно найти ниже:

person Matthias Linder    schedule 21.09.2011