Как работает google-api в оффлайн запросе с токенами

Моя первая проблема заключается в том, что когда я создаю экземпляр Google_Client и устанавливаю все необходимые scopes, configs, access types и others, а затем генерирую URL-адрес для пользователя для AUTH с помощью $client->createAuthUrl();, я разрешаю доступ к моему приложению с учетной записью Google, которую я зарегистрирован. Все работает нормально, пока мне не нужно обновить токен.

Ниже я объясню об обновлении токена.

  $this->client = new Google_Client();
            $this->client->setAuthConfig(Storage::path('secret.json'));
            $this->client->setAccessType("offline");        // offline access
            $this->client->setIncludeGrantedScopes(true);   // incremental auth
            $this->client->addScope(Google_Service_Drive::DRIVE_METADATA_READONLY);
            $this->client->setRedirectUri(env('APP_URL') . '/login/google/callback');

Итак, следующее, что я сделал, это получить данные пользователя Google с помощью laravel-socialite. Вот данные, которые я получаю:

Код для запроса

  public function redirectToGoogleProvider()
    {
        $parameters = ['access_type' => 'offline'];

        return Socialite::driver('google')
            ->scopes(["https://www.googleapis.com/auth/drive"])
            ->with($parameters)
            ->redirect();
    }
User {#466 ▼
  +token: "ya29.GmPhBiSuYJ_oKsvDTUXrj3lJR5jlEYV4x8d0ZuYZLnDJgYL-uePT_KuUkIb-OQUiWBElhm6ljfac5QhDTXYK3qjWjje1vsnZsTAJ7pXyjNAVd2buZy0a6xZCTdnLdFNMKMDMXa6bGbA"
  +refreshToken: null
  +expiresIn: 3599
  +id: "101509757451285102354"
  +nickname: null
  +name: "My name"
  +email: "My email"
  +avatar: "https://lh3.googleusercontent.com/-Qsbr3VmcZ50/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rdS2HIl3LCv5EYCmvyXulG8n-Ap7w/mo/photo.jpg"
  +user: array:12 [▶]
  +"avatar_original": "https://lh3.googleusercontent.com/-Qsbr3VmcZ50/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rdS2HIl3LCv5EYCmvyXulG8n-Ap7w/mo/photo.jpg"
}

Здесь нет токена обновления. Я предполагаю, что это из-за того, как работает Google в автономный режим.

After a user grants offline access to the requested scopes, you can continue to use the API client to access Google APIs on the user's behalf when the user is offline. The client object will refresh the access token as needed.

Поэтому я устанавливаю токен, который я получаю из запроса и сохраняю в базе данных, в экземпляр Google_Client с помощью

$client->setAccessToken(Auth::user()->getUserInfo()->refresh_token)

Это работает. Но когда токен нужно обновить, он не обновляется. Вместо этого я получаю

"Invalid creditianals error".

Гугл говорит, что:

"The client object will refresh the access token as needed." But this is not happening.

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

Я попытался использовать CODE parameter, чтобы получить токен и обновить его.

$client->fetchAccessTokenWithAuthCode($code);

И получил эту ошибку:

array:2 [▼
  "error" => "invalid_request"
  "error_description" => "Could not determine client ID from request."
]

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Поиск нового уровня в Laravel с помощью MeiliSearch и Scout
Поиск нового уровня в Laravel с помощью MeiliSearch и Scout
Laravel Scout - это популярный пакет, который предоставляет простой и удобный способ добавить полнотекстовый поиск в ваше приложение Laravel. Он...
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
3
0
1 445
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы получите токен обновления только при первом запросе доступа пользователя. После этого Google предполагает, что вы сохранили токен обновления.

Попросите пользователя перейти в свою учетную запись Google и отозвать доступ, предоставленный этому клиентскому приложению.

Перейдите на страницу с приложениями, имеющими доступ к вашей учетной записи:

  • https://myaccount.google.com/u/0/разрешения.
  • В меню «Сторонние приложения» выберите свое приложение.
  • Нажмите «Удалить доступ», а затем нажмите «ОК» для подтверждения.

Затем попросите их снова аутентифицировать ваше приложение. В это время вы должны увидеть маркер обновления. Убедитесь, что вы сохранили его.

отозвать

вы также можете сделать

$client->revokeToken();

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

обновить доступ

Это мой код для обновления токена доступа ПРИМЕЧАНИЕ. Это PHP, я не разработчик larvel, он выглядит очень близко, вы должны быть в состоянии скрыть это, если он не работает из коробки.

/**
 * Authenticating to Google using Oauth2
 * Documentation:  https://developers.google.com/identity/protocols/OAuth2
 * Returns a Google client with refresh token and access tokens set. 
 *  If not authencated then we will redirect to request authencation.
 * @return A google client object.
 */
