Using Google Drive REST API with Laravel

In this tutorial, we’ll be using Google Drive REST API with Laravel application. We’ll authenticate the user with OAuth service and retrieve their access token. We’ll then use it to manage their drive data and perform read and write operations on their behalf. 

Create a new project and generate authentication scaffolding. Install Socialite and Google Client Library. We’ll use Socialite to authenticate the user with Google’s OAuth service.

Create Google Drive Application

Now head over to Google Developers Console and create or select an existing project. You can use this wizard to register Google Drive Application.

Now click continue and go to credentials.

Choose Google drive API, select Web Server and check User Data. 

Enter client ID name and enter Javascript origin and redirect URIs. Enter your domain name instead of localhost address for production. Redirect URI will be used to redirect users back to your app after authentication. Create client ID and enter product name. Download JSON credentials file and save it.

 

Now click on Enable API and Services from the dashboard.

Search for Google Drive and Google+ APIs and make sure both are enabled.

Configure Google Service

Now edit config/serivces.php  and add google service at the end of services array.

Now define enviroment variables in .env file.

You Redirect URI must match with the one we entered during application setup.

OAuth Authentication

Add these routes to your app/routes/web.php  file.

Now define both methods in LoginController.

We’re using “https://www.googleapis.com/auth/drive” scope to request user permissions for managing their drive data in redirectToGoogleProvider() method. In handleProviderGoogleCallback()  method, we’re creating a new user if it doesn’t exist. if it exists, we’re updating their refresh_token. Make sure you add this column to your user’s table by modifying user migration and setting the password to nullable.

Dependency Injection

Create GoogleServiceProvider. We’ll inject Google Client class into service container to avoid unnecessary instantiation.

After instantiation of Google_Client  class, we’re reading google service credentials and creating a temporary file with it. We’re doing this because setAuthConfig() method expects JSON file with credentials that we downloaded after setting up drive app. Now every time we type hint Google_Client in our methods, we’ll have access to this instance from the service container.

If you’re running Laravel 5.5 or above, you don’t have to add  GoogleServiceProvider to the providers array in config/app.php. Laravel Auto-Discovery automatically register your service providers. However, if you’re using an order Laravel version, you are required to add it manually. Register GoogleServiceProvider by adding this line to the $providers array in config/app.php file.

Routes and Controller

Add these routes to your app. Create DriveController and implement these methods to handle requests.

In our controller’s constructor, we can’t access Auth user, this is why we’re using a closure based middleware. We’re setting refresh token and creating a new instance of  Google_Service_Drive  with Google_Client instance from the service container.

Retrieving Folders

In getDrive()  method, we’re calling ListFolders($id)  method with ‘root’ param. root refers to the main directory of google drive. Then we’re retrieving root folders with  files->listFiles($optParams) method. If you want to access files in a specific folder you can pass folder ID instead of root to access its files. You can also recursively calling ListFolder() method in the foreach loop.

Creating Files from Storage and Form Uploads

createFile($file, $parent_id = null) takes two arguments. First one is a file. It can be a file object retrieved from requests or a storage path. You can pass the parent id as the second param. If you want to create a new file under a specific folder. After creating the file, it returns file id.

Creating Folders

You can pass the folder name to createFolder($folder_name) method to create a new folder and get its ID.

Deleting Folders

To delete a folder or file, all you have to do is to pass an id to deleteFileOrFolder($id) and it will be deleted from the drive.

I’ve set up an example project repository. If you’ve any issue integrating Google Drive API in your app, comment your issue and I’ll try to help you with it.

Update: August 23rd, 2019

I’ve noticed one similar issue reported in a lot of comments on this post.

I tried to debug this error and found that issued token expires after an hour. If you try to make API request after an hour, you get this error as JSON response. Here’s how I fixed this issue. A commit for this fix is also available in the repository.

Create a new migration to add two new columns to the users table. Run

php artisan make:migration add_token_expire_column_to_users --table=users

And add this code up() and down() methods in your migration class.

Also, update redirectToGoogleProvider() and  handleProviderGoogleCallback() method code in LoginController.

Update $fillable array on your User model.

Now update  DriverController constructor.

