7
votes

I'm following the Quickstart on https://developers.google.com/drive/api/v3/quickstart/python. I've enabled the drive API through the page, loaded the credentials.json and can successfully list files in my google drive. However when I wanted to download a file, I got the message

`The user has not granted the app ####### read access to the file`

Do I need to do more than putting that scope in my code or do I need to activate something else?

SCOPES = 'https://www.googleapis.com/auth/drive.file'
client.flow_from_clientsecrets('credentials.json', SCOPES)
3
if you have not requested a token (i.e. completed an OAuth Flow), then you have not received permission yet. I.e. in the quickstarts there is usually code relating to if credentials.invalid: ... where something like flow.run_local_server() is used to display a prompt to the user and then request the access (and perhaps also refresh) token, depending on your application type. - tehhowch
Also note that the drive.file scope means there are special rules about file access. Namely, the user must "pick" your app to open a file with (or create the file with the app) in order for your app to gain permissions to use that file. - tehhowch
So, credentials aren't invalid, otherwise I wouldn't be able to list the content. So for the scope, drive.file should grant downloads. - user1767754
Your statement "drive.file should grant downloads" is, in general, false. As I mentioned above, there are special rules regarding file access with drive.file scope. You mention following the quickstart, which uses drive.metadata.readonly to allow listing all the files. You still need to get explicit user permission to obtain content, because you are using the drive.file scope and a file that was not created with your app. Hence, you must ask the user to use your app to open the file. Review the Drive File Picker code. - tehhowch
Where do I get "explicit user permission to obtain content" - user1767754

3 Answers

10
votes

Once you go through the Quick-Start Tutorial initially the Scope is given as:

SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly'

So after listing files and you decide to download, it won't work as you need to generate the the token again, so changing scope won't recreate or prompt you for the 'google authoriziation' which happens on first run.

To force generate the token, just delete your curent token or use a new file to store your key from the storage:

store = file.Storage('tokenWrite.json')
1
votes

I ran into the same error. I authorized the entire scope, then retrieved the file, and use the io.Base class to stream the data into a file. Note, you'll need to create the file first.

from __future__ import print_function
from googleapiclient.discovery import build
import io
from apiclient import http
from google.oauth2 import service_account

SCOPES = ['https://www.googleapis.com/auth/drive']

SERVICE_ACCOUNT_FILE = 'credentials.json'
FILE_ID = <file-id>

credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)

service = build('drive', 'v3', credentials=credentials)

def download_file(service, file_id, local_fd):

  request = service.files().get_media(fileId=file_id)
  media_request = http.MediaIoBaseDownload(local_fd, request)

  while True:    
    _, done = media_request.next_chunk()

    if done:
      print ('Download Complete')
      return

file_io_base = open('file.csv','wb')

download_file(service=service,file_id=FILE_ID,local_fd=file_io_base)

Hope that helps.

0
votes

Delete token.pickle and re-run program.

Elaboration:- Once you run Quickstart Example, It stores a token.pickle.

Thereafter, even if you change the scope in google api console and add the following the scope in your code :-

SCOPES = ['https://www.googleapis.com/auth/drive']

It will not work until you delete and earlier scope's token.pickle. Once deleted rerun program. A tab will appear, authorize the app and Done.