C.W.K.
Stream
Lesson 02 of 04 · published

Token Refresh & loadCodeAssist

~14 min · oauth, refresh-token, load-code-assist

Level 0Spark
0 XP0/35 lessons0/10 achievements
0/140 XP to next level140 XP to go0% complete

The OAuth dance

Standard Google OAuth: an access token is good for ~1 hour. After that you POST the refresh token to oauth2.googleapis.com/token with grant_type=refresh_token, and you get a new access token back. Persist the new expiry; rotate before it dies.

loadCodeAssist — the pre-flight you can't skip

Before the first generate call on the OAuth path, you must call loadCodeAssist. It returns a cloudaicompanionProject string that you have to pass in every subsequent generateContent body's project field. Skip it and you get HTTP 500 with a confusing error.

The OAuth client credentials are public

The Gemini CLI ships with hardcoded client_id and client_secret values. They're public — anyone running the CLI uses the same pair. Google treats them as identifiers, not secrets. You can use them for your own OAuth flow.

Code

Refresh the access token·python
import json, os, time, requests

# Public — same values the Gemini CLI uses
GEMINI_CLI_CLIENT_ID     = '681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com'
GEMINI_CLI_CLIENT_SECRET = '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'

CREDS_PATH = os.path.expanduser('~/.gemini/oauth_creds.json')

def get_access_token():
    with open(CREDS_PATH) as f:
        creds = json.load(f)

    # Refresh if expired (or about to)
    if creds.get('expiry_date', 0) < (time.time() + 60) * 1000:
        resp = requests.post('https://oauth2.googleapis.com/token', data={
            'client_id':     GEMINI_CLI_CLIENT_ID,
            'client_secret': GEMINI_CLI_CLIENT_SECRET,
            'refresh_token': creds['refresh_token'],
            'grant_type':    'refresh_token',
        })
        resp.raise_for_status()
        new = resp.json()
        creds['access_token'] = new['access_token']
        creds['expiry_date']  = int(time.time() * 1000) + new['expires_in'] * 1000
        with open(CREDS_PATH, 'w') as f:
            json.dump(creds, f)

    return creds['access_token']
loadCodeAssist — get your project string·python
import requests

def load_code_assist(access_token):
    resp = requests.post(
        'https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist',
        headers={
            'Authorization': f'Bearer {access_token}',
            'Content-Type': 'application/json',
        },
        json={
            'cloudaicompanionProject': '',
            'metadata': {
                'ideType':    'IDE_UNSPECIFIED',
                'platform':   'PLATFORM_UNSPECIFIED',
                'pluginType': 'GEMINI',
            },
        },
    )
    resp.raise_for_status()
    return resp.json().get('cloudaicompanionProject')
Putting it together — bootstrap an OAuth-authed call·python
token   = get_access_token()
project = load_code_assist(token)  # cache this — usually stable per-user

resp = requests.post(
    'https://cloudcode-pa.googleapis.com/v1internal:generateContent',
    headers={
        'Authorization': f'Bearer {token}',
        'Content-Type':  'application/json',
    },
    json={
        'model':   'gemini-2.5-flash',
        'project': project,
        'request': {
            'contents': [{'role': 'user', 'parts': [{'text': 'Hello'}]}],
            'generationConfig': {},
        },
    },
)
print(resp.json()['response']['candidates'][0]['content']['parts'][0]['text'])

External links

Exercise

Implement get_access_token() and load_code_assist() from scratch in a small Python script. Use your own ~/.gemini/oauth_creds.json. Confirm the flow: refresh works, loadCodeAssist returns a project, and a manual v1internal:generateContent POST returns text. If you don't have Gemini CLI installed, write the OAuth dance from a fresh client_secrets file instead.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.