2

I'm trying to create a Laravel API to interact with Gmail via the Google API client library for PHP. However, I encounter issues with the endpoint to get mails.

So far, I could do authorization and get the access token in my DB. This endpoint return JSON of this format:

{
    "user_id": 1,
    "token": {
        "access_token": "ya29.XXXXXXXXXXXX-K_P9Z0Rd0nU5WzSvU3TMlC0TZQRbMLkDHE1XI3j29mbIRP13dt_NGMb4d9trTECGKiwbjM45Ijk7fbhpLzU2JL7w-6w_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        "expires_in": 3599,
        "scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/gmail.readonly",
        "token_type": "Bearer",
        "id_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-yK6GHPRATk64GaPcYCuIVyqTYNNvfvr8gCVuTCKr3RMtUz5J97ROZkJRN5w_AQvyJFL369MNTnHQAiqJoPIN2Wes0OAbeq1urpDRMRaAfF6Nuqun-pmewI8g5OCjkUMqekZTKlzCqRl7Xkm4qQRrwk66SxwGdC78Iy5Z_3VG1OIj681zoY18dQu9_ZqGMnwVuHmffmzNInuAmqHcQ7CLM_I_vJbWV3-UKVRF2UtjHvmUydCdo4PrEpL371i8exwPme5IK_xMcywxdfdjxm4duyv6X_ky2MCYwANNBSyBLMYh214FZPowL8choHmMIms-biJlg",
        "created": 1647922921
    },
    "name": "gmail",
    "updated_at": "2022-03-22T04:22:01.000000Z",
    "created_at": "2022-03-22T04:22:01.000000Z",
    "id": 10
}

My second endpoint is to get the mails by using the access token.

My implementation looks like this.

    // Services
    public function getEMails($user_id, $access_token){
        $this->client->setAccessToken($access_token);
        $service = new Gmail($this->client);
        $messages = $service->users_messages->listUsersMessages($user_id);
        return $messages;
    }
   // Controller
    public function getMails(WebService $web_service, GmailServices $gmail_services){
        $access_token = $web_service->token['access_token'];
        $user_id = 'me';
        if (isset($access_token)){
            $mails = $gmail_services->getEMails($user_id, $access_token);
        }
        return $mails;
    }
  // api.php (In route middleware)
  Route::get('/web-service/{mails}', [WebServiceController::class, 'getMails'])->name('web-service.getMails');

Hitting this endpoint, I get the below error.

"message": "Trying to access array offset on value of type null", "exception": "ErrorException"

which corresponds to this line $access_token = $web_service->token['access_token'];

I did some minor research, but I couldn't resolve it. Unfortunately, I'm not advanced in Laravel development, and I'll appreciate any help and feedback.

Furthermore, I equally want to add that my implementation is largely based on Bitfumes test-driven API development on YouTube. Here is the link to the repo https://github.com/bitfumes/laravel-test-driven-api

Thanks!

After trying the above, I was expecting to get the mails and equally work on pagination or querying via LIMIT so that the backend will not over-stress to get all mails at once.

Karl Hill
  • 12,937
  • 5
  • 58
  • 95
