DEV Community

Cover image for AgentFlow: Autonomous AI Agents with Secure OAuth Integration via Auth0 Token Vault
Abhi nandan
Abhi nandan

Posted on

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

Auth0 for AI Agents Challenge Submission

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

Top comments (0)