No service account required
OAuth 2.0 utility for Agent Platform (formerly Vertex AI)
End users authenticate with their own Google account via OAuth 2.0. No service account JSON, no key rotation, no backend secrets.
GIS-based flow for the browser, token refresh for Node.js, and a PostgreSQL manager for persistent sessions. Pick what fits.
Calls streamGenerateContent directly over SSE with ReadableStream. Token-by-token output, no wrappers.
<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?;