Search code examples
javagoogle-apigoogle-drive-apigoogle-oauth

Do I need G Suite account to make requests impersonating an user with a service account?


I'm trying to make requests to Drive API impersonating an user using a service account but I'm getting a TokenResponseException: 401 Unauthorized exception.

I've followed what is described in https://developers.google.com/identity/protocols/OAuth2ServiceAccount:

Then, in my code:

File p12 = new File("p12FileFromDevelopersConsole.p12");
HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();

GoogleCredential c = new GoogleCredential.Builder()
    .setTransport(transport)
    .setJsonFactory(jsonFactory)
    .setServiceAccountId("[email protected]")
    .setServiceAccountUser("[email protected]")
    .setServiceAccountScopes(Arrays.asList(DriveScopes.DRIVE, DriveScopes.DRIVE_APPDATA, DriveScopes.DRIVE_FILE, DriveScopes.DRIVE_METADATA_READONLY))
    .setServiceAccountPrivateKeyFromP12File(p12)
    .build();

List<com.google.api.services.drive.model.File> files = drive.files()
    .list()
    .setSpaces("drive")
    .setFields("nextPageToken, files(id, name, webViewLink)")
    .setPageSize(10)
    .execute() // < --- exception is thrown here
    .getFiles();

for(com.google.api.services.drive.model.File f : files) {
    System.out.println(f.getName());
}

The stack trace is:

com.google.api.client.auth.oauth2.TokenResponseException: 401 Unauthorized
    at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
    at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
    at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
    at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:384)
    at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
    at com.google.api.client.auth.oauth2.Credential.intercept(Credential.java:217)
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:868)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)

I could note that if I comment out the line .setServiceAccountUser("[email protected]"), I am able to execute code and it prints "Getting started" in my System.out.println.

I've seen many posts with this problem but none with a concrete solution. Then, I looked at documentation referenced above and it says many times about G Suite and then I realize it could be that these requests only work with a G Suite account. Am I right? If not, how can I make it work?


Solution

  • Yes, domain-wide delegation for service accounts is only for GSuite users because GSuite admins must grant service accounts domain-wide authority -- see Step 4 here: https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority

    Consider using regular OAuth (https://developers.google.com/identity/protocols/OAuth2WebServer) to grant access to your web app from your gmail account.