Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Sign In for Websites causing "Signature verification failed" with JWT PHP library

Google web sign in has driven me positively crazy...

I'm building a simple web application, and I'm trying to integrate Google's sign in feature into the website (https://developers.google.com/identity/sign-in/web/).

The JavaScript seemed to go fairly well, and the next step was to verify the id_token I was receiving with my backend server (again, against Google's recommendation: https://developers.google.com/identity/sign-in/web/backend-auth).

It's a PHP-based web application, and I've successfully installed the Google Client API library using composer: composer require google/apiclient, but when posting my id_token value to my PHP backend system I'm consistently receiving the following error:

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...')

I've also used the id_token value on Google's "tokeninfo" endpoint (https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=ABC123), and the id_token validates perfectly, so I'm sure it's not the id_token value that's wrong. It's also passing it perfectly via the POST variable to the PHP script, so I'm at a bit of a loss.

Here's my code:

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>

...and my PHP catching the 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);
    }

?>

Thank you so much for taking the time to read this, and for any assistance! It's greatly appreciated!

like image 900
Chris Kempen Avatar asked Aug 02 '17 00:08

Chris Kempen


4 Answers

This has been fixed in v2.2.1 of google/apiclient so make sure you are running this version or later if anyone else encounters this issue.

Related discussions here and here.

like image 156
Joe Race Avatar answered Oct 31 '22 01:10

Joe Race


I had the same issue today.

Easier if you just execute:

composer require firebase/php-jwt:4.0
like image 11
Bruno FG Avatar answered Nov 05 '22 00:11

Bruno FG


Fortunately you can verify id_token without google library as described here 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's 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());
            }
        }
    }

If you have no exceptions then your token is valid

like image 3
Anatoly E Avatar answered Nov 05 '22 00:11

Anatoly E


It is a problem with php-jwt. Latest version is not working with Google Api Client.

Try to use php-jwt version 4.

I put "firebase/php-jwt": "<5.0" in my composer.json file.

Worked as a charm!

like image 2
Pedro Pereira Avatar answered Nov 05 '22 01:11

Pedro Pereira