ARMEL FOPA
  • 206
  • 3
  • 8
  • 1
    My first question would be why are you storing the access token in the database? Its going to expire in an hour. My second question would be to ask why you haven't posted your authorization code. if your properly setting Google_Client it should be handling all that for you. – Linda Lawton - DaImTo Mar 22 '22 at 07:52
  • 1
    Consider providing a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) including your authorization code. – Iamblichus Mar 22 '22 at 08:27
  • Hello @DaImTo, I'm planning to do a refresh of the access token as I proceed. And I equally exchange my authorization code to get a the access token and other details as you saw. The auth code is of this format to be precise ({ "code":"4/0AX4XXXXXXXXXXXXXXX2y2i-blXDoofX00zrLo3QDCoDSLlBpvwXcK6SA" }) – ARMEL FOPA Mar 22 '22 at 08:50
  • The authorization code only lasts five minutes and can only be used once again why bother? – Linda Lawton - DaImTo Mar 22 '22 at 08:53
  • Why aren't you just using $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken()); or $client->getAccessToken() if you really want one for some reason. – Linda Lawton - DaImTo Mar 22 '22 at 08:56
  • Exactly, I don't keep track of the authorization code, I simply exchange it for the access token. – ARMEL FOPA Mar 22 '22 at 08:59
  • Thanks @DalmTo, I'm going to add that for the refresh token. Just that I was trying to glue the pieces first. – ARMEL FOPA Mar 22 '22 at 09:02
  • Hello @Lamblichus, can this repo: https://github.com/bitfumes/laravel-test-driven-api help for the minimal reproducible example? Overall it's the same implementation. – ARMEL FOPA Mar 22 '22 at 09:04
  • 1
    @ARMELFOPA that is not the definition of a [example] – Linda Lawton - DaImTo Mar 22 '22 at 09:43
  • Does this answer your question? [Trying to access array offset on value of type null](https://stackoverflow.com/questions/59322150/trying-to-access-array-offset-on-value-of-type-null) – Linda Lawton - DaImTo Mar 22 '22 at 09:44
  • Unfortunately no, in my code I'm checking if the variable is set already – ARMEL FOPA Mar 22 '22 at 12:12
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/243229/discussion-between-armel-fopa-and-daimto). – ARMEL FOPA Mar 23 '22 at 10:26
  • What have you tried to resolve the problem? Why not check whatever `$web_service` contains? Maybe this is not an object, or it's property `token` does not contain the accessstoken? – Nico Haase Mar 24 '22 at 16:24
  • First look at the problem, the `$web_service->token` is an object, not an array, so you need to use `$web_service->token->access_token` – yangqi Mar 26 '22 at 18:46
  • @ARMELFOPA please include your authorization code. – Linda Lawton - DaImTo Mar 27 '22 at 21:01
  • Hi @yangqi, I need to add that there was cast protected $casts = [ 'token' => 'json' ]; – ARMEL FOPA Mar 28 '22 at 10:10

1 Answers1

0

Everything you do should be going though the Google_Client object. Your code is a little confusing to me you have two getEMails methods and neither appear to be using assigning things properly though the client.

When properly initialized it is able to handle everything for you.

$client = new Google_Client();
$client->setApplicationName('Gmail API PHP Quickstart');
$client->setScopes(Google_Service_Gmail::GMAIL_READONLY);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');

You should be testing if the access token has expired. If it is expired then have the Google_Client request a new one.

 if ($client->isAccessTokenExpired()) {
        // Refresh the token if possible, else fetch a new one.
        if ($client->getRefreshToken()) {
            $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
        } else {
           // request access of the user if it has not been granted.
        }
        // note after refresh remember to store the new refresh token.
    }
Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • The first method, getEmails is in the Services directory, and contains the logic for getting the mails. The getMails is in the controller and if you did notice it calls getEmails in the services together with the user_id and access token as param... – ARMEL FOPA Mar 28 '22 at 15:37
  • I still dont undrstand why you are bothering with $web_service->token['access_token'];. Once the refresh token is set in the client let it handle the access token. Just pass the service around to your other methods. You are getting a null exception becouse it doesnt exist print the full object you will see its not there so its not going to work – Linda Lawton - DaImTo Mar 30 '22 at 06:50
  • The $web_service->token['access_token'] was a way for me to get the access token. I did a dd() and I noticed it was null, reason why I got the error: "message": "Trying to access array offset on value of type null", "exception": "ErrorException". I hardcoded the token in the setAccessToken method and I could have results. Earlier on I sent a github repo and thesame implementation was working, I'm trying to figure out why it's not working for me and I'm also looking for a better way to pass my access token, refreshing the token is however not my problem – ARMEL FOPA Mar 30 '22 at 09:45
  • As a reminder, I'm trying to build this as an API that can be called by any frontend app, reason why the implementation slightly deviate from what you see in the doc or other PHP tuts – ARMEL FOPA Mar 30 '22 at 09:47
  • I am having issues with your design approach. You should not be sending access tokens to your backend. You should be storing the refresh token in your backend and then having your backend request the access tokens as needed. passing around access tokens like this is a security risk. – Linda Lawton - DaImTo Mar 30 '22 at 09:59
  • 1
    You are right @DalmTo, I'm working on that as well – ARMEL FOPA Mar 30 '22 at 11:00