[Python] Google API Call: Request had insufficient authentication scopes

Discussão em 'Python' iniciado por Stack, Junho 14, 2025.

  1. Stack

    Stack Membro Participativo

    I have a python script that I run manually to connect to my photo library and download images. This was working for a time, and then it stopped working. Nothing changed, just time. I then let AI try to fix it and the code has changed a lot since then. From what I can tell, this is still sound... but I get the error:

    googleapiclient.errors.HttpError: <HttpError 403 when requesting https://photoslibrary.googleapis.com/v1/albums?fields=albums%28id%2Ctitle%29&pageSize=10&alt=json returned "Request had insufficient authentication scopes.". Details: "Request had insufficient authentication scopes.">

    Note: I'm not storing tokens locally, I ripped that out to force me to get a token each time as I troubleshoot this.

    I just created a new project in GCP, enabled Photos API, set up consent screen, downloaded the credentials.json, and still getting this bloody error.

    [​IMG]

    [​IMG]

    Source code


    from typing import List
    import os
    from google.oauth2.credentials import Credentials
    from google_auth_oauthlib.flow import InstalledAppFlow
    from google.auth.transport.requests import Request
    from google.auth.transport.requests import AuthorizedSession
    from googleapiclient.discovery import build
    import requests
    from models.comic import Comic
    import logging
    import json

    logger = logging.getLogger(__name__)

    class GooglePhotosIngester:
    """Handles fetching and processing images from Google Photos"""

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

    def __init__(self):
    # Get path to credentials directory relative to project root
    self.root_dir = os.path.dirname(os.path.dirname(__file__))
    self.credentials_dir = os.path.join(self.root_dir, 'credentials')
    self.credentials_path = os.path.join(self.credentials_dir, 'google_photos_credentials.json')
    self.credentials = None
    self.service = None

    # Verify credentials file exists and is readable
    if not os.path.exists(self.credentials_path):
    raise FileNotFoundError(f"Credentials file not found at {self.credentials_path}")

    try:
    with open(self.credentials_path, 'r') as f:
    creds_data = json.load(f)
    logger.info(f"Successfully loaded credentials file. Client ID: {creds_data.get('installed', {}).get('client_id', 'Not found')}")
    except Exception as e:
    logger.error(f"Error reading credentials file: {str(e)}")
    raise

    def authenticate(self) -> None:
    """Handles Google Photos authentication with fresh credentials each time"""
    try:
    logger.info("Starting authentication flow...")
    flow = InstalledAppFlow.from_client_secrets_file(
    self.credentials_path,
    self.SCOPES
    )
    logger.info("Created InstalledAppFlow with scopes: %s", self.SCOPES)

    self.credentials = flow.run_local_server(
    port=0,
    prompt='consent',
    include_granted_scopes=False
    )



    logger.info(f"Token: {self.credentials.token}")
    logger.info(f"Refresh token: {self.credentials.refresh_token}")
    logger.info(f"Expiry: {self.credentials.expiry}")
    logger.info(f"Scopes: {self.credentials.scopes}")
    try:
    self.credentials.refresh(Request())
    except Exception as e:
    logger.error(f"Refresh failed: {e}")


    # Dump the user email for clarity

    authed_session = AuthorizedSession(self.credentials)
    userinfo = authed_session.get('https://www.googleapis.com/oauth2/v1/userinfo?alt=json')
    logger.info(f"Authenticated as: {userinfo.json()}")




    logger.info("Successfully obtained credentials")
    logger.info("Credential scopes: %s", self.credentials.scopes)
    logger.info("Credential has refresh token: %s", bool(self.credentials.refresh_token))

    # Initialize the Photos Library service with the correct discovery URL
    logger.info("Building Photos Library service...")
    self.service = build(
    'photoslibrary',
    'v1',
    credentials=self.credentials,
    discoveryServiceUrl='https://photoslibrary.googleapis.com/$discovery/rest?version=v1'
    )
    logger.info("Successfully built Photos Library service")

    # Test the connection by listing albums
    logger.info("Testing connection by listing albums...")
    albums = self.service.albums().list(
    fields='albums(id,title)',
    pageSize=10
    ).execute()

    for album in albums.get('albums', []):
    logger.info(f"Album title: {album['title']}, ID: {album['id']}")

    except Exception as e:
    logger.error(f"Error during authentication: {str(e)}")
    if hasattr(e, 'resp'):
    logger.error(f"Response status: {e.resp.status}")
    logger.error(f"Response content: {e.content}")
    raise

    Output logs


    INFO:ingestion.google_photos:Starting authentication flow...
    INFO:ingestion.google_photos:Created InstalledAppFlow with scopes: ['https://www.googleapis.com/auth/photoslibrary.readonly']
    Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=xxx&redirect_uri=http%3A%2F%2Flocalhost%3A54357%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fphotoslibrary.readonly&state=xxx&prompt=consent&access_type=offline
    INFO:google_auth_oauthlib.flow:"GET /?state=MtQFaaKY9pwXXGMrvjQO5QLf1qigbV&code=4/0AUJR-x5DwOJVymCBhrL-aAwD-5OrrR1Ju9yhYDfIqyo7TgzxOh4RYnfM0leVMk2GDo5Bhg&scope=https://www.googleapis.com/auth/photoslibrary.readonly HTTP/1.1" 200 65
    INFO:ingestion.google_photos:Token: ya29.xxx5g0175
    INFO:ingestion.google_photos:Refresh token: 1//0517zxxx8w
    INFO:ingestion.google_photos:Expiry: 2025-06-14 20:19:21.889031
    INFO:ingestion.google_photos:Scopes: ['https://www.googleapis.com/auth/photoslibrary.readonly']
    INFO:ingestion.google_photos:Authenticated as: {'error': {'code': 401, 'message': 'Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', 'status': 'UNAUTHENTICATED'}}
    INFO:ingestion.google_photos:Successfully obtained credentials
    INFO:ingestion.google_photos:Credential scopes: ['https://www.googleapis.com/auth/photoslibrary.readonly']
    INFO:ingestion.google_photos:Credential has refresh token: True
    INFO:ingestion.google_photos:Building Photos Library service...
    INFO:googleapiclient.discovery_cache:file_cache is only supported with oauth2client<4.0.0
    INFO:ingestion.google_photos:Successfully built Photos Library service
    INFO:ingestion.google_photos:Testing connection by listing albums...
    WARNING:googleapiclient.http:Encountered 403 Forbidden with reason "PERMISSION_DENIED"
    ERROR:ingestion.google_photos:Error during authentication: <HttpError 403 when requesting https://photoslibrary.googleapis.com/v1/albums?fields=albums%28id%2Ctitle%29&pageSize=10&alt=json returned "Request had insufficient authentication scopes.". Details: "Request had insufficient authentication scopes.">
    ERROR:ingestion.google_photos:Response status: 403
    ERROR:ingestion.google_photos:Response content: b'{\n "error": {\n "code": 403,\n "message": "Request had insufficient authentication scopes.",\n "status": "PERMISSION_DENIED"\n }\n}\n'
    Traceback (most recent call last):
    File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
    File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
    File "/Users/davidlozzi/.cursor/extensions/ms-python.debugpy-2025.8.0-darwin-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 71, in <module>
    cli.main()
    File "/Users/davidlozzi/.cursor/extensions/ms-python.debugpy-2025.8.0-darwin-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 501, in main
    run()
    File "/Users/davidlozzi/.cursor/extensions/ms-python.debugpy-2025.8.0-darwin-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 384, in run_module
    run_module_as_main(options.target, alter_argv=True)
    File "/Users/davidlozzi/.cursor/extensions/ms-python.debugpy-2025.8.0-darwin-arm64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 228, in _run_module_as_main
    return _run_code(code, main_globals, None, "__main__", mod_spec)
    File "/Users/davidlozzi/.cursor/extensions/ms-python.debugpy-2025.8.0-darwin-arm64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 118, in _run_code
    exec(code, run_globals)
    File "/Users/davidlozzi/git/sw-panels-data/ingestion/pipeline.py", line 387, in <module>
    main()
    File "/Users/davidlozzi/git/sw-panels-data/ingestion/pipeline.py", line 384, in main
    pipeline.ingest_album(album_id, full_ingestion=args.full, incomplete_only=args.incomplete)
    File "/Users/davidlozzi/git/sw-panels-data/ingestion/pipeline.py", line 308, in ingest_album
    all_comics = self.photos_ingester.fetch_album_images(album_id)
    File "/Users/davidlozzi/git/sw-panels-data/ingestion/google_photos.py", line 113, in fetch_album_images
    self.authenticate()
    File "/Users/davidlozzi/git/sw-panels-data/ingestion/google_photos.py", line 93, in authenticate
    albums = self.service.albums().list(
    File "/Users/davidlozzi/git/sw-panels-data/.venv/lib/python3.9/site-packages/googleapiclient/_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
    File "/Users/davidlozzi/git/sw-panels-data/.venv/lib/python3.9/site-packages/googleapiclient/http.py", line 938, in execute
    raise HttpError(resp, content, uri=self.uri)
    googleapiclient.errors.HttpError: <HttpError 403 when requesting https://photoslibrary.googleapis.com/v1/albums?fields=albums%28id%2Ctitle%29&pageSize=10&alt=json returned "Request had insufficient authentication scopes.". Details: "Request had insufficient authentication scopes.">

    Continue reading...

Compartilhe esta Página