Now here’s an explanation of what we did here, why it needed to be done and why didn’t I do it when I first published this tutorial. I noticed a few comments coming on this post that has this issue, which I initially ignored and though it was a user error. A few months ago, I was working on a client’s project where we were using Gmail’s API to send emails and we followed the same OAuth flow mentioned in this tutorial to retrieve the token and consume it for sending emails. I noticed the same error that users have been reporting here and figured out I was making a mistake. I learned that Google tokens have an expiry time of one hour and we need to refresh it using refresh_token afterward.

We added two new columns to store expiry time and token issued, which was previously stored in refresh_token column on the user’s record. On redirect back from the OAuth consent screen, we are now updating user’s token, expiry_time, name, and refresh_token if present. I learned that Google only issues refresh token one time after the user accepts the permissions. I’ve set approval_prompt parameter to value force which requests API to display consent screen every time the user tries to log in. You can remove it if you want.  And lastly, we updated code in the constructor to check if the user’s access token is expired and request a new using refresh token.

Share this post

68 thoughts on “Using Google Drive REST API with Laravel”

  1. Nice. Useful tutorial.
    Could you please make a tutorial on a Timesheet app. Users/Employees would enter their daily work: start time, end time, project name, notes and any expenses.
    Many Thanks !

  2. I have an error when i´m trying to upload a file, the app says “Trying to get property of non-object” What is the problem with the code?

    1. Are you uploading the file through a web form? Make sure you have enctype="multipart/form-data" on your form. Also make sure $request->file('file') exists before passing it to createFile($request->file('file')) method.

  3. i made all the things that are in this document and it’s write me:
    Too few arguments to function App\Http\Controllers\DriveController::__construct(), 0 passed in /opt/lampp/htdocs/Agile/app/Http/Controllers/DashbourdController.php on line 11 and exactly 1 expected
    what should i do?

    1. Have you created GoogleClientProvider? Can you share DriveController.php, GoogleClientProvider.php and DashboardController.php code in a Gist so that I can look into the issue?

  4. i have a problem that Auth::user() is null and then the DriveController is crashing in the cunstructor.
    What is the problem?

    Thanks

  5. Hi, I get the following error when I try to log in for the first time, it seems that the application does not get the value of refresh_token

    Integrity constraint violation: 1048 Column ‘refresh_token’ cannot be null (SQL: insert into users (email, refresh_token, name, updated_at, created_at) values (xxx@xxxx, , xxxxxx, 2018-08-14 20:13:44, 2018-08-14 20:13:44))

    Can you help me?

    1. Try dumping $auth_user in handleProviderGoogleCallback method on LoginController to see if it has refresh_token property on redirect after you authorize access to OAuth service.

      1. Don’t sign up using Laravel default auth form. After setting up app and running migrations, redirect to /login/google and it will redirect to Google OAuth page where you’ll be asked to allow access to your account. After allowing, you’ll be redirect back to Laravel app with a refresh_token and a new user will be created with your google account email.

  6. hi,
    maybe can you explain how does the refresh token has been created because in my project it stay null.

    Thanks

    1. Just change $table->string('refresh_token') to $table->string('refresh_token')->nullable(); in users table migration. After installing composer dependencies, run migrations. Now redirect to /login/google/ route in browser. It will redirect you to Google and ask your to allow access to your account. After allowing, you’ll be redirected back to your Laravel app and a new user will be created with your Google email.

      1. i’m getting this error: Class ‘App\Http\Controllers\Auth\Socialite’ not found. i install the socialite and add use to the logonController.
        what do i need to do?

  7. Hi,
    Once I get through the authentication process, how do I create a new $client instance with my credentials to insert into the DriveController($client)

    1. It’s already being done in DriveController. You can access Google_Service_Drive instance with $this->drive. We’ve GoogleClientProvider which binds Google_Client into Laravel service container and automatically resolves it when you type hint on your controller methods and constructors. We’ve an instance of Google_Client in DriveController constructor.

  8. hi,
    I got error when trying to access /drive :
    “Class App\Http\Controllers\Google_Client does not exist”
    Thank you for your attention.

  9. I’ ve used dd($auth_user->refreshToken); because when I want to upload a file or maybe see my files, I have an error called “Trying to get property of non-object”

    And dd(—) returns to me “null”

    So, what can I do? Please and thank you very much!

  10. Hi,
    I’m a little new here if I follow this tutorial and then run: php artisan serve –port=8080 and then go to
    localhost::8080/drive is that supposed to work?

    1. Yes. Make sure you also change Redirect URI in .env file to GOOGLE_REDIRECT=’http://127.0.0.1:8080/login/google/callback’ and also add this to authorized redirect URIs when creating your app in Google developers console.

      1. I think there is something missing here like how do I create a new user in the database? because when I register in the home page it creates a user without a refresh token and login/google redirects me back to the home page

        1. It’s already being done in LoginController‘s handleProviderGoogleCallback method. In redirectToGoogleProvider method, user will be redirected to Google’s OAuth service and redirected back with a refresh_token. A new user will created with a refresh_token if it doesn’t exist. refresh_token will be updated for existing users.

          1. Could you explain more about the refresh_token? I only see answers that don’t help. I’ve been trying to fix the error for 4 days but I still get “Refresh token must be passed in or set as part of setAccessToken”… And if I return the refresh_token, this is null.

            Could you please explain more without saying that the refresh_token will be generated automatically (which does not happen)?

          2. Automatically? where did i say that? I’m afraid you don’t understand OAuth workflow. We’ve these two routes in our app.

            // Redirects you to Google OAuth
            Route::get('/login/google', 'Auth\LoginController@redirectToGoogleProvider');

            // Handles redirect callback after user authorize access to their account.
            Route::get('login/google/callback', 'Auth\LoginController@handleProviderGoogleCallback');

            In handleProviderGoogleCallback methods, Socialite retrieves user account information along with refresh_token. If a user with email already exists, we’ll simply refresh their token, otherwise we’ll create a new user with that email and token.
            User::firstOrCreate(['email' => $auth_user->email], ['refresh_token' => $auth_user->refreshToken, 'name' => $auth_user->name]);

  11. I’m sorry bro! Honestly, it doesn’t work for me. I have done the tutorial 4 times but I cann’t solve the problem with refresh_token. Sorry, before I said “Automatically” and it’s not true. But you say that the Refresh_token will be updated. But I don’t know how to do that. I have the same data as you but my app does not work.

      1. Hi there Waleed !

        Thanks for this tutorial !

        I’m having the same problem as Brayan, did you find the solution ?

          1. Thanks for your reply Waleed, but I have already changed everything according to your GitHub repository, including the $auth_user->token instead of $auth_user->refreshToken.

            I am still getting “refresh token must be passed in or set as part of setAccessToken” error.

          2. Sorry about the double post, I solved the problem by adding ‘refresh_token’ to the list of protected $fillablearray.

            Now I am getting error :
            { “error”: { “errors”: [ { “domain”: “usageLimits”, “reason”: “dailyLimitExceededUnreg”, “message”: “Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.”, “extendedHelp”: “https://code.google.com/apis/console” } ], “code”: 403, “message”: “Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.” } }

          3. i got same error before, i slove my problem with add
            App\Providers\GoogleClientProvider::class,
            to config/app.php

          4. Great thanks for your help Rizky. It actually worked using App\Providers\GoogleServiceProvider::class,instead of App\Providers\GoogleClientProvider::class,

  12. Hi , Thanks for your great article

    if our program upload to the web and if we want to one time permission granting .
    what should we do ?

    my mean from one time permission granting is :
    for example if we want creating some apps for syncing files fromour server and user drive and upload this script to the server and for first time we grant permissions from user , and for syncing file in the backend.

    thanks for your answer

  13. Hello, Thanks for your great tutorials, but i getting error like this, can you help me?
    { “error”: { “errors”: [ { “domain”: “usageLimits”, “reason”: “dailyLimitExceededUnreg”, “message”: “Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.”, “extendedHelp”: “https://code.google.com/apis/console” } ], “code”: 403, “message”: “Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.” } }

  14. I have followed your article and found 1 error after call . Its showing Error. Page cannot be displayed. Please contact your service provider for more details. (31)

    Can you please help me to find out solution. I have also enable Google+ API, Google Drive API.

  15. hello Waleed Ahmed,

    I’m gettting this error again and again , i have searched in google but did n’t get any proper solution it’s been three days , facing this problems . please help me out from below error exceptions .

    { “error”: { “errors”: [ { “domain”: “usageLimits”, “reason”: “dailyLimitExceededUnreg”, “message”: “Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.”, “extendedHelp”: “https://code.google.com/apis/console” } ], “code”: 403, “message”: “Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.” } }

  16. Hi all, i’v got this error,
    refresh token must be passed in or set as part of setAccessToken
    please help.
    The problem is in GoogleDriveController’s __Construct method

    1. You have to use google social authentication route (/login/google) and follow oAuth flow to create the user and retrieve their access token. Try dumping Auth::user()->refresh_token and see if it has refresh token.

  17. I face this problem when trying to get the driver file.
    { “error”: { “errors”: [ { “domain”: “usageLimits”, “reason”: “dailyLimitExceededUnreg”, “message”: “Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.”, “extendedHelp”: “https://code.google.com/apis/console” } ], “code”: 403, “message”: “Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.” } }

    1. I am getting the same issue

      { “error”: { “errors”: [ { “domain”: “usageLimits”, “reason”: “dailyLimitExceededUnreg”, “message”: “Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.”, “extendedHelp”: “https://code.google.com/apis/console” } ], “code”: 403, “message”: “Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.” } }

      Any advice, Waleed?

      1. I figured it out. The issued token expires after an hour and if you try to request using an expired token, you get this error. I’ve updated the tutorial with this fix.

  18. { “error”: { “errors”: [ { “domain”: “global”, “reason”: “required”, “message”: “Login Required”, “locationType”: “header”, “location”: “Authorization” } ], “code”: 401, “message”: “Login Required” } }

    this is the error I have been having

  19. Client error: GET https://www.googleapis.com/plus/v1/people/me?prettyPrint=false resulted in a 403 Forbidden response:
    {“error”:{“errors”:[{“domain”:”global”,”reason”:”insufficientPermissions”,”message”:”Insufficient Permission: Request ha (truncated…)

    In Laravel 5.4 this error has been occurred due to this line in redirectToGoogleProvider() in Login controller.
    return Socialite::driver(‘google’)->scopes([“https://www.googleapis.com/auth/drive”])->with($parameters)->redirect();

    Kindly ensure me, what should be the mininum Laravel version for this??

    1. This error has nothing to do with Laravel’s version. You need to enable Google+ API and Google Drive API in Google developers console.

      return Socialite::driver(‘google’)->scopes([“https://www.googleapis.com/auth/drive”])->with($parameters)->redirect();

      Here you’re requesting (https://www.googleapis.com/auth/drive) scope which needs Google Drive API to be enabled for your app otherwise you’ll get this error.

      Go to Google developers console dashboard (https://console.cloud.google.com/apis/dashboard) and open your app. Click on Enable API & Services, search for Google+ and Google Drive API’s and enable them.

  20. I have Checked many times both API were enabled in my project. But Still got this error. I don’t know why this happening.

      1. please provide the the GitHub project link so i can download the latest code and migration.
        Thanks in advance.

  21. I assume that when i run
    “composer require laravel/socialite” and “composer require google/apiclient”, i must be on the project folder first, right?

    I’ve run that commands on the www dir and now my composer is giving problems.

  22. Can you tell me how I can download files from google drive to my server? Currently, it’s only showing a folder name. not showing files.

  23. hello can you please show me how to display the images from google drive
    Thank you in advance

  24. This is a great tutorial. Informative and detailed explanation. Btw, do you have a tutorial on login using google, facebook, twitter using socialite ? Only using api request done by postman, I hope you can have one. Thanks in advance

  25. Hi can you share the code about how to use the gmail api within laravel structure instead of Google drive api.It will be a big help…

  26. Has following your code but still get error when uploading file with form. The Error code is: “File not found at path:” and I navigated to this code “$content = gettype($file) === ‘object’ ? File::get($file) : Storage::get($file);”. In view, I have set enctype.

  27. hi super concept and tutorial but i need lock out to the google drive at the time its using access token to get the google drive data get and show my website this concept i need this please help me.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.