Google API不允许我在PHP中使用OAuth检索Gmail邮件(返回401 - 需要登录)

时间:2017-03-31 07:17:40

标签: php google-api gmail google-oauth google-api-php-client

我在这方面已经结束了。我有OAuth设置和工作(即我可以在授权后检索访问/刷新令牌),但是当我尝试连接到我的Gmail收件箱时,Google只会向JSON发回401,说明需要登录。< / p>

我正在使用beta版google-api-php-client库,但我无法找到任何不推荐使用的解决方案。奇怪的是,我能够在第一次测试时检索到一条消息,但在那之后(没有任何代码更改)它开始返回401而它正在驱使我疯狂。

以下是相关的相关代码:

<?php

public function __construct( $autoAuth = FALSE )
{
    require_once( 'scripts/gmail/config.class.php' );
    require_once( 'scripts/gmail/google-api-php-client/vendor/autoload.php' );

    $this->appName = gmail_config::$appName;
    $this->clientId = gmail_config::$clientId;
    $this->clientSecret = gmail_config::$clientSecret;

    $this->client = new Google_Client();

    // Using the JSON also returned a 401 so I tried it without; no change.
    //$this->client->setAuthConfig( 'scripts/gmail/gmail_client_id.json' );
    $this->client->setClientId( gmail_config::$clientId );
    $this->client->setClientSecret( gmail_config::$clientSecret );

    // On user auth screen, it keeps saying I'm only requesting "offline access".  Any idea why?
    $this->client->addScope( 'https://mail.google.com/' );
    $this->client->addScope( 'https://www.googleapis.com/auth/gmail.compose' );

    $this->client->setRedirectUri( '(redacted)' );

    // Required for generating a refresh token.  --Kris
    $this->client->setAccessType( 'offline' );
    $this->client->setApprovalPrompt( 'force' );

    $this->authURL = $this->client->createAuthUrl();

    $this->SQLInit();

    if ( $autoAuth === TRUE )
    {
        if ( !( $this->auth() ) )
        {
            print 'User authorization required : ' . $this->authURL;
        }
    }
}

