Skip to main content

Overview

ADK-TS provides a comprehensive authentication system for securing tool access. The auth system supports multiple authentication schemes including API keys, HTTP auth, OAuth2, and OpenID Connect.

Authentication Components

The auth system consists of three main components:
import { 
  AuthScheme,      // Defines how to authenticate
  AuthConfig,      // Tool auth configuration
  AuthCredential,  // User credentials
  AuthHandler,     // Orchestrates authentication
} from '@iqai/adk';

Auth Schemes

API Key Authentication

Authenticate using API keys in headers, query params, or cookies:
import { ApiKeyScheme, AuthSchemeType } from '@iqai/adk';

const apiKeyScheme = new ApiKeyScheme({
  in: 'header',              // 'header' | 'query' | 'cookie'
  name: 'X-API-Key',         // Header/param name
  description: 'API key for service authentication',
});

console.log(apiKeyScheme.type); // AuthSchemeType.APIKEY
Source: packages/adk/src/auth/auth-schemes.ts:28

HTTP Authentication

Support for HTTP auth schemes including Basic, Bearer, and Digest:
import { HttpScheme } from '@iqai/adk';

// Bearer token
const bearerScheme = new HttpScheme({
  scheme: 'bearer',
  bearerFormat: 'JWT',
  description: 'JWT bearer token',
});

// Basic auth
const basicScheme = new HttpScheme({
  scheme: 'basic',
  description: 'Username and password',
});
Supported schemes:
  • basic - Username/password encoded in Base64
  • bearer - Bearer tokens (JWT, OAuth2, etc.)
  • digest - Digest access authentication
  • other - Custom HTTP auth schemes
Source: packages/adk/src/auth/auth-schemes.ts:62

OAuth2 Authentication

Complete OAuth2 flow support:
import { OAuth2Scheme } from '@iqai/adk';

const oauth2Scheme = new OAuth2Scheme({
  flows: {
    authorizationCode: {
      authorizationUrl: 'https://api.example.com/oauth/authorize',
      tokenUrl: 'https://api.example.com/oauth/token',
      refreshUrl: 'https://api.example.com/oauth/refresh',
      scopes: {
        'read:data': 'Read access to data',
        'write:data': 'Write access to data',
      },
    },
  },
  description: 'OAuth2 authorization',
});
Supported OAuth2 Flows:
  • implicit - Implicit grant flow
  • password - Resource owner password flow
  • clientCredentials - Client credentials flow
  • authorizationCode - Authorization code flow (recommended)
Source: packages/adk/src/auth/auth-schemes.ts:116

OpenID Connect

OpenID Connect authentication:
import { OpenIdConnectScheme } from '@iqai/adk';

const oidcScheme = new OpenIdConnectScheme({
  openIdConnectUrl: 'https://accounts.google.com/.well-known/openid-configuration',
  description: 'Google OpenID Connect',
});
Source: packages/adk/src/auth/auth-schemes.ts:143

Auth Configuration

Define how tools should authenticate:
import { AuthConfig, ApiKeyScheme } from '@iqai/adk';

const authConfig = new AuthConfig({
  authScheme: new ApiKeyScheme({
    in: 'header',
    name: 'Authorization',
  }),
  context: {
    // Additional context for auth
    environment: 'production',
    apiVersion: 'v2',
  },
});
Source: packages/adk/src/auth/auth-config.ts:6

Auth Credentials

API Key Credential

import { ApiKeyCredential } from '@iqai/adk';

const credential = new ApiKeyCredential('sk_live_abc123xyz');

const token = credential.getToken(); // 'sk_live_abc123xyz'

const headers = credential.getHeaders(authConfig);
console.log(headers); // { 'X-API-Key': 'sk_live_abc123xyz' }
Source: packages/adk/src/auth/auth-credential.ts:59

Basic Auth Credential

import { BasicAuthCredential } from '@iqai/adk';

const credential = new BasicAuthCredential('username', 'password');

const token = credential.getToken(); // Base64 encoded

const headers = credential.getHeaders(authConfig);
console.log(headers); // { 'Authorization': 'Basic dXNlcm5hbWU6cGFzc3dvcmQ=' }
Source: packages/adk/src/auth/auth-credential.ts:97

Bearer Token Credential

import { BearerTokenCredential } from '@iqai/adk';

const credential = new BearerTokenCredential('eyJhbGc...');

const headers = credential.getHeaders(authConfig);
console.log(headers); // { 'Authorization': 'Bearer eyJhbGc...' }
Source: packages/adk/src/auth/auth-credential.ts:137

OAuth2 Credential with Refresh

