No service account required

pisces-ai-oauth

OAuth 2.0 utility for Agent Platform (formerly Vertex AI)

No Keys, Just Login

End users authenticate with their own Google account via OAuth 2.0. No service account JSON, no key rotation, no backend secrets.

Browser & Server

GIS-based flow for the browser, token refresh for Node.js, and a PostgreSQL manager for persistent sessions. Pick what fits.

Streaming Ready

Calls streamGenerateContent directly over SSE with ReadableStream. Token-by-token output, no wrappers.

Quick Start

<script src="https://accounts.google.com/gsi/client" async></script>
<script src="lib/vertex-ai-oauth.browser.js"></script>

const auth = new VertexAIOAuth({
  clientId:  'YOUR_CLIENT_ID.apps.googleusercontent.com',
  projectId: 'your-gcp-project',
  region:    'us-central1',
  model:     'gemini-2.5-flash',
});

auth.tryAutoSignIn();              // restore token on load
loginBtn.onclick = () => auth.signIn();
const { VertexAIOAuth } = require('./lib/vertex-ai-oauth.js');

const auth = new VertexAIOAuth({
  projectId: 'my-project',
  region:    'us-central1',
  model:     'gemini-2.5-flash',
  getToken: async () => {
    const client = await gauth.getClient();
    const { token } = await client.getAccessToken();
    return { accessToken: token, expiresIn: 3600 };
  },
});

const text = await auth.collect([{ role: 'user', parts: [{ text: 'Hello!' }] }]);
const { createTokenManager } = require('./lib/vertex-ai-oauth-postgresql');

const manager = createTokenManager({
  query,                       // your pg query fn
  clientId:     process.env.GCP_OAUTH_CLIENT_ID,
  clientSecret: process.env.GCP_OAUTH_CLIENT_SECRET,
});

// auto-refreshes when expired
const creds = await manager.getValidCredentials(userId);
// → { accessToken, projectId, region } | null
// sneak peek — extracted from a production Rust client. Crate landing soon.
use pisces_ai_oauth::{OAuthCredentials, generate_pkce, build_auth_url,
                      exchange_code, get_valid_token};

let creds = OAuthCredentials {
    client_id:     "YOUR_CLIENT_ID.apps.googleusercontent.com".into(),
    client_secret: None,                // PKCE — no secret needed
    redirect_uri:  "http://127.0.0.1:8217/callback".into(),
};

let pkce = generate_pkce();
let url  = build_auth_url(&creds, Some(&pkce));
// open `url` in a browser, receive `code` on the loopback listener

let client = reqwest::Client::new();
let tokens = exchange_code(&client, &creds, &code, Some(&pkce.verifier)).await?;

// later: returns cached token, auto-refreshes near expiry
let tokens = get_valid_token(&client, &creds, &tokens).await?;