public function auth()
{
    $res = $this->SQLQuery( 'SELECT * FROM oauth_tokens WHERE service = ?', array( 'gmail' ) );

    if ( !empty( $res ) && !empty( $res['accessToken'] ) && !empty( $res['refreshToken'] ) && !empty( $res['issued'] ) && !empty( $res['lastRefresh'] ) )
    {
        $this->accessToken = json_decode( $res['accessToken'], TRUE );
        $this->refreshToken = $res['refreshToken'];
        $this->tokenIssued = strtotime( $res['issued'] );
        $this->lastTokenRefresh = strtotime( $res['lastRefresh'] );

        if ( (time() - $this->lastTokenRefresh) >= 3600 )
        {
            $this->refreshToken();
        }

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

public function refreshToken()
{
    $this->client->refreshToken( $this->refreshToken );
    $this->accessToken = $this->client->getAccessToken();

    $this->SQLQuery( 'DELETE FROM oauth_tokens WHERE service = ?', array( 'gmail' ), SQL_RETURN_AFFECTEDROWS );

    $this->SQLQuery( 'INSERT INTO oauth_tokens ( service, accessToken, refreshToken, lastRefresh ) VALUES ( ?, ?, ?, NOW() )', 
            array( 'gmail', json_encode( $this->accessToken ), $this->accessToken['refresh_token'] ), 
            SQL_RETURN_AFFECTEDROWS );
}

public function getMessagesTest( $limit = 5 )
{
    $service = new Google_Service_Gmail( $this->client );

    $optParams = [];
    $optParams['maxResults'] = $limit; // Return Only $limit Messages
    $optParams['labelIds'] = 'INBOX'; // Only show messages in Inbox

    $messages = $service->users_messages->listUsersMessages( 'me', $optParams ); // <-- THIS IS WHERE IT'S FAILING!
    $list = $messages->getMessages();

    $res = array();
    foreach ( $list as $key => $email )
    {
        $messageId = $email->getId(); // Grab Message

        $optParamsGet = [];
        $optParamsGet['format'] = 'full'; // Display message in payload

        $message = $service->users_messages->get( 'me', $messageId, $optParamsGet );
        $messagePayload = $message->getPayload();

        $headers = $message->getPayload()->getHeaders();
        $parts = $message->getPayload()->getParts();

        $body = $parts[0]['body'];
        $rawData = $body->data;
        $sanitizedData = strtr( $rawData, '-_', '+/' );
        $decodedMessage = base64_decode( $sanitizedData );

        $res[$key] = array();
        $res[$key]['message'] = $decodedMessage;
        $res[$key]['parts'] = $parts;
    }

    return $res;
}

如果它有帮助,这里是从redirectUri端点存储令牌的地方:

    <?php

    try
    {
        $gmail = new gmail();
        $token = $gmail->client->fetchAccessTokenWithAuthCode( $params[1]['code'] );

        $sql->query( 'DELETE FROM oauth_tokens WHERE service = ?', array( 'gmail' ), SQL_RETURN_AFFECTEDROWS );
        $sql->query( 'INSERT INTO oauth_tokens ( service, accessToken, refreshToken, lastRefresh ) VALUES ( ?, ?, ?, NOW() )', array( 'gmail', json_encode( $token ), $token['refresh_token'] ) );
    }
    catch ( Exception $e )
    {
        return array( 'status' => 500, 'error' => 'SQL error on insert : ' . $e->getMessage() );
    }

我一直在谷歌搜索这几个小时,我尝试过的任何东西都没有任何效果。我确实尝试重新访问authURL来刷新令牌/ etc,但这不起作用。

我更喜欢一个简单的修复(也许只是我缺少的一些必需的东西?)而不是重构一切以使用别的东西。那就是说,我很难过。有谁知道我在这里做错了什么?如果您需要其他代码/信息,请告诉我,我会将其包含在内。

感谢您的帮助!现在这阻碍了整个项目,所以希望你们中的一个人能看到我错过的东西......

修改

我将一个var_dump($ this-&gt;客户端)添加到getMessagesTest()中,就像你建议的那样。这是编辑后的输出:

object(Google_Client)#10 (8) {
  ["auth":"Google_Client":private]=>
  object(Google\Auth\OAuth2)#11 (24) {
    ["authorizationUri":"Google\Auth\OAuth2":private]=>
    object(GuzzleHttp\Psr7\Uri)#12 (7) {
      ["scheme":"GuzzleHttp\Psr7\Uri":private]=>
      string(5) "https"
      ["userInfo":"GuzzleHttp\Psr7\Uri":private]=>
      string(0) ""
      ["host":"GuzzleHttp\Psr7\Uri":private]=>
      string(19) "accounts.google.com"
      ["port":"GuzzleHttp\Psr7\Uri":private]=>
      NULL
      ["path":"GuzzleHttp\Psr7\Uri":private]=>
      string(14) "/o/oauth2/auth"
      ["query":"GuzzleHttp\Psr7\Uri":private]=>
      string(0) ""
      ["fragment":"GuzzleHttp\Psr7\Uri":private]=>
      string(0) ""
    }
    ["tokenCredentialUri":"Google\Auth\OAuth2":private]=>
    object(GuzzleHttp\Psr7\Uri)#13 (7) {
      ["scheme":"GuzzleHttp\Psr7\Uri":private]=>
      string(5) "https"
      ["userInfo":"GuzzleHttp\Psr7\Uri":private]=>
      string(0) ""
      ["host":"GuzzleHttp\Psr7\Uri":private]=>
      string(18) "www.googleapis.com"
      ["port":"GuzzleHttp\Psr7\Uri":private]=>
      NULL
      ["path":"GuzzleHttp\Psr7\Uri":private]=>
      string(16) "/oauth2/v4/token"
      ["query":"GuzzleHttp\Psr7\Uri":private]=>
      string(0) ""
      ["fragment":"GuzzleHttp\Psr7\Uri":private]=>
      string(0) ""
    }
    ["redirectUri":"Google\Auth\OAuth2":private]=>
    string(47) "(my redirect URL)"
    ["clientId":"Google\Auth\OAuth2":private]=>
    string(72) "(my clientId).apps.googleusercontent.com"
    ["clientSecret":"Google\Auth\OAuth2":private]=>
    string(24) "(my client secret)"
    ["username":"Google\Auth\OAuth2":private]=>
    NULL
    ["password":"Google\Auth\OAuth2":private]=>
    NULL
    ["scope":"Google\Auth\OAuth2":private]=>
    NULL
    ["state":"Google\Auth\OAuth2":private]=>
    NULL
    ["code":"Google\Auth\OAuth2":private]=>
    NULL
    ["issuer":"Google\Auth\OAuth2":private]=>
    string(72) "(my clientId).apps.googleusercontent.com"
    ["audience":"Google\Auth\OAuth2":private]=>
    NULL
    ["sub":"Google\Auth\OAuth2":private]=>
    NULL
    ["expiry":"Google\Auth\OAuth2":private]=>
    int(3600)
    ["signingKey":"Google\Auth\OAuth2":private]=>
    NULL
    ["signingAlgorithm":"Google\Auth\OAuth2":private]=>
    NULL
    ["refreshToken":"Google\Auth\OAuth2":private]=>
    NULL
    ["accessToken":"Google\Auth\OAuth2":private]=>
    NULL
    ["idToken":"Google\Auth\OAuth2":private]=>
    NULL
    ["expiresIn":"Google\Auth\OAuth2":private]=>
    NULL
    ["expiresAt":"Google\Auth\OAuth2":private]=>
    NULL
    ["issuedAt":"Google\Auth\OAuth2":private]=>
    NULL
    ["grantType":"Google\Auth\OAuth2":private]=>
    NULL
    ["extensionParams":"Google\Auth\OAuth2":private]=>
    array(0) {
    }
  }
  ["http":"Google_Client":private]=>
  NULL
  ["cache":"Google_Client":private]=>
  NULL
  ["token":"Google_Client":private]=>
  NULL
  ["config":"Google_Client":private]=>
  array(23) {
    ["application_name"]=>
    string(0) ""
    ["base_path"]=>
    string(26) "https://www.googleapis.com"
    ["client_id"]=>
    string(72) "(my clientId).apps.googleusercontent.com"
    ["client_secret"]=>
    string(24) "(my client secret)"
    ["redirect_uri"]=>
    string(47) "(my redirect URL)"
    ["state"]=>
    NULL
    ["developer_key"]=>
    string(0) ""
    ["use_application_default_credentials"]=>
    bool(false)
    ["signing_key"]=>
    NULL
    ["signing_algorithm"]=>
    NULL
    ["subject"]=>
    NULL
    ["hd"]=>
    string(0) ""
    ["prompt"]=>
    string(0) ""
    ["openid.realm"]=>
    string(0) ""
    ["include_granted_scopes"]=>
    NULL
    ["login_hint"]=>
    string(0) ""
    ["request_visible_actions"]=>
    string(0) ""
    ["access_type"]=>
    string(7) "offline"
    ["approval_prompt"]=>
    string(5) "force"
    ["retry"]=>
    array(0) {
    }
    ["cache_config"]=>
    array(0) {
    }
    ["token_callback"]=>
    NULL
    ["jwt"]=>
    NULL
  }
  ["logger":"Google_Client":private]=>
  NULL
  ["deferExecution":"Google_Client":private]=>
  bool(false)
  ["requestedScopes":protected]=>
  array(2) {
    [0]=>
    string(24) "https://mail.google.com/"
    [1]=>
    string(45) "https://www.googleapis.com/auth/gmail.compose"
  }
}

再次感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

所以看起来问题是访问令牌已经过期并且我的refreshToken()函数被操纵以通过比较当前时间与数据库中存储时间来手动检查到期时间。但由于我使用的是幂等REST API,因此我将其设置为在用户授权发生时删除并重新插入令牌(即通过在浏览器中遵循authURL)。结果,每当我不耐烦地返回到authURL以尝试获取新令牌时,“已发布”的时间戳被重置,而不是意识到它只是给了我与之前每次相同的过期访问令牌。

显然,你使用刷新令牌来获取新的令牌;即使重新开始使用authURL也不会削减它。当我注意到客户端有一个内置的isAccessTokenExpired()函数时,我把它全部放在一起,所以我修改了我的ifcheck来使用它,现在一切正常。

TL; DR:我的刷新代码未被调用,因为我错误地认为当用户重新访问authURL时会自动刷新访问令牌。