import { OAuth2Credential } from '@iqai/adk';

const credential = new OAuth2Credential({
  accessToken: 'ya29.abc...',
  refreshToken: 'def456...',
  expiresIn: 3600, // 1 hour
  refreshFunction: async (refreshToken) => {
    const response = await fetch('https://api.example.com/oauth/refresh', {
      method: 'POST',
      body: JSON.stringify({ refresh_token: refreshToken }),
    });
    
    const data = await response.json();
    return {
      accessToken: data.access_token,
      refreshToken: data.refresh_token,
      expiresIn: data.expires_in,
    };
  },
});

// Check if expired
if (credential.isExpired()) {
  await credential.refresh();
}

const headers = credential.getHeaders(authConfig);
Source: packages/adk/src/auth/auth-credential.ts:171

Auth Handler

The AuthHandler orchestrates authentication:
import { AuthHandler, AuthConfig, BearerTokenCredential } from '@iqai/adk';

const handler = new AuthHandler({
  authConfig: new AuthConfig({
    authScheme: new HttpScheme({ scheme: 'bearer' }),
  }),
  credential: new BearerTokenCredential('token123'),
});

// Get token
const token = handler.getToken();

// Get headers for HTTP requests
const headers = handler.getHeaders();

// Refresh if needed
await handler.refreshToken();
Source: packages/adk/src/auth/auth-handler.ts:7

Tool Authentication

Authenticated Tool

import { BaseTool, ToolContext, AuthConfig, ApiKeyScheme } from '@iqai/adk';
import { z } from 'zod';

class WeatherTool extends BaseTool {
  name = 'get_weather';
  description = 'Get weather for a location';
  
  inputSchema = z.object({
    location: z.string(),
  });
  
  authConfig = new AuthConfig({
    authScheme: new ApiKeyScheme({
      in: 'header',
      name: 'X-Weather-API-Key',
    }),
  });
  
  async execute(context: ToolContext, args: { location: string }) {
    // Auth automatically handled by context
    const authHandler = context.getAuthHandler(this.authConfig);
    
    if (!authHandler) {
      throw new Error('Weather API credentials not provided');
    }
    
    const headers = authHandler.getHeaders();
    
    const response = await fetch(
      `https://api.weather.com/v1/weather?location=${args.location}`,
      { headers }
    );
    
    return response.json();
  }
}

Providing Credentials

import { AgentBuilder, ApiKeyCredential } from '@iqai/adk';

const weatherTool = new WeatherTool();

const agent = new AgentBuilder()
  .withModel('gpt-4')
  .withTools([weatherTool])
  .withAuthCredentials([
    {
      tool: weatherTool,
      credential: new ApiKeyCredential(process.env.WEATHER_API_KEY!),
    },
  ])
  .buildLlm();

Auth Preprocessor

The auth system includes a preprocessor that handles authentication automatically:
import { AuthPreprocessor } from '@iqai/adk';

// The preprocessor:
// 1. Identifies tools requiring auth
// 2. Checks for available credentials
// 3. Injects auth into tool context
// 4. Handles token refresh if needed
Source: packages/adk/src/auth/auth-preprocessor.ts

Common Patterns

Environment-Based Credentials

import { ApiKeyCredential, BearerTokenCredential } from '@iqai/adk';

const credentials = new Map<string, AuthCredential>([
  ['stripe', new ApiKeyCredential(process.env.STRIPE_API_KEY!)],
  ['github', new BearerTokenCredential(process.env.GITHUB_TOKEN!)],
  ['database', new BasicAuthCredential(
    process.env.DB_USER!,
    process.env.DB_PASSWORD!,
  )],
]);

const agent = new AgentBuilder()
  .withTools([stripeTool, githubTool, dbTool])
  .withAuthCredentials([
    { tool: stripeTool, credential: credentials.get('stripe')! },
    { tool: githubTool, credential: credentials.get('github')! },
    { tool: dbTool, credential: credentials.get('database')! },
  ])
  .buildLlm();

Per-User Credentials

interface UserCredentials {
  userId: string;
  credentials: Map<string, AuthCredential>;
}

const userCredentials = new Map<string, UserCredentials>();

function createAgentForUser(userId: string) {
  const userCreds = userCredentials.get(userId);
  
  if (!userCreds) {
    throw new Error('User credentials not found');
  }
  
  return new AgentBuilder()
    .withModel('gpt-4')
    .withTools([emailTool, calendarTool])
    .withAuthCredentials([
      { tool: emailTool, credential: userCreds.credentials.get('email')! },
      { tool: calendarTool, credential: userCreds.credentials.get('calendar')! },
    ])
    .buildLlm();
}

