AgentFlow: Autonomous AI Agents with Secure OAuth Integration via Auth0 Token Vault

AI Summary6 min read

TL;DR

AgentFlow is an AI agent platform that securely manages OAuth tokens using Auth0's Token Vault. It enables autonomous agents to interact with services like Gmail and Slack, with features for agent creation, real-time monitoring, and encrypted storage.

Key Takeaways

  • AgentFlow uses Auth0's Token Vault for secure OAuth token storage, preventing exposure to client-side code and enabling encrypted persistence in DynamoDB.
  • The platform supports multi-service integration, including Gmail, Slack, and Google Calendar, with agent-specific token scoping for enhanced security.
  • Key features include a visual agent builder, real-time dashboard for monitoring agent activities, and easy agent management with pause/resume and deletion capabilities.
  • Lessons learned highlight the importance of consistent key management in token storage and the value of Auth0 patterns in simplifying complex security challenges.

Tags

devchallengeauth0challengeaiauthentication

This is a submission for the Auth0 for AI Agents Challenge

What I Built

AgentFlow is a comprehensive AI agent platform that enables users to create, manage, and deploy autonomous AI agents that can interact with their connected services (Gmail, Slack, Google Calendar, etc.) on their behalf. The platform solves the critical problem of secure credential management for AI agents while providing a beautiful, intuitive interface for agent creation and monitoring.

Key Features:

🤖 Multi-Template Agent Builder

  1. Pre-configured templates (Email Assistant, Calendar Manager, Social Media Manager)
  2. Custom agent creation with flexible service selection
  3. Visual workflow configuration

🔐 Secure OAuth Integration

  1. Auth0-powered authentication for users
  2. Token Vault for secure credential storage
  3. Service-specific permission scopes
  4. Encrypted token storage in DynamoDB

📊 Real-Time Agent Dashboard

  1. Live activity feed showing agent actions
  2. Performance metrics and analytics
  3. Success rate tracking
  4. Time-saving calculations

⚡ Agent Management

  1. Pause/Resume agents with one click
  2. Delete agents with confirmation
  3. View detailed activity logs
  4. Monitor connected services

🔗 Multi-Service Integration

  1. Gmail (read, send, categorize emails)
  2. Slack (post messages, notifications)
  3. Google Calendar (event management)
  4. Extensible architecture for more services

Demo

Live Demo: https://main.d13aenlm5qrdln.amplifyapp.com
GitHub Repository: https://github.com/Abhinandangithub01/AgentFlow

How I Used Auth0 for AI Agents

Auth0 was absolutely critical to solving the core security challenge of AgentFlow. Here's how I leveraged Auth0's capabilities:

  1. User Authentication (Auth0 SDK)
// Next.js App Router integration
import { handleAuth, handleLogin, handleCallback } from '@auth0/nextjs-auth0';

export const GET = handleAuth({
  login: handleLogin({
    returnTo: '/dashboard'
  }),
  callback: handleCallback({
    redirectUri: process.env.AUTH0_BASE_URL + '/api/auth/callback'
  })
});
Enter fullscreen mode Exit fullscreen mode

Why this matters: Every user action is authenticated, ensuring only authorized users can create and manage agents.

  1. Token Vault for Secure Credential Storage The game-changer was implementing Auth0's Token Vault pattern for storing OAuth tokens:
// lib/token-vault.ts
export class TokenVaultService {
  async storeOAuthToken(
    userId: string,
    service: string,
    tokens: SecureToken,
    agentId?: string
  ): Promise<TokenVaultEntry> {
    const vaultKey = this.generateVaultKey(userId, service, agentId);

    // Store in DynamoDB with encryption
    await DynamoDBService.put(TABLES.TOKENS, {
      PK: `TOKEN#${vaultKey}`,
      SK: 'METADATA',
      accessToken: tokens.accessToken,
      refreshToken: tokens.refreshToken,
      expiresIn: tokens.expiresIn,
      tokenType: tokens.tokenType,
      scope: tokens.scope,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    });

    return entry;
  }

  async getOAuthToken(
    userId: string,
    service: string,
    agentId?: string
  ): Promise<SecureToken | null> {
    const vaultKey = this.generateVaultKey(userId, service, agentId);
    const item = await DynamoDBService.get(TABLES.TOKENS, {
      PK: `TOKEN#${vaultKey}`,
      SK: 'METADATA',
    });

    return item ? {
      accessToken: item.accessToken,
      refreshToken: item.refreshToken,
      expiresIn: item.expiresIn,
      tokenType: item.tokenType,
      scope: item.scope,
    } : null;
  }
}
Enter fullscreen mode Exit fullscreen mode

