This document captures the testing needed to definitively determine which GCP project needs APIs enabled for each authentication method.
When calling Google Workspace APIs, you may get "API not enabled" errors. The question is: which project needs the API enabled?
There are several candidates:
- OAuth Client Project - The project where
client_secrets.jsonwas created - ADC Quota Project - Set via
gcloud auth application-default set-quota-project - gcloud Config Project - Set via
gcloud config set project
We need to isolate which one(s) matter for each authentication method.
| Authentication Method | Where to Enable APIs (assumed) |
|---|---|
gwsa Profile (User-Provided OAuth Client) |
OAuth Client Project |
| ADC with Google's Built-in OAuth Client | Quota Project |
| ADC with a User-Provided OAuth Client | OAuth Client Project |
Open questions:
- Does
gcloud config projectever matter? - Are "quota project" and "API enablement project" always the same?
- Does it differ between personal Gmail and corporate/Workspace accounts?
You need three distinct GCP projects where you have Owner or Editor access:
- Project A - Will be used as OAuth client project
- Project B - Will be used as ADC quota project
- Project C - Will be used as gcloud config project
Ensure none of them have the test API enabled initially. Use an API that's easy to toggle, like docs.googleapis.com.
gcloud services list --enabled --project=PROJECT_A | grep -i docs
gcloud services list --enabled --project=PROJECT_B | grep -i docs
gcloud services list --enabled --project=PROJECT_C | grep -i docsgcloud services disable docs.googleapis.com --project=PROJECT_A --force
gcloud services disable docs.googleapis.com --project=PROJECT_B --force
gcloud services disable docs.googleapis.com --project=PROJECT_C --force- Go to Google Cloud Console → Project A → APIs & Services → Credentials
- Create OAuth client ID (Desktop app)
- Download as
client_secrets.json
# Set config project to C
gcloud config set project PROJECT_C
# Verify
gcloud config get-value project # Should show PROJECT_CDuring test setup, a critical difference in gcloud's behavior was observed:
-
gcloud config set project <PROJECT_ID>is permissive.- User Context Checked: The active
gcloudCLI account (set viagcloud config set account). - Permission Checked: Performs a basic check for general project visibility (e.g.,
resourcemanager.projects.get). - Behavior: If the permission is missing, it only issues a strong warning but allows the operation to proceed. This is because it only affects the local CLI's default project flag for subsequent
gcloudcommands. The real permission check happens when those subsequent commands are run.
- User Context Checked: The active
-
gcloud auth application-default set-quota-project <PROJECT_ID>is strict.- User Context Checked: The user authenticated via
gcloud auth application-default login. - Permission Checked: Performs an immediate, mandatory check for the
serviceusage.services.usepermission. - Behavior: If the permission is missing, the command fails. This is a hard requirement because this setting has direct billing and quota implications for the project, and the authorizing user must have the right to incur that usage.
- User Context Checked: The user authenticated via
This distinction is crucial for understanding how to properly configure the testing environment and interpret setup errors.
Three projects involved:
- Project A = OAuth client project (where
client_secrets.jsonwas created) - Project B = ADC quota project (set via
gcloud auth application-default set-quota-project) - Project C = gcloud config project (set via
gcloud config set project)
For each auth mode, we test all three permutations: API enabled in only A, only B, or only C.
gwsa profiles, managed via gwsa profiles add, use a user-provided OAuth client ID (from client_secrets.json). The tool manages the token generation and refresh flow itself, storing the resulting token in ~/.config/gworkspace-access/profiles/ and operating independently of the gcloud ADC file.
-
Client Type: Custom OAuth Client (confirmed by
gwsa.sdk.auth.get_credentialsreturning a source path touser_token.json) -
One-time setup:
gwsa client import /path/to/client_secrets.json(created in Project A)gwsa profiles add test-profilegwsa profiles use test-profilegcloud config set project PROJECT_C(to isolate it from A)gcloud auth application-default set-quota-project PROJECT_B(even though not using ADC, to ensure isolation)
-
Verify three distinct projects:
OAuth client project: PROJECT_A(fromclient_secrets.json)Quota project: $(cat ~/.config/gcloud/application_default_credentials.json | jq -r '.quota_project_id')Config project: $(gcloud config get-value project)
Scenario 1.1: API enabled ONLY in OAuth client project (A)
# Ensure all disabled first
gcloud services disable docs.googleapis.com --project=PROJECT_A --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_B --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_C --force 2>/dev/null
# Enable only in A
gcloud services enable docs.googleapis.com --project=PROJECT_A
# TEST
python3 test_script.py --mode=token-profile
# Record result: PASS / FAIL
# Cleanup
gcloud services disable docs.googleapis.com --project=PROJECT_A --forceScenario 1.2: API enabled ONLY in quota project (B)
# Ensure all disabled first
gcloud services disable docs.googleapis.com --project=PROJECT_A --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_B --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_C --force 2>/dev/null
# Enable only in B
gcloud services enable docs.googleapis.com --project=PROJECT_B
# TEST
python3 test_script.py --mode=token-profile
# Record result: PASS / FAIL
# Cleanup
gcloud services disable docs.googleapis.com --project=PROJECT_B --forceScenario 1.3: API enabled ONLY in gcloud config project (C)
# Ensure all disabled first
gcloud services disable docs.googleapis.com --project=PROJECT_A --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_B --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_C --force 2>/dev/null
# Enable only in C
gcloud services enable docs.googleapis.com --project=PROJECT_C
# TEST
python3 test_script.py --mode=token-profile
# Record result: PASS / FAIL
# Cleanup
gcloud services disable docs.googleapis.com --project=PROJECT_C --forceThis method is initiated by running gcloud auth application-default login without the --client-id-file flag. It uses Google's own internal OAuth client for authentication.
-
Client Type: Google's Built-in ADC Client (confirmed by
google.auth.default()returningdiscovered project_idand the absence of--client-id-filein thegcloud auth application-default logincommand) -
Note: Pure ADC is typically blocked for Workspace scopes when used with personal Gmail accounts. It is crucial to use a corporate/Workspace account for these tests.
-
One-time setup:
gcloud auth application-default login \ --scopes=https://www.googleapis.com/auth/documents.readonly,openid,https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/cloud-platformgcloud auth application-default set-quota-project PROJECT_Bgcloud config set project PROJECT_C
-
Verify:
OAuth client project: N/A(using Google'sgcloudclient)Quota project: $(cat ~/.config/gcloud/application_default_credentials.json | jq -r '.quota_project_id')Config project: $(gcloud config get-value project)
Scenario 2.1: API enabled ONLY in quota project (B)
# Ensure all disabled first
gcloud services disable docs.googleapis.com --project=PROJECT_B --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_C --force 2>/dev/null
# Enable only in B
gcloud services enable docs.googleapis.com --project=PROJECT_B
# TEST
python3 test_script.py --mode=adc
# Record result: PASS / FAIL
# Cleanup
gcloud services disable docs.googleapis.com --project=PROJECT_B --forceScenario 2.2: API enabled ONLY in gcloud config project (C)
# Ensure all disabled first
gcloud services disable docs.googleapis.com --project=PROJECT_B --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_C --force 2>/dev/null
# Enable only in C
gcloud services enable docs.googleapis.com --project=PROJECT_C
# TEST
python3 test_script.py --mode=adc
# Record result: PASS / FAIL
# Cleanup
gcloud services disable docs.googleapis.com --project=PROJECT_C --forceNote: No test for "OAuth client project" because pure ADC uses Google's client, not yours.
This method is initiated by running gcloud auth application-default login with the --client-id-file flag. It uses a custom, user-provided OAuth client, but stores the resulting credentials in the gcloud ADC file. Three projects are in play.
One-time setup:
# Login with ADC using YOUR client
gcloud auth application-default login \
--client-id-file=/path/to/client_secrets.json \
--scopes=https://www.googleapis.com/auth/documents.readonly,openid,https://www.googleapis.com/auth/userinfo.email
# Set quota project to B (different from OAuth client project A)
gcloud auth application-default set-quota-project PROJECT_B
# Set config project to C
gcloud config set project PROJECT_CVerify three distinct projects:
echo "OAuth client project: PROJECT_A (from client_secrets.json)"
echo "Quota project: $(cat ~/.config/gcloud/application_default_credentials.json | jq -r '.quota_project_id')"
echo "Config project: $(gcloud config get-value project)"Scenario 3.1: API enabled ONLY in OAuth client project (A)
# Ensure all disabled first
gcloud services disable docs.googleapis.com --project=PROJECT_A --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_B --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_C --force 2>/dev/null
# Enable only in A
gcloud services enable docs.googleapis.com --project=PROJECT_A
# TEST
python3 test_script.py --mode=adc
# Record result: PASS / FAIL
# Cleanup
gcloud services disable docs.googleapis.com --project=PROJECT_A --forceScenario 3.2: API enabled ONLY in quota project (B)
# Ensure all disabled first
gcloud services disable docs.googleapis.com --project=PROJECT_A --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_B --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_C --force 2>/dev/null
# Enable only in B
gcloud services enable docs.googleapis.com --project=PROJECT_B
# TEST
python3 test_script.py --mode=adc
# Record result: PASS / FAIL
# Cleanup
gcloud services disable docs.googleapis.com --project=PROJECT_B --forceScenario 3.3: API enabled ONLY in gcloud config project (C)
# Ensure all disabled first
gcloud services disable docs.googleapis.com --project=PROJECT_A --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_B --force 2>/dev/null
gcloud services disable docs.googleapis.com --project=PROJECT_C --force 2>/dev/null
# Enable only in C
gcloud services enable docs.googleapis.com --project=PROJECT_C
# TEST
python3 test_script.py --mode=adc
# Record result: PASS / FAIL
# Cleanup
gcloud services disable docs.googleapis.com --project=PROJECT_C --forceSimple Python test script to check if Docs API is accessible:
import google.auth
from googleapiclient.discovery import build
def test_docs_api(use_adc=False, profile=None):
if use_adc:
creds, project = google.auth.default()
print(f"Using ADC, project={project}")
else:
from gwsa.sdk.auth import get_credentials
creds, source = get_credentials(profile=profile)
print(f"Using profile, source={source}")
service = build('docs', 'v1', credentials=creds)
try:
# Try to get a nonexistent doc - 404 means API is enabled
service.documents().get(documentId='nonexistent').execute()
except Exception as e:
error = str(e)
if '404' in error or 'not found' in error.lower():
print("SUCCESS: API is enabled (got 404 for nonexistent doc)")
return True
elif 'not been used' in error or 'disabled' in error:
print(f"FAIL: API not enabled")
print(f"Error: {error[:300]}")
return False
else:
print(f"OTHER ERROR: {error[:300]}")
return None
# For token profile:
# test_docs_api(profile='test-profile')
# For ADC:
# test_docs_api(use_adc=True)Fill in after testing:
Tested on: December 15, 2025 Account Type: A personal Gmail account Isolation Strategy: The OAuth client project (A) was explicitly designated for client_secrets.json. The ADC Quota Project (B) and gcloud Config Project (C) were set to distinct, separate projects and did not have the API enabled. This ensured that only the OAuth client project's API enablement status would influence the outcome.
| Scenario | API Enabled In | Result | Error (if any) |
|---|---|---|---|
| 1.1 | OAuth client project (A) | PASS (Enabled) / FAIL (Disabled) | Confirmed API not enabled error when disabled in A |
| 1.2 | Quota project (B) | FAIL (Enabled in B, Disabled in A and C) | Expected failure: API not enabled. This confirms that enabling the API solely in the quota project (B) is not sufficient; the OAuth client project (A) remains pivotal. |
| 1.3 | Config project (C) | FAIL (Enabled in C, Disabled in A and B) | Expected failure: API not enabled. This confirms that enabling the API solely in the gcloud config project (C) is not sufficient; the OAuth client project (A) remains pivotal. |
Conclusion for Token Profiles:
- Which project(s) work? OAuth client project
- Which project(s) fail? Quota project and gcloud config project are not used for API enablement checks. The call will fail if the API is not enabled in the OAuth client project.
Tested on: December 15, 2025
Account Type: A corporate/Workspace account
Isolation Strategy: The ADC Quota Project (B) was explicitly set via gcloud auth application-default set-quota-project. The gcloud Config Project (C) was set to a distinct, separate project and did not have the API enabled. This ensured that only the ADC Quota Project's API enablement status would influence the outcome.
| Scenario | API Enabled In | Result | Error (if any) |
|---|---|---|---|
| 2.1 | Quota project (B) | PASS (Enabled) / FAIL (Disabled) | Confirmed API not enabled error when disabled in B |
| 2.2 | Config project (C) | FAIL (Enabled in C, Disabled in B) / FAIL (Disabled in B and C) | Expected failure: API not enabled. Confirmed consistent error pointing to Quota Project (B), proving gcloud config project (C) is not used for API enablement. |
Conclusion for Pure ADC:
- Which project(s) work? Quota project
- Which project(s) fail? gcloud config project is not used for API enablement checks. The call will fail if the API is not enabled in the quota project.
Tested on: December 15, 2025 Account Type: A personal Gmail account Isolation Strategy: A user-provided OAuth client (from Project A) was used to log in. The ADC Quota Project (B) and gcloud Config Project (C) were set to distinct, separate projects. This allowed isolation of all three project types.
| Scenario | API Enabled In | Result | Error (if any) |
|---|---|---|---|
| 3.1 | OAuth client project (A) | FAIL (Enabled in A, Disabled in B and C) | Expected failure: API not enabled. Confirmed consistent error pointing to Quota Project (B), proving OAuth client project (A) is not used. |
| 3.2 | Quota project (B) | PASS (Enabled) / FAIL (Disabled) | Confirmed API not enabled error when disabled in B. |
| 3.3 | Config project (C) | FAIL (Enabled in C, Disabled in B) | Expected failure: API not enabled. Confirmed consistent error pointing to Quota Project (B), proving gcloud config project (C) is not used. |
Conclusion for ADC with a User-Provided OAuth Client:
- Which project(s) work? Quota project
- Which project(s) fail? OAuth client project and gcloud config project are not used for API enablement checks. The call will fail if the API is not enabled in the quota project.
| Authentication Method | Project That Must Have API Enabled |
|---|---|
gwsa Profile (User-Provided OAuth Client) |
OAuth client project |
| ADC with Google's Built-in OAuth Client | ADC quota project |
| ADC with a User-Provided OAuth Client | ADC quota project |
Does gcloud config project ever matter? No, it has no impact on API enablement checks.
Is quota project and API-enabled project always the same? For ADC-based flows, yes. The quota project serves as the project where API enablement is checked and usage is attributed.
- Error message explicitly named the OAuth client project number
- Suggests OAuth client project is what matters
- Not fully isolated - didn't test if enabling in config project also works
- Test with three distinct projects:
- Config = project with no Docs API
- Quota = project with no Docs API
- OAuth client = project WITH Docs API
- Result: Docs API worked
- Conclusion: OAuth client project matters for this mode
- Not tested: Would it also work if only quota or config project had API?
- Could not test - Google's gcloud OAuth client was blocked for Workspace scopes on personal Gmail accounts ("This app is blocked" error)
- Need to test with corporate/Workspace account where pure ADC is allowed
Based on results, review and update:
-
GOOGLE-API-ACCESS.md - Main doc with "Where to Enable APIs" table
- Section: "Step 2: Which Project to Enable Them In"
- Section: "Quota Project (ADC only)"
- Troubleshooting section
-
README.md - If any high-level guidance changes
-
PROFILES.md - If profile behavior differs from documented
Current guidance in GOOGLE-API-ACCESS.md:
| If you use... | Enable APIs in... |
|---------------|-------------------|
| Token Profile | The OAuth client project |
| ADC with --client-id-file | The OAuth client project |
| Pure ADC | Your quota project |Open questions for further testing:
- Does gcloud config project ever matter for any auth mode?
- Is quota project and API-enablement project always the same for pure ADC?
- Is the guidance different for corporate vs personal accounts?
-
Quota vs API enablement: Are these truly the same project, or can they differ?
- Quota = where usage is counted/billed
- API enablement = where permission to call is checked
-
Error messages: Do error messages always indicate the correct project?
-
Service accounts: Does behavior differ for service account auth?
-
Corporate policies: Do org policies affect which project is checked?