function getOauth2Client() {
    try {

        $client = buildClient();

        // Set the refresh token on the client. 
        if (isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']) {
            $client->refreshToken($_SESSION['refresh_token']);
        }

        // If the user has already authorized this app then get an access token
        // else redirect to ask the user to authorize access to Google Analytics.
        if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {

            // Set the access token on the client.
            $client->setAccessToken($_SESSION['access_token']);                 

            // Refresh the access token if it's expired.
            if ($client->isAccessTokenExpired()) {              
                $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
                $client->setAccessToken($client->getAccessToken()); 
                $_SESSION['access_token'] = $client->getAccessToken();              
            }           
            return $client; 
        } else {
            // We do not have access request access.
            header('Location: ' . filter_var( $client->getRedirectUri(), FILTER_SANITIZE_URL));
        }
    } catch (Exception $e) {
        print "An error occurred: " . $e->getMessage();
    }
}

Большое спасибо! Я получил токен обновления. Итак, теперь я буду хранить этот токен обновления, и каждый раз, когда он истечет, клиент будет получать новый токен, верно? Мне не нужно записывать время, которое истечет, чтобы взять новый токен и тому подобное?

Kristian Vasilev 04.04.2019 10:35

К сожалению, библиотека php так не работает. Вы должны передать ему токен обновления и попросить его получить новый. Я разместил некоторый код из одного из моих php-проектов, надеюсь, он поможет вам с larvel.

DaImTo 04.04.2019 11:17

Не забудьте принять ответ, если он вам помог. Это также повысит вашу репутацию.

DaImTo 04.04.2019 12:43

при подаче токена обновления я продолжаю получать сообщение «отсутствует ключ json в поле client_id в .../vendor/google/auth/src/Credentials/UserRefreshCredential‌​s.php:70», на удивление, последние пару месяцев все работало нормально. Как вы в итоге решили это?

Oliver White 08.03.2020 19:57

Позвольте мне поделиться тем, что я наконец придумал в Laravel, чтобы заставить его работать.

public static function getOauth2Client($o_user, $f_client) {
    try {
        $o_client = new Google_Client();
        $o_client - >setAuthConfig($f_client);
        $o_client - >setAccessType('offline');
        $o_client - >setIncludeGrantedScopes(true);
        $i_token_remaining = ($o_user - >gdrive_access_ttl - (time() - $o_user - >gdrive_access_created));
        $google_client_token = ['refresh_token' = >$o_user - >gdrive_refresh_token, 'access_token' = >$o_user - >gdrive_access_token, 'created' = >$o_user - >gdrive_access_created, 'expires_in' = >$i_token_remaining, ];
        $o_client - >setAccessToken(json_encode($google_client_token));

        #token validity time is less than 60 seconds
        if ($i_token_remaining < 60 || $o_client - >isAccessTokenExpired()) {
            $res = $o_client - >fetchAccessTokenWithRefreshToken($o_client - >getRefreshToken());
            $o_user - >gdrive_access_token = $res['access_token'];
            $o_user - >gdrive_access_created = time();
            $o_user - >gdrive_access_ttl = $res['expires_in'];
        }
        return $o_client;
    } catch(Exception $e) {
        print 'An error occurred: '.$e - >getMessage();
    }
}

По моему опыту:

  • setAccessToken не будет работать только с предоставленным токеном, для этого требуется от меня «expires_in»
  • $o_client->isAccessTokenExpired() всегда возвращает true, если только «создано» не предоставлено с $google_client_token, поскольку «создано» учитывается для истечения срока действия
  • isAccessTokenExpired проверяет за 30 секунд до истечения срока действия, поэтому мою 60-секундную проверку можно безопасно удалить.
  • $o_user - пользовательский объект, например. Пользователь:: найти ($ i_user_id);
  • токен обновления никогда не меняется, он устанавливается один раз и навсегда, это только токен доступа, который изменяется всякий раз, когда запрашивается обновление
  • ttl маркера доступа составляет 3600 — угадайте, почему?)) — поэтому, пожалуйста, сохраните и обновите токен доступа.
  • вне области этой функции я также обновляю свою БД после изменения токена доступа
User::updateOrCreate(
    ['email' => $o_user - > email],
    [
        'gdrive_access_token' => $o_user - > gdrive_access_token,
        'gdrive_access_created' => $o_user - > gdrive_access_created,
        'gdrive_access_ttl' => $o_user - > gdrive_access_ttl,
    ]);

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

Kristian Vasilev 17.06.2019 10:31

@KristianVasilev спасибо, что поделились. Я считаю, что это зависит от приложения. Мое приложение также использовало Google API в автономном режиме. Вот почему мне нужны были жетоны в первую очередь. Приложение всегда было включено, отправляя около 4 запросов в секунду. Каждый запрос токена является попаданием в Google API. Это влияет на ограничения использования API. Запрос маркеров обновления и доступа считается за 2 обращения. Итак, помимо использования как такового, моей целью было минимизировать количество запросов к API. Однако, если это не относится к вашему приложению, я согласен, чем проще, тем лучше. В любом случае нет необходимости запрашивать токен обновления, он не меняется для каждого пользователя.

Maksim Dzmitryew̆ 12.07.2020 11:53

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