Dynamic Token Refresh

class TokenManager {
  private tokens = new Map<string, OAuth2Credential>();
  
  async getCredential(service: string): Promise<OAuth2Credential> {
    let credential = this.tokens.get(service);
    
    if (!credential || credential.isExpired()) {
      credential = await this.refreshToken(service);
      this.tokens.set(service, credential);
    }
    
    return credential;
  }
  
  private async refreshToken(service: string): Promise<OAuth2Credential> {
    // Fetch new token from your auth service
    const response = await fetch(`/api/auth/${service}/refresh`);
    const data = await response.json();
    
    return new OAuth2Credential({
      accessToken: data.access_token,
      refreshToken: data.refresh_token,
      expiresIn: data.expires_in,
    });
  }
}

const tokenManager = new TokenManager();

// Use with agent
const credential = await tokenManager.getCredential('google');

const agent = new AgentBuilder()
  .withTools([gmailTool])
  .withAuthCredentials([{ tool: gmailTool, credential }])
  .buildLlm();

Conditional Authentication

class ConditionalAuthTool extends BaseTool {
  async execute(context: ToolContext, args: any) {
    // Check if auth is required for this operation
    if (args.operation === 'read') {
      // Public operation - no auth needed
      return this.performPublicOperation(args);
    }
    
    // Private operation - auth required
    const authHandler = context.getAuthHandler(this.authConfig);
    
    if (!authHandler) {
      throw new Error('Authentication required for this operation');
    }
    
    return this.performPrivateOperation(args, authHandler);
  }
}

Security Best Practices

Security Guidelines:
  1. ⛔ Never hardcode credentials in source code
  2. ✅ Store credentials in environment variables or secure vaults
  3. ✅ Use OAuth2 with refresh tokens for long-lived access
  4. ✅ Implement token rotation and expiry
  5. ✅ Validate credentials before use
  6. ✅ Log authentication failures for monitoring
  7. ⛔ Never log or expose credentials in error messages

Credential Validation

function validateCredential(credential: AuthCredential): boolean {
  const token = credential.getToken();
  
  if (!token || token.length === 0) {
    return false;
  }
  
  // Check expiry for OAuth2
  if (credential instanceof OAuth2Credential) {
    return !credential.isExpired();
  }
  
  return true;
}

Secure Credential Storage

import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';

class SecureCredentialStore {
  private algorithm = 'aes-256-gcm';
  private key: Buffer;
  
  constructor(encryptionKey: string) {
    this.key = Buffer.from(encryptionKey, 'hex');
  }
  
  encrypt(credential: string): string {
    const iv = randomBytes(16);
    const cipher = createCipheriv(this.algorithm, this.key, iv);
    
    let encrypted = cipher.update(credential, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    
    const authTag = cipher.getAuthTag();
    
    return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
  }
  
  decrypt(encryptedCredential: string): string {
    const [ivHex, authTagHex, encrypted] = encryptedCredential.split(':');
    
    const iv = Buffer.from(ivHex, 'hex');
    const authTag = Buffer.from(authTagHex, 'hex');
    
    const decipher = createDecipheriv(this.algorithm, this.key, iv);
    decipher.setAuthTag(authTag);
    
    let decrypted = decipher.update(encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    
    return decrypted;
  }
}

Testing Authentication

import { describe, it, expect } from 'vitest';
import { AuthHandler, ApiKeyCredential, AuthConfig, ApiKeyScheme } from '@iqai/adk';

describe('Authentication', () => {
  it('should generate correct API key headers', () => {
    const handler = new AuthHandler({
      authConfig: new AuthConfig({
        authScheme: new ApiKeyScheme({
          in: 'header',
          name: 'X-API-Key',
        }),
      }),
      credential: new ApiKeyCredential('test123'),
    });
    
    const headers = handler.getHeaders();
    
    expect(headers).toEqual({ 'X-API-Key': 'test123' });
  });
  
  it('should refresh expired OAuth2 tokens', async () => {
    const credential = new OAuth2Credential({
      accessToken: 'old_token',
      refreshToken: 'refresh_token',
      expiresIn: -1, // Already expired
      refreshFunction: async () => ({
        accessToken: 'new_token',
        refreshToken: 'new_refresh_token',
        expiresIn: 3600,
      }),
    });
    
    expect(credential.isExpired()).toBe(true);
    
    await credential.refresh();
    
    expect(credential.getToken()).toBe('new_token');
    expect(credential.isExpired()).toBe(false);
  });
});

Next Steps

Telemetry

Monitor authentication and agent performance

Tools

Build tools that require authentication

Build docs developers (and LLMs) love