ValueError: Client secrets must be for a web or installed app
Asked Answered
D

4

58

I am running the quickstart.py example code under Python Quickstart and I am getting the following error:

ValueError: Client secrets must be for a web or installed app.

I created a credentials.json file with project owner rights.

The error occurs in the following piece of code:

if os.path.exists('token.pickle'):
    with open('token.pickle', 'rb') as token:
        creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
        creds.refresh(Request())
    else:
        flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
        creds = flow.run_local_server()
    # Save the credentials for the next run
    with open('token.pickle', 'wb') as token:
        pickle.dump(creds, token)

I notice also that the token.pickle file is not being created. This is the error output:

  File "updateSlidev01.py", line 51, in <module>
    main()
  File "updateSlidev01.py", line 31, in main
    flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
  File "/Library/Python/2.7/site-packages/google_auth_oauthlib/flow.py", line 174, in from_client_secrets_file
    return cls.from_client_config(client_config, scopes=scopes, **kwargs)
  File "/Library/Python/2.7/site-packages/google_auth_oauthlib/flow.py", line 147, in from_client_config
    'Client secrets must be for a web or installed app.')
ValueError: Client secrets must be for a web or installed app.
Donnadonnamarie answered 4/6, 2019 at 13:46 Comment(2)
How did you create that credentials.json file?Agreed
Hi! That was exactly the problem. I answered the question below. Thanks a lot!Donnadonnamarie
D
51

The problem was that I was using the json generated under Service account keys Manage service accounts and not the one under OAuth 2.0 client IDs.

Follow this instructor for creating oauth client id. https://developers.google.com/workspace/guides/create-credentials#oauth-client-id

Donnadonnamarie answered 13/6, 2019 at 20:25 Comment(4)
What can you use instead if the json was created as a service account?Unsex
Found the solution for it here developers.google.com/identity/protocols/…Unsex
@Unsex Thanks for that, always appreciate it when someone comes back to answer their question.Mancy
Hi - so can I ask does this mean a normal serivce account cannot be used for such an app?Wivinah
W
65

For anyone coming here because they would like to actually connect to the GCP calendar API via a service-account and not this Oauth2 client id, create the creds object in the original example as follows:

from google.oauth2 import service_account

SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
SERVICE_ACCOUNT_FILE = '/path/to/service.json'

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

Assuming the service-account is configured with the correct access, this will access the calendar without prompting user for confirmation.

Wivinah answered 24/10, 2020 at 1:34 Comment(3)
Why would we use a scope of "sqlservice.admin" if we're trying to connect to the GCP calendar API? I think you meant "calendar.readonly." Please correct if I'm right.Maxama
Probably - I think this was just the scope I happened to be using at the time. But yes, obviously use the appropriate scope for the task at hand.Wivinah
You can find the source url for service account delegation here: developers.google.com/identity/protocols/oauth2/…Touch
D
51

The problem was that I was using the json generated under Service account keys Manage service accounts and not the one under OAuth 2.0 client IDs.

Follow this instructor for creating oauth client id. https://developers.google.com/workspace/guides/create-credentials#oauth-client-id

Donnadonnamarie answered 13/6, 2019 at 20:25 Comment(4)
What can you use instead if the json was created as a service account?Unsex
Found the solution for it here developers.google.com/identity/protocols/…Unsex
@Unsex Thanks for that, always appreciate it when someone comes back to answer their question.Mancy
Hi - so can I ask does this mean a normal serivce account cannot be used for such an app?Wivinah
C
1

In my case credentials.json file had wrong keys names.

File looked like this:

{"SCc":{"client_id":"****","project_id":"****","wnc":"****","dVc":"****","vnc":"****","p2a":"****","kNc":["http://localhost"]}}

I changed it to:

{"installed":{"client_id":"****","project_id":"****","auth_uri":"****","token_uri":"****","auth_provider_x509_cert_url":"****","client_secret":"****","redirect_uris":["http://localhost"]}}

And works fine.

Chris answered 23/8, 2023 at 12:40 Comment(1)
Thank you! I was having the exact same problem. Fixing the keys in this way fixes things. But such a bizarre issueShaikh
S
-1

Posting this for others, the issue would be downloading the wrong type of keys. You will see this error if you download the 'Service Accounts' keys rather than the 'OAuth 2.0 Client IDs'.

To resolve the error:

  1. Go to the credentials page (https://console.cloud.google.com/apis/credentials).

enter image description here

  1. Click on the name of the 'OAuth 2.0 Client IDs' client, in that case that would be 'Desktop client 1' for me. Then, click on the 'Download JSON' button.

enter image description here

  1. Rename the file into credentials.json, and you should be able to run the gmail/quickstart/quickstart.py code provided by google. For reference, as of 8 July 2024, for reference, this is a snippet of the code:

     import os
     import google.auth
     from google.auth.transport.requests import Request
     from google.oauth2.credentials import Credentials
     from google_auth_oauthlib.flow import InstalledAppFlow
     from googleapiclient.discovery import build
     from googleapiclient.errors import HttpError
    
     SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
    
     def main():
       creds = None
    
    
     if os.path.exists("token.json"):
         creds = Credentials.from_authorized_user_file("token.json", SCOPES)
    
     if not creds or not creds.valid:
         if creds and creds.expired and creds.refresh_token:
             creds.refresh(Request())
         else:
             flow = InstalledAppFlow.from_client_secrets_file(
                 "credentials.json", SCOPES
             )
             creds = flow.run_local_server(port=0)
    
         with open("token.json", "w") as token:
             token.write(creds.to_json())
    
     try:
         service = build("gmail", "v1", credentials=creds)
    
         results = service.users().labels().list(userId="me").execute()
         labels = results.get("labels", [])
    
         if not labels:
             print("No labels found.")
             return
         print("Labels:")
         for label in labels:
             print(label["name"])
    
     except HttpError as error:
         print(f"An error occurred: {error}")
    
    main()
    
  1. You should a message in the following lines:

    Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=[...]&access_type=offline

Sigridsigsmond answered 8/7, 2024 at 14:59 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.