50

I've some issues with Google Drive API, service account and authentication. I read a lot, but I cannot figure out how to solve this.

Context: I have some files on my Drive account (about 35GB) and a simple web app which let users to log in, see some selected folders/files from my Drive and download them if needed. The only one who can directly access to my Drive account is (or should be) my server, users do their stuff through web app/server.

After some search I found server-to-server authorization docs that should be perfect for my purpose BUT, as I can see, service account does not share same Drive space: they have their own and it's not upgradable. Because of this (weird) limit I cannot use service account since I have more than 35GB and I need to "share" everything.

Other way: use "standard" OAuth in order to obtain an access token and then use it to make call to Drive API, but access tokens have an expire date and I can't update it manually every time.

So, first question: is there a way to increase quota for service account? If not, if there a way to use my "normal" account (owner) acting like a service account?

Second (dummy) question: I read docs about creating new OAuth credentials and at the end you obtain some example code and "client-secret" JSON. I run the example but I didn't understand what the role of that JSON file is: I must log in and give permission anyway, why do I need it?

Third (dummy enough) question: if OAuth is the only solution, is there a way to obtain/refresh access tokens without doing it manually every time? I looked at OAuth docs and "user interaction/confirmation" is one of the basic thing in the auth flow, so I don't think is possible.

tehhowch
  • 9,645
  • 4
  • 24
  • 42
  • 1
    see https://stackoverflow.com/questions/19766912/how-do-i-authorise-an-app-web-or-installed-without-user-intervention-canonic – pinoyyid Aug 03 '17 at 20:19
  • Awesome! I didn't find it before, I'll try in that way and let's see what happens... Thanks a lot! –  Aug 04 '17 at 13:26

2 Answers2

133

Share your Drive account's folder to your service account.
Your service account's addresss looks like XXX@XXX.iam.gserviceaccount.com.
Then your service account can see the shared folder from your Drive account.
So you have 35GB service account.

drinkmystery
  • 1,476
  • 1
  • 10
  • 6
  • 11
    THIS THIS THIS!!!!!! WOW. I struggled through SO many Stackoverflow posts... until this one. YOU saved me! Thank you so much @drinkmystery. I am indebted to you. – pmalbu Oct 03 '19 at 03:33
  • 4
    Man this answer really helped me. I was hating Google Drive so much for making my life miserable – David Olmo Pérez Nov 24 '19 at 10:26
  • how much time it takes to effect this sharing to show up in service account.. – Junaid Farooq Nov 28 '19 at 15:49
  • @JunaidFarooq not in real-time, In my case, it's around 5 minutes – Wildan Muhlis Jan 15 '20 at 10:31
  • 3
    THIS IS THE RIGHT ANSWER!! – Irvin Chan Jan 08 '21 at 22:45
  • 2
    Thanks for this insight. It is very helpful because the quickstart page does not mention this approach, at least as far as I noticed. – Kota Mori Sep 11 '21 at 04:33
  • Is this still supported? I simply get a message saying "Permission denied. Your access may have been changed.", when trying to make my service account a Content Manager. I feel like I'm missing a trick here... – Ruben Feb 09 '22 at 11:35
  • 1
    I can confirm that, by today, this is still working... I was struggling with this too, so much appreciated!!! – KurroGB Apr 12 '22 at 17:56
  • This answer works fine when you don't need the service account to *create* files **and** your Workspace domain doesn't restrict file sharing with external domains (i.e you are not restricted from adding the service account as a collaborator). In either of these cases, see this answer: https://stackoverflow.com/questions/19766912/how-do-i-authorise-an-app-web-or-installed-without-user-intervention – beano May 19 '22 at 01:08
  • I get the following error: `Sharing with e-mail address not associated to a Google account is impossible right now`. – Moebius May 25 '22 at 15:06
13

The answer of drinkmystery is the right way to go in case your account is private and not a part of Google Workspace (formerly GSuite). Otherwise, there's a more elegant way to solve this with the createDelegated() method. It was described here and you should follow all the configuration instructions from there, but the code samples provided there are based on some deprecated packages, so it took me some time to make it work properly combining with the codes from the Drive API tutorial.

So for those who just need a working code sample, here it is (note the use of createDelegated):

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.ServiceAccountCredentials;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.List;

public class DelegatedDriveWithServiceAccountQuickstart {
    private static final String APPLICATION_NAME = "your awesome app";
    private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();

    private static final List<String> SCOPES = Arrays.asList(DriveScopes.DRIVE);
    private static final String CREDENTIALS_FILE_PATH = "/path/to/your/service-account-key-downloaded-from-google-api-console.json";

    public static void main(String... args) throws IOException, GeneralSecurityException {
        // Build a new authorized API client service
        final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
        HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(ServiceAccountCredentials.fromStream(new FileInputStream(CREDENTIALS_FILE_PATH))
                .createScoped(SCOPES)
                .createDelegated("user.whose.drive.you.want.to.share@your-domain-in-gsuite.com"));
        Drive driveService = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer)
                .setApplicationName(APPLICATION_NAME)
                .build();

        // Print the names and IDs for up to 10 files.
        FileList result = driveService.files().list()
                .setPageSize(10)
                .setFields("nextPageToken, files(id, name)")
                .execute();
        List<File> files = result.getFiles();
        if (files == null || files.isEmpty()) {
            System.out.println("No files found.");
        } else {
            System.out.println("Files:");
            for (File file : files) {
                System.out.printf("%s (%s)\n", file.getName(), file.getId());
            }
        }
    }
}

Please, make sure your project dependencies are up to date (don't use blindly those from the Drive API tutorial). Here is my build.gradle:

apply plugin: 'java'
apply plugin: 'application'

mainClassName = 'DelegatedDriveWithServiceAccountQuickstart'
sourceCompatibility = 1.8
targetCompatibility = 1.8
version = '1.0'

repositories {
    mavenCentral()
}

dependencies {
    compile 'com.google.api-client:google-api-client:1.31.1'
    compile 'com.google.apis:google-api-services-drive:v3-rev20201130-1.31.0'
    compile 'com.google.auth:google-auth-library-oauth2-http:0.22.2'
}

Sergey Plotnikov
  • 323
  • 3
  • 11
  • Thank you for the example using a service account. – Welington Veiga Aug 12 '21 at 21:45
  • See https://console.developers.google.com/apis/credentials/wizard and https://chrisboakes.com/building-a-rest-api-with-google-sheets-and-aws-lambda/ – David Feb 13 '22 at 23:12
  • I got `POST https://oauth2.googleapis.com/token` `{ "error": "invalid_grant", "error_description": "Invalid email or User ID" }` – Psijic Jun 30 '23 at 14:12