Вход в Google для веб-сайтов, вызывающий "Подтверждение подписи" с библиотекой JWT PHP

Подтверждение Google в Интернете привело меня к сумасшедшему...

Я создаю простое веб-приложение, и я пытаюсь интегрировать функцию входа Google на веб-сайт (https://developers.google.com/identity/sign-in/web/).

JavaScript, похоже, прошел достаточно хорошо, и следующим шагом было проверить id_token, который я получал на моем бэкэнд-сервере (опять же, против рекомендации Google: https://developers.google.com/identity/sign-in/web/backend-auth).

Это веб-приложение на основе PHP, и я успешно установил библиотеку API клиента Google с помощью композитора: composer require google/apiclient, но при отправке значения моего id_token в мою внутреннюю систему PHP я постоянно получаю следующую ошибку

Firebase\JWT\SignatureInvalidException
File: .\vendor\firebase\php-jwt\src\JWT.php:112 
Message: Signature verification failed

Stack trace:
#0 .\vendor\google\apiclient\src\Google\AccessToken\Verify.php(103): Firebase\JWT\JWT::decode('eyJhbGciOiJSUzI...', '-----BEGIN PUBL...', Array)
#1 .\vendor\google\apiclient\src\Google\Client.php(712): Google_AccessToken_Verify->verifyIdToken('eyJhbGciOiJSUzI...', '10...')

Я также использовал значение id_token в конечной точке "tokeninfo" Google (https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=ABC123), а id_token проверяет отлично, поэтому я уверен, что это не значение id_token, которое неправильно. Он также отлично передает его через переменную POST в PHP скрипт, поэтому я немного теряю.

Здесь мой код:

JavaScript:

<script src="https://apis.google.com/js/platform.js?onload=googleAppStart" async defer></script>
<script>
    var googleAppStart  = function(){gapi.load('auth2', initGoogleSignIn);};
    var auth            = false;
    function initGoogleSignIn(){
        auth = gapi.auth2.init({
            client_id   : 'client-id-is-here',
            scope       : 'profile'
        });

        auth.attachClickHandler(document.getElementById('my-button'));
        auth.isSignedIn.listen(googleSignInChanged);
        auth.currentUser.listen(googleCurrentUserChanged);

        if (auth.isSignedIn.get() == true)
            auth.signIn();
    }

    function googleSignInChanged(signed_in){}
    function googleCurrentUserChanged(user){
        var auth_response   = user.getAuthResponse();
        var id_token        = auth_response.id_token;
        if (id_token != undefined){
            var url     = '/verify-google-signin';
            var params  = {id_token: id_token};
            jQuery.post(url, params, function(data){}, 'json');
        }
    }
</script>

... и мой PHP поймает POST:

<?php

    require_once '/vendor/autoload.php';

    $credentials = array("client_id" => "client-id-is-here");
    $client = new \Google_Client($credentials);

    $data = $_POST;
    if (isset($data['id_token'])) {

        $id_token = trim($data['id_token']);

        // Exception generated here...
        $payload = $client->verifyIdToken($id_token);
    }

?>

Большое вам спасибо за то, что нашли время, чтобы прочитать это и за любую помощь! Он очень благодарен!

Ответ 1

У меня была такая же проблема сегодня.

Легче, если вы просто выполните:

composer require firebase/php-jwt:4.0

Ответ 2

Это проблема с php-jwt. Последняя версия не работает с клиентом Google Api.

Попробуйте использовать версию php-jwt версии 4.

Я положил "firebase/php-jwt": "< 5.0" в мой файл composer.json.

Работал как шарм!

Ответ 3

У меня проблемы с проверкой подлинности входа в систему Google вчера, используя как google/apiclient 2.2.0, так и 2.1.3.

tl; dr это, скорее всего, сбои на стороне Google или некоторые неясные ограничения, о которых я не знаю (ничего в консоли разработчика об этом не было).

Во-первых, "idToken", который Google предоставлял мне на стороне клиента, не был действительным JWT: openssl_verify() отклонял его в Firebase\JWT\JWT, бросая Firebase\JWT\SignatureInvalidException. Я последовал вашим советам, установил google/apiclient 2.1.3, и это исключение больше не выбрасывалось, но полученная полезная нагрузка была нулевой (поэтому idToken все еще недействителен).

Несколько часов спустя, я испытал периодические результаты с apiclient 2.3.0: иногда токен был недействителен проверкой подписи (и выбрасывал исключение подписи), а иногда токен был криптографически действителен, но возвращаемая полезная нагрузка была нулевой. Время от времени маркер был действительным (!).

В конце концов, аутентификационный процесс бэкэнд выполнялся каждый раз.

По мере того как я начал испытывать эти проблемы, я попытался исправить это, создав новые ключи OAuth2, вернусь к предыдущим версиям моей кодовой базы (как на стороне сервера, так и на стороне клиента), которые, как я знал, работали, удалили все данные браузера и попытались чтобы получить токен на Кордове с помощью входа для Android. Ничего не получилось. Также нет сообщений в Консоли разработчика, никаких видимых ограничений, нет электронной почты безопасности.

Если это не ошибка, а функция, обработка ошибок довольно жесткая:)

Ответ 4

К счастью, вы можете проверить id_token без библиотеки google, как описано здесь https://developers.google.com/identity/sign-in/web/backend-auth#calling-the-tokeninfo-endpoint

    if (isset($data['id_token'])) {
        $id_token = trim($data['id_token']);
        try {
            $res = (new \GuzzleHttp\Client())->request('GET',
                'https://www.googleapis.com/oauth2/v3/tokeninfo', [
                    'query' => ['id_token' => $id_token],
                ]);
            $payload = json_decode($res->getBody()->getContents(), true);
            //you still need to check that the aud claim contains one of your app client IDs
            if ($payload['aud'] != "client-id-is-here") {
                throw new \Exception("App Isn't valid", 422);
            }

        } catch (RequestException $e) {
            //IF token isn't valid you will be here
            if ($e->hasResponse()) {
                /** @var \GuzzleHttp\Psr7\Response $resp */
                $resp = $e->getResponse();
                $error = json_decode($resp->getBody()->getContents(), true)['error_description'];
                throw new \Exception($error, $resp->getStatusCode());
            }
        }
    }

Если у вас нет исключений, ваш токен действителен

Ответ 5

Это было исправлено в v2.2.1 google/apiclient, поэтому убедитесь, что вы используете эту версию или более позднюю версию, если кто-то еще сталкивается с этим проблема.

Связанные обсуждения здесь и здесь.