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

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

Я попытался захватить свойство "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);
    }
}

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

Общая идея заключается в следующем:

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

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

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

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

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

Смотрите также:

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

Это решение предполагает, что у вас уже есть идентификатор клиента, секрет клиента и токен обновления. Обратите внимание, что вам не нужно вводить токен доступа в коде. (Короткоживущий код доступа получен "под капотом" с сервера 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;
    }
}

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

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

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

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

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

Другие вопросы по тегам