Skip to main content

Overview

Several providers require OAuth authentication instead of static API keys:
  • Anthropic - Claude Pro/Max subscription
  • OpenAI Codex - ChatGPT Plus/Pro subscription (access to GPT-5.x Codex models)
  • GitHub Copilot - Copilot subscription
  • Google Gemini CLI - Gemini 2.0/2.5 via Google Cloud Code Assist (free tier or paid)
  • Antigravity - Free Gemini 3, Claude, GPT-OSS via Google Cloud

OAuth Providers

The toolkit includes built-in OAuth support for these providers.

getOAuthProvider()

Get an OAuth provider by ID:
function getOAuthProvider(
  id: OAuthProviderId
): OAuthProviderInterface | undefined
id
OAuthProviderId
required
Provider identifier: 'anthropic', 'openai-codex', 'github-copilot', 'google-gemini-cli', or 'google-antigravity'.

getOAuthProviders()

Get all registered OAuth providers:
function getOAuthProviders(): OAuthProviderInterface[]

registerOAuthProvider()

Register a custom OAuth provider:
function registerOAuthProvider(
  provider: OAuthProviderInterface
): void

OAuthProviderInterface

Interface for OAuth provider implementations:
interface OAuthProviderInterface {
  readonly id: OAuthProviderId;
  readonly name: string;
  
  login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
  usesCallbackServer?: boolean;
  refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials>;
  getApiKey(credentials: OAuthCredentials): string;
  modifyModels?(models: Model<Api>[], credentials: OAuthCredentials): Model<Api>[];
}
id
string
Unique provider identifier.
name
string
Human-readable provider name.
login
function
Initiate login flow and return credentials to persist.
usesCallbackServer
boolean
Whether login uses a local callback server and supports manual code input.
refreshToken
function
Refresh expired credentials and return updated credentials to persist.
getApiKey
function
Convert credentials to API key string for the provider.
modifyModels
function
Optional: Modify models for this provider (e.g., update baseUrl).

Login Flow

Each provider has a dedicated login function.

loginAnthropic()

function loginAnthropic(
  callbacks: OAuthLoginCallbacks
): Promise<OAuthCredentials>

loginOpenAICodex()

function loginOpenAICodex(
  callbacks: OAuthLoginCallbacks
): Promise<OAuthCredentials>

loginGitHubCopilot()

function loginGitHubCopilot(
  callbacks: OAuthLoginCallbacks
): Promise<OAuthCredentials>

loginGeminiCli()

function loginGeminiCli(
  callbacks: OAuthLoginCallbacks
): Promise<OAuthCredentials>

loginAntigravity()

function loginAntigravity(
  callbacks: OAuthLoginCallbacks
): Promise<OAuthCredentials>

OAuthLoginCallbacks

Callbacks for handling the login flow:
interface OAuthLoginCallbacks {
  onAuth: (info: OAuthAuthInfo) => void;
  onPrompt: (prompt: OAuthPrompt) => Promise<string>;
  onProgress?: (message: string) => void;
  onManualCodeInput?: () => Promise<string>;
  signal?: AbortSignal;
}
onAuth
(info: OAuthAuthInfo) => void
required
Called when authentication URL is ready. Display URL and instructions to user.
interface OAuthAuthInfo {
  url: string;
  instructions?: string;
}
onPrompt
(prompt: OAuthPrompt) => Promise<string>
required
Called when user input is needed. Return the user’s response.
interface OAuthPrompt {
  message: string;
  placeholder?: string;
  allowEmpty?: boolean;
}
onProgress
(message: string) => void
Optional progress updates during login.
onManualCodeInput
() => Promise<string>
Optional callback for manual authorization code input (when callback server fails).
signal
AbortSignal
Optional abort signal to cancel login.

Login Example

import { loginGitHubCopilot, type OAuthCredentials } from '@mariozechner/pi-ai';
import { writeFileSync } from 'fs';
import * as readline from 'readline/promises';

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

const credentials: OAuthCredentials = await loginGitHubCopilot({
  onAuth: (info) => {
    console.log(`\nOpen this URL: ${info.url}`);
    if (info.instructions) {
      console.log(info.instructions);
    }
  },
  
  onPrompt: async (prompt) => {
    const answer = await rl.question(`${prompt.message} `);
    return answer;
  },
  
  onProgress: (message) => {
    console.log(`[Progress] ${message}`);
  },
  
  onManualCodeInput: async () => {
    return await rl.question('Enter authorization code: ');
  }
});

rl.close();

// Store credentials (your responsibility)
const auth = {
  'github-copilot': {
    type: 'oauth',
    ...credentials
  }
};

writeFileSync('auth.json', JSON.stringify(auth, null, 2));
console.log('Saved credentials to auth.json');

Token Management

OAuthCredentials

Credentials returned by login and refresh functions:
type OAuthCredentials = {
  refresh: string;
  access: string;
  expires: number;  // Unix timestamp in milliseconds
  [key: string]: unknown;  // Provider-specific fields
}

getOAuthApiKey()

