TypeScriptADK-TS

Authentication

Secure API access and credential management for ADK-TS tools

Authenticate your ADK tools to access protected APIs and services. ADK-TS provides authentication classes for API keys, Bearer tokens, Basic auth, and OAuth 2.0 with automatic token refresh.

Supported Auth Methods

MethodUse CaseToken Refresh
API KeySimple API access with static keysNo
BearerJWT tokens, temporary access tokensNo
BasicUsername/password authenticationNo
OAuth 2.0User authorization flows, long-lived accessYes

All credentials should be stored in environment variables, never hardcoded in your source code.

Quick Start

Learn how to set up authentication for your tools using ADK-TS authentication classes.

API Key Authentication

Use API keys for simple authentication. The key is sent in a custom header with each request.

import {
  ApiKeyScheme,
  ApiKeyCredential,
  AuthConfig,
  AuthHandler,
} from "@iqai/adk";

// 1. Create authentication scheme
const apiKeyScheme = new ApiKeyScheme({
  in: "header",
  name: "X-API-Key",
});

// 2. Create credential from environment
const apiKeyCredential = new ApiKeyCredential(process.env.API_KEY!);

// 3. Create auth handler
const authHandler = new AuthHandler({
  authConfig: new AuthConfig({ authScheme: apiKeyScheme }),
  credential: apiKeyCredential,
});

// 4. Use in API requests
const response = await fetch("https://api.example.com/data", {
  headers: authHandler.getHeaders(),
});

Bearer Token Authentication

Use Bearer tokens for JWT-based authentication. The token is sent in the Authorization header.

import {
  HttpScheme,
  BearerTokenCredential,
  AuthConfig,
  AuthHandler,
} from "@iqai/adk";

const bearerScheme = new HttpScheme({
  scheme: "bearer",
  bearerFormat: "JWT",
});

const bearerCredential = new BearerTokenCredential(process.env.AUTH_TOKEN!);

const authHandler = new AuthHandler({
  authConfig: new AuthConfig({ authScheme: bearerScheme }),
  credential: bearerCredential,
});

OAuth 2.0 with Token Refresh

OAuth 2.0 credentials support automatic token refresh:

import {
  OAuth2Scheme,
  OAuth2Credential,
  AuthConfig,
  AuthHandler,
} from "@iqai/adk";

// Define OAuth2 scheme
const oauth2Scheme = new OAuth2Scheme({
  flows: {
    authorizationCode: {
      authorizationUrl: "https://accounts.google.com/o/oauth2/auth",
      tokenUrl: "https://oauth2.googleapis.com/token",
      scopes: {
        "https://www.googleapis.com/auth/drive.readonly": "View files",
      },
    },
  },
});

// Create credential with refresh function
const oauth2Credential = new OAuth2Credential({
  accessToken: process.env.ACCESS_TOKEN!,
  refreshToken: process.env.REFRESH_TOKEN!,
  expiresIn: 3600,
  refreshFunction: async (refreshToken) => {
    const response = await fetch("https://oauth2.googleapis.com/token", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams({
        grant_type: "refresh_token",
        refresh_token: refreshToken,
        client_id: process.env.CLIENT_ID!,
        client_secret: process.env.CLIENT_SECRET!,
      }),
    });

    const data = await response.json();
    return {
      accessToken: data.access_token,
      refreshToken: data.refresh_token || refreshToken,
      expiresIn: data.expires_in,
    };
  },
});

// Create auth handler
const authHandler = new AuthHandler({
  authConfig: new AuthConfig({ authScheme: oauth2Scheme }),
  credential: oauth2Credential,
});

// Token automatically refreshes when expired
if (oauth2Credential.isExpired()) {
  await oauth2Credential.refresh();
}

Using Authentication with Tools

Integrate authentication into your custom tools by creating an AuthHandler in the constructor and using it in your tool's runAsync method.

In Custom Tools

Create a BaseTool subclass with authentication by setting up the AuthHandler as a private property in the constructor:

import {
  BaseTool,
  AuthConfig,
  ApiKeyScheme,
  ApiKeyCredential,
  AuthHandler,
  ToolContext,
  FunctionDeclaration,
} from "@iqai/adk";
import { Type } from "@google/genai";

class WeatherTool extends BaseTool {
  private authHandler: AuthHandler;

  constructor() {
    super({
      name: "get_weather",
      description: "Get weather forecast",
    });

    // Set up authentication manually
    const apiKeyScheme = new ApiKeyScheme({
      in: "header",
      name: "X-API-Key",
    });

    const apiKeyCredential = new ApiKeyCredential(
      process.env.WEATHER_API_KEY || ""
    );

    this.authHandler = new AuthHandler({
      authConfig: new AuthConfig({ authScheme: apiKeyScheme }),
      credential: apiKeyCredential,
    });
  }

