将GoogleAuthUtil.getToken提供的访问令牌发送到php Web服务器后进行身份验证

将GoogleAuthUtil.getToken提供的访问令牌发送到php Web服务器后进行身份验证

问题描述:

I am following the docs from link below:

https://developers.google.com/+/mobile/android/sign-in#enable_server-side_api_access_for_your_app

Specifically the part that says:

If you do not require offline access, you can retrieve the access token and send it to your server over a secure connection. You can obtain the access token directly using GoogleAuthUtil.getToken() by specifying the scopes without your server's OAuth 2.0 client ID. For example:

I retrieve the access token like this:

accessToken = GoogleAuthUtil.getToken(
                        AuthenticatorActivity.this,
                        Plus.AccountApi.getAccountName(Common.mGoogleApiClient),
                        "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/plus.login email"
                );

After I retrieve the access token I send it to a web server, on the web server i can see that it's a valid access token by calling

https://www.googleapis.com/oauth2/v1/tokeninfo?access_token='.$_POST['google_access_token']

The request above returns the android apps client id, it also returns the users email correctly.

The problem is that when I try to run $client->authenticate($_POST['google_access_token']); I get an exception with the message: "invalid_grant: Incorrect token type".

To prevent getToken caching I always invalidate the token in android app:

if (accessToken != null && !accessToken.isEmpty()) { GoogleAuthUtil.invalidateToken(AuthenticatorActivity.this, accessToken); }

Here's the php code:

        if (!isset($_POST['google_access_token'])) {
        throw new Exception('missing google_access_token');
    }


    $client = new \Google_Client();
    $client->setApplicationName("GiverHub");

    $client->setClientId($this->config->item('google_client_id'));
    $client->setClientSecret($this->config->item('google_client_secret'));

    $client->setDeveloperKey($this->config->item('google_developer_key'));
    $client->setRedirectUri($this->config->item('google_redirect_uri'));

    $client->setScopes([
        'https://www.googleapis.com/auth/plus.login',
        'https://www.googleapis.com/auth/plus.me',
        'email',
    ]);



    try {
        $client->authenticate($_POST['google_access_token']);  // if i remove this the rest of the code below works!  ...
        $reqUrl = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token='.$_POST['google_access_token'];
        $req = new \Google_Http_Request($reqUrl);

        $io = $client->getIo();
        $response  = $io->executeRequest($req);


        $response = $response[0];

        $response = json_decode($response, true);
        if ($response === null) {
            throw new Exception('Failed to check token. response null');
        }

        if ($response['issued_to'] !== '466530377541-s7cfm34jpf818gbr0547pndpq9songkg.apps.googleusercontent.com') {
            throw new Exception('Invalid access token. issued to wrong client id: '. print_r($response, true));
        }

        if (!isset($response['user_id'])) {
            throw new Exception('Missing user_id');
        }

        if (!isset($response['email'])) {
            throw new Exception('Missing email');
        }

        /** @var \Entity\User $user */
        $user = Common::create_member_google([
            'id' => $response['user_id'],
            'email' => $response['email'],
            'given_name' => '',
            'family_name' => '',
        ]);
        $user->login($this->session);
        if ($user instanceof \Entity\User) {
            echo json_encode( [ 'success' => true, 'user' => $user ] );
        } else {
            echo json_encode( [ 'success' => false, 'msg' => $user ] );
        }
    } catch(Exception $e) {
        echo json_encode(['success' => false, 'msg' => $e->getMessage()]);
    }

The above code works if i remove the $client->authenticate(); line ... The problem is that I can't get the given_name / family_name etc .. only email / google_user_id from the tokeninfo ...

Any thoughts about why the key works for tokeninfo but not for authenticate?

I have tried many different variations of the scopes .. both on the server side and the android side ..

This is the solution I came up with after user158443 suggested I use $client->setAccessToken();

// first json_encode the access token before sending it to $client->setAccessToken();
$json_encoded_access_token = json_encode([
            'access_token' => $_POST['google_access_token'],
            'created' => time(),  // make up values for these.. otherwise the client thinks the token has expired..
            'expires_in' => time()+60 // made up a value in the future... 
        ]);

// and then set it
$client->setAccessToken($json_encoded_access_token);

// and then get userinfo or whatever you want from google api !! :)
$oauth2 = new \Google_Service_Oauth2($client);
$user_info = $oauth2->userinfo->get();

NOTE: it's probably not smart to "emulate" the expires_in and created that i just did if you are in production ... You should probably call tokeninfo first and get the expires time from there...

NOTE: I still have no idea how to get a refresh token for this... but I don't need one for my use case..

The $client->authenticate() method doesn't quite do what you're trying to do. It takes a one-time code from an earlier OAuth transaction and exchanges it for the access token. In your case - you're saying you already have the access token.

You should be able to call $client->setAccessToken() to set the token instead, so it may look something like

$client->setAccessToken($_POST['google_access_token']);