Shivamyadav's avatar

Google api OAuth unauthorized issue?

I have a GoogleDriveService to mange the work for the drive related.

Earlier it was setup by me to work like get the new refresh access token for each request and it was working fine.

Yesterday my senior suggested me to use this way: 1. To add the logic only get the new access token if it is expired. 2. I have added this logic but now I am getting 401 (Unauthorized) error.

My constructor code

As soon i removed this below line form the top (first) set access token 'created' => (int) $accessToken['token_created_at'], // Unix timestamp It works but now each time it fetches the new access token for it.

Main error i am getting

"error": {
    "code": 401,
    "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
    "errors": [
      {
        "message": "Invalid Credentials",
        "domain": "global",
        "reason": "authError",
        "location": "Authorization",
        "locationType": "header"
      }
    ],
    "status": "UNAUTHENTICATED"
  }
}
0 likes
4 replies
imranbru's avatar
imranbru
Best Answer
Level 4

Your logic is actually fine. The problem is almost certainly your database migration.

Google access tokens are frequently longer than 255 characters. If you used $table->string('access_token') in your migration, Laravel defaults to a VARCHAR(255) and is silently truncating your token when saving it to the database.

Here is exactly why your test behaved that way: When you omit the created key, the Google client assumes the token is expired and forces a refresh. It then uses the full, fresh token directly from memory, which is why it works. However, when you include created, the client checks the timestamp, sees it hasn't been an hour yet, and sends the truncated (corrupted) token from the database to Google. Google rejects it, triggering the 401 Unauthorized.

Change your columns to text in your database migration:

$table->text('access_token');
$table->text('refresh_token');

Clear out your oauth_credentials table and re-authenticate to store a fresh, full-length token.

Avoid putting heavy external API calls inside a __construct(). If Google's API goes down, times out, or rate-limits you, your entire class fails to instantiate and it can crash Laravel's service container resolution. Move the token initialization to a dedicated connect() or boot() method that you call when you actually need to interact with the Drive API.

1 like
Shivamyadav's avatar

Thanks for the reply ☺️.

Actually this was the issue it was silently truncating the data and it get's only stored a allowed bytes of charcters to the table.

Yeah, I have already fixed and moved the constructor logic out and created a method authenticate and using it simply

1 like
martinbean's avatar

@shivamyadav I see you didn’t take my advice (that I’m sure I mentioned a couple of times when you’ve posted about this Google Drive-related project) of moving your Google Client creation logic to a service provider, so that classes then using that client (controllers, etc) just need to type-hint it and it will be automatically built and resolved:

public function register(): void
{
    $this->app->singleton(Client::class, function (Application $app) {
        return new Client([
            'client_id' => $app['config']['services.google.client_id'],
            'client_secret' => $app['config']['services.google.client_secret'],
            'scopes' => 'https://www.googleapis.com/auth/drive',
            'redirect_uri' => $app['config']['services.google.redirect_uri'],
            'prompt' => 'consent',
            'access_type' => 'offline',
        ]);
    });
}

For refreshing access tokens, I have a scheduled task that fires daily to refresh any soon-to-expire access tokens. But again, I’m sure I’ve mentioned this before:

Schedule::command('access-token:refresh')->daily();

If you find you’re unable to refresh an access token (because it’s been revoked by the owner), then you can delete it, and send a notification to the owner telling them they need to re-connect their Google account.

1 like
Shivamyadav's avatar

Sorry, I was kind a little bit in hurry to finish it somehow at that time. So, I missed it to implement it.

According to your advice. I will implement it on Saturday, as today I am working on some other cases.

Thanks, for your valuable time and advice.

Please or to participate in this conversation.