  async runAsync(
    args: Record<string, any>,
    context: ToolContext
  ): Promise<any> {
    const location = args.location as string;

    try {
      const response = await fetch(
        `https://api.weather.com/forecast?location=${location}`,
        { headers: this.authHandler.getHeaders() }
      );

      if (!response.ok) {
        throw new Error(`API returned status ${response.status}`);
      }

      return await response.json();
    } catch (error) {
      return `Unable to fetch weather for ${location}. Please try again later.`;
    }
  }

  getDeclaration(): FunctionDeclaration {
    return {
      name: this.name,
      description: this.description,
      parameters: {
        type: Type.OBJECT,
        properties: {
          location: {
            type: Type.STRING,
            description: "City name",
          },
        },
        required: ["location"],
      },
    };
  }
}

In Function Tools

For function-based tools created with createTool, define the AuthHandler outside the tool and reference it in the function closure:

import {
  createTool,
  AuthConfig,
  ApiKeyScheme,
  ApiKeyCredential,
  AuthHandler,
} from "@iqai/adk";
import { z } from "zod";

// Create auth handler outside the tool
const apiKeyScheme = new ApiKeyScheme({
  in: "header",
  name: "X-API-Key",
});

const apiKeyCredential = new ApiKeyCredential(process.env.WEATHER_API_KEY!);

const authHandler = new AuthHandler({
  authConfig: new AuthConfig({ authScheme: apiKeyScheme }),
  credential: apiKeyCredential,
});

// Use in tool
const weatherTool = createTool({
  name: "get_weather",
  description: "Get weather forecast",
  schema: z.object({
    location: z.string().describe("City name"),
  }),
  fn: async ({ location }) => {
    const response = await fetch(
      `https://api.weather.com/forecast?location=${location}`,
      { headers: authHandler.getHeaders() }
    );
    return response.json();
  },
});

Error Handling

Properly handle authentication errors including token refresh, 401/403 responses, and network failures:

import { AuthHandler } from "@iqai/adk";

async function callAuthenticatedApi(
  endpoint: string,
  authHandler: AuthHandler
) {
  try {
    // Refresh token if expired
    if (authHandler.credential?.canRefresh()) {
      await authHandler.refreshToken();
    }

    const response = await fetch(endpoint, {
      headers: authHandler.getHeaders(),
    });

    // Handle auth-specific errors
    if (response.status === 401) {
      return { success: false, error: "Authentication failed" };
    }

    if (response.status === 403) {
      return { success: false, error: "Insufficient permissions" };
    }

    if (!response.ok) {
      return { success: false, error: `API error: ${response.status}` };
    }

    const data = await response.json();
    return { success: true, data };
  } catch (error) {
    console.error("API call failed:", error);
    return { success: false, error: "Service unavailable" };
  }
}

Credential Management

Manage credentials securely using environment variables and implement retry logic for resilient API calls.

Environment Variables

Validate that all required environment variables are present at application startup:

import dotenv from "dotenv";
import { ApiKeyCredential } from "@iqai/adk";

dotenv.config();

// Validate required variables
const required = ["API_KEY", "CLIENT_ID", "CLIENT_SECRET"];
const missing = required.filter((key) => !process.env[key]);
if (missing.length > 0) {
  throw new Error(`Missing required env vars: ${missing.join(", ")}`);
}

// Create credentials
const apiKey = new ApiKeyCredential(process.env.API_KEY!);

Retry Logic

Implement retry with exponential backoff for transient failures, but skip retries for authentication errors:

import {
  ApiKeyScheme,
  AuthConfig,
  ApiKeyCredential,
  AuthHandler,
} from "@iqai/adk";

async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  initialDelay = 1000
): Promise<T> {
  let lastError: Error | undefined;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;

      // Don't retry auth failures
      if (
        error instanceof Error &&
        error.message.includes("Authentication failed")
      ) {
        throw error;
      }

      // Exponential backoff
      const delay = initialDelay * Math.pow(2, attempt);
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }

  throw lastError || new Error("Operation failed after retries");
}

// Usage
const endpoint = "https://api.example.com/data";

// Create auth handler outside the tool
const apiKeyScheme = new ApiKeyScheme({
  in: "header",
  name: "X-API-Key",
});

const apiKeyCredential = new ApiKeyCredential(process.env.WEATHER_API_KEY!);

const authHandler = new AuthHandler({
  authConfig: new AuthConfig({ authScheme: apiKeyScheme }),
  credential: apiKeyCredential,
});

const result = await withRetry(async () => {
  const response = await fetch(endpoint, { headers: authHandler.getHeaders() });
  if (!response.ok) throw new Error(`Failed: ${response.status}`);
  return response.json();
});

Security Best Practices

Follow these security guidelines to protect your credentials and ensure secure authentication:

  • Store in environment variables - Never hardcode credentials
  • Use HTTPS only - All authenticated requests must use HTTPS
  • Implement token refresh - Refresh tokens before they expire
  • Never log credentials - Don't log tokens or API keys
  • Validate early - Check credentials at application startup
  • Minimum permissions - Request only required scopes
  • Monitor failures - Track authentication errors
  • Rotate regularly - Update credentials periodically in production

How is this guide?