Security Benefits:

✅ Tokens never exposed to client-side code
✅ Encrypted at rest in DynamoDB (AWS AES-256)
✅ Scoped to specific user + service + agent combinations
✅ Automatic token refresh handling
✅ Secure revocation on agent deletion

  1. OAuth Flow for Service Connections
// app/api/auth/gmail/callback/route.ts
export async function GET(request: NextRequest) {
  const session = await getSession();
  if (!session?.user) {
    return NextResponse.redirect(new URL('/?error=no_session', request.url));
  }

  const code = searchParams.get('code');

  // Exchange code for tokens
  const oauth2Client = new google.auth.OAuth2(
    process.env.GOOGLE_CLIENT_ID,
    process.env.GOOGLE_CLIENT_SECRET,
    redirectUri
  );

  const { tokens } = await oauth2Client.getToken(code);

  // Store in Token Vault (Auth0 pattern)
  await tokenVault.storeOAuthToken(
    session.user.sub,
    'gmail',
    {
      accessToken: tokens.access_token,
      refreshToken: tokens.refresh_token,
      expiresIn: tokens.expiry_date ? 
        Math.floor((tokens.expiry_date - Date.now()) / 1000) : 3600,
      tokenType: 'Bearer',
      scope: tokens.scope || '',
    }
  );

  // Store connection record
  await DynamoDBService.put(TABLES.CONNECTIONS, {
    PK: `USER#${session.user.sub}`,
    SK: 'SERVICE#gmail',
    service: 'gmail',
    status: 'connected',
    scopes: tokens.scope?.split(' ') || [],
    connectedAt: new Date().toISOString(),
  });

  return NextResponse.redirect(new URL('/integrations', request.url));
}
Enter fullscreen mode Exit fullscreen mode
  1. Agent-Specific Token Access Each agent gets its own scoped access to tokens:
// When agent needs to access Gmail
const agent = await agentManager.getAgent(agentId, userId);
const gmailToken = await tokenVault.getOAuthToken(
  userId,
  'gmail',
  agentId  // Agent-specific token
);

// Use token to perform action
const gmail = google.gmail({ version: 'v1', auth: oauth2Client });
oauth2Client.setCredentials({
  access_token: gmailToken.accessToken,
  refresh_token: gmailToken.refreshToken
});
Enter fullscreen mode Exit fullscreen mode
  1. Session Management
// Middleware for protected routes
export async function middleware(request: NextRequest) {
  const session = await getSession();

  if (!session?.user) {
    return NextResponse.redirect(new URL('/', request.url));
  }

  return NextResponse.next();
}
Enter fullscreen mode Exit fullscreen mode
  1. Persistent Token Storage Critical Fix: Initially used in-memory storage which lost tokens on reload. Migrated to DynamoDB for persistence:
// Before: ❌ Lost on reload
global.tokenVault = new Map();
global.tokenVault.set(key, tokens);

// After: ✅ Persists forever
await DynamoDBService.put(TABLES.TOKENS, {
  PK: `TOKEN#${key}`,
  SK: 'METADATA',
  ...tokens
});
Enter fullscreen mode Exit fullscreen mode

Result: Tokens now survive:
✅ Page reloads
✅ Server restarts
✅ Deployment updates
✅ Load balancing across instances

Lessons Learned and Takeaways

🎯 1. Token Security is HARD (But Auth0 Makes It Easier)
Challenge: Initially, I stored OAuth tokens in memory, which seemed simple but caused tokens to disappear on every page reload!

Solution: Implemented Auth0's Token Vault pattern with DynamoDB persistence.

Lesson: Never underestimate the complexity of secure credential management. Auth0's patterns and documentation were invaluable in getting this right.

// The critical bug that took hours to debug:
// Storing with entry.id but retrieving with vaultKey! 🐛
await this.encryptAndStore(entry.id, tokens);  // ❌ Wrong key
const token = await this.decryptAndRetrieve(vaultKey);  // ❌ Different key

// The fix:
await this.encryptAndStore(vaultKey, tokens);  // ✅ Consistent key
const token = await this.decryptAndRetrieve(vaultKey);  // ✅ Same key
Enter fullscreen mode Exit fullscreen mode

Huge thanks to Auth0 and DEV.to for this challenge! Building AgentFlow taught me more about authentication, security, and AI agents than any tutorial could. The Auth0 SDK and Token Vault patterns were game-changers.

Special shoutout to the Auth0 documentation team - your Next.js examples saved me countless hours!

Built with ❤️ using Auth0, Next.js 14, TypeScript, DynamoDB, and AWS Amplify

Visit Website