Get API key from credentials, automatically refreshing if expired:
function getOAuthApiKey(
  providerId: OAuthProviderId,
  credentials: Record<string, OAuthCredentials>
): Promise<{ newCredentials: OAuthCredentials; apiKey: string } | null>
providerId
OAuthProviderId
required
The OAuth provider ID.
credentials
Record<string, OAuthCredentials>
required
Map of provider IDs to credentials.
result
{ newCredentials: OAuthCredentials; apiKey: string } | null
Returns updated credentials and API key, or null if no credentials found.
Throws Error if refresh fails.

Example

import { getModel, complete, getOAuthApiKey } from '@mariozechner/pi-ai';
import { readFileSync, writeFileSync } from 'fs';

// Load stored credentials
const auth = JSON.parse(readFileSync('auth.json', 'utf-8'));

// Get API key (refreshes if expired)
const result = await getOAuthApiKey('github-copilot', auth);

if (!result) {
  throw new Error('Not logged in to GitHub Copilot');
}

// Save potentially refreshed credentials
auth['github-copilot'] = { type: 'oauth', ...result.newCredentials };
writeFileSync('auth.json', JSON.stringify(auth, null, 2));

// Use the API key
const model = getModel('github-copilot', 'gpt-4o');
const response = await complete(model, {
  messages: [{ role: 'user', content: 'Hello!' }]
}, {
  apiKey: result.apiKey
});

console.log(response.content);

refreshOAuthToken()

Manually refresh a token:
function refreshOAuthToken(
  providerId: OAuthProviderId,
  credentials: OAuthCredentials
): Promise<OAuthCredentials>
Deprecated: Use getOAuthProvider(id).refreshToken() instead.

Provider-Specific Functions

GitHub Copilot

getGitHubCopilotBaseUrl()

Get the base URL for GitHub Copilot API:
function getGitHubCopilotBaseUrl(
  token?: string,
  enterpriseDomain?: string
): string
Extracts base URL from token’s proxy-ep field, or constructs from enterprise domain.

normalizeDomain()

Normalize domain input for enterprise GitHub:
function normalizeDomain(input: string): string | null
Accepts URLs or bare domains, returns normalized hostname.

Google Cloud

refreshGoogleCloudToken()

Refresh Google Cloud OAuth tokens (Gemini CLI and Antigravity):
function refreshGoogleCloudToken(
  credentials: OAuthCredentials
): Promise<OAuthCredentials>

CLI Tool

The quickest way to authenticate is via the CLI:
# Interactive provider selection
npx @mariozechner/pi-ai login

# Login to specific provider
npx @mariozechner/pi-ai login anthropic
npx @mariozechner/pi-ai login github-copilot
npx @mariozechner/pi-ai login google-gemini-cli

# List available providers
npx @mariozechner/pi-ai list
Credentials are saved to auth.json in the current directory.

Environment Variables

Some providers support environment variable auth as an alternative to OAuth:
ProviderEnvironment Variable
AnthropicANTHROPIC_OAUTH_TOKEN (OAuth token from login)
GitHub CopilotCOPILOT_GITHUB_TOKEN, GH_TOKEN, or GITHUB_TOKEN
Google Gemini CLIOAuth only (no env var support)
AntigravityOAuth only (no env var support)
OpenAI CodexOAuth only (no env var support)

Provider Notes

OpenAI Codex

Requires ChatGPT Plus or Pro subscription. Provides access to GPT-5.x Codex models with extended context windows and reasoning. The library automatically handles session-based prompt caching when sessionId is provided in stream options. Set transport option to "sse", "websocket", or "auto" for transport selection. WebSocket connections with sessionId are reused per session and expire after 5 minutes of inactivity.

Azure OpenAI

Azure OpenAI uses API key auth, not OAuth. Set AZURE_OPENAI_API_KEY and either AZURE_OPENAI_BASE_URL or AZURE_OPENAI_RESOURCE_NAME. Use AZURE_OPENAI_API_VERSION to override API version (defaults to v1). Deployment names are treated as model IDs by default. Override with azureDeploymentName option or AZURE_OPENAI_DEPLOYMENT_NAME_MAP using comma-separated pairs like gpt-4o-mini=my-deployment,gpt-4o=prod.

GitHub Copilot

If you get “The requested model is not supported” error:
  1. Open VS Code
  2. Open Copilot Chat
  3. Click the model selector
  4. Select the model (it will show a warning icon)
  5. Click “Enable”

Google Gemini CLI / Antigravity

These use Google Cloud OAuth. The apiKey returned by getOAuthApiKey() is a JSON string containing both the token and project ID. For paid Cloud Code Assist subscriptions, set GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_PROJECT_ID to your project ID.

Vertex AI (ADC)

Vertex AI uses Application Default Credentials (ADC), not OAuth:
# Local development
gcloud auth application-default login
export GOOGLE_CLOUD_PROJECT="my-project"
export GOOGLE_CLOUD_LOCATION="us-central1"

# CI/Production (service account)
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
See Vertex AI documentation for details.

Build docs developers (and LLMs) love