Skip to main content

Auth0 Setup Guide

This guide walks you through setting up Auth0 Machine-to-Machine (M2M) authentication for your Bindu agents.
Auth0 provides industry-standard OAuth 2.0 / JWT authentication with fine-grained permission control.

Prerequisites

  • Auth0 account (free tier available at auth0.com)
  • Bindu agent installed and configured
  • Basic understanding of OAuth 2.0 and JWT tokens

Auth0 Setup

Step 1: Create Auth0 Account

1

Sign Up

  1. Go to auth0.com and sign up
  2. Create a new tenant (e.g., your-company)
  3. Your Auth0 domain will be: your-company.auth0.com

Step 2: Create an API

1

Navigate to APIs

In your Auth0 Dashboard, go to Applications → APIs
2

Create API

Click Create API and fill in:
  • Name: Bindu Agent API
  • Identifier: https://api.bindu.ai (this is your audience)
  • Signing Algorithm: RS256
Click Create
The API identifier (audience) can be any URL-like string. It doesn’t need to be a real URL.

Step 3: Define Permissions (Optional)

If you want permission-based access control:
1

Go to Permissions Tab

In your API settings, navigate to the Permissions tab
2

Add Permissions

Add the following permissions:
  • agent:read - Read tasks, contexts, agent info
  • agent:write - Send messages, create tasks
  • agent:admin - Cancel tasks, clear contexts
Click Add for each permission

Step 4: Create M2M Application

1

Navigate to Applications

Go to Applications → Applications
2

Create Application

Click Create Application and fill in:
  • Name: Bindu M2M Client (or your service name)
  • Type: Select Machine to Machine Applications
Click Create
3

Authorize API

  1. Select your API (Bindu Agent API)
  2. Select permissions (if using permission-based access)
  3. Click Authorize

Step 5: Get Credentials

1

Copy Credentials

In your M2M application settings, copy:
  • Domain: your-company.auth0.com
  • Client ID: abc123xyz789...
  • Client Secret: supersecret... (keep this secure!)
  • Audience: https://api.bindu.ai
Keep your Client Secret secure! Never commit it to version control or expose it in client-side code.

Agent Configuration

Basic Configuration

Update your agent config to enable authentication:
{
  "author": "[email protected]",
  "name": "Secure Agent",
  "auth": {
    "enabled": true,
    "domain": "your-company.auth0.com",
    "audience": "https://api.bindu.ai"
  },
  "deployment": {
    "url": "http://localhost:3773"
  },
  "storage": {
    "type": "memory"
  },
  "scheduler": {
    "type": "memory"
  }
}

With Permission-Based Access Control

For fine-grained permissions:
{
  "auth": {
    "enabled": true,
    "domain": "your-company.auth0.com",
    "audience": "https://api.bindu.ai",
    "require_permissions": true,
    "permissions": {
      "message/send": ["agent:write"],
      "tasks/get": ["agent:read"],
      "tasks/cancel": ["agent:admin"],
      "tasks/list": ["agent:read"],
      "contexts/list": ["agent:read"],
      "tasks/feedback": ["agent:write"]
    }
  }
}

Configuration Fields

FieldTypeRequiredDescription
enabledbooleanYesEnable/disable authentication
domainstringYes*Auth0 tenant domain
audiencestringYes*API identifier from Auth0
algorithmsarrayNoJWT algorithms (default: ["RS256"])
issuerstringNoToken issuer (auto-generated from domain)
jwks_uristringNoJWKS endpoint (auto-generated from domain)
require_permissionsbooleanNoEnable permission checking (default: false)
permissionsobjectNoMap JSON-RPC methods to required permissions
*Required when enabled: true

Client Implementation

Python Client

Create a client that automatically handles token management:
import os
import time
import requests
from typing import Optional

class BinduM2MClient:
    def __init__(self):
        self.auth0_domain = os.getenv("AUTH0_DOMAIN")
        self.client_id = os.getenv("AUTH0_CLIENT_ID")
        self.client_secret = os.getenv("AUTH0_CLIENT_SECRET")
        self.audience = os.getenv("AUTH0_AUDIENCE")
        self.agent_url = os.getenv("BINDU_AGENT_URL", "http://localhost:3773")

        self._token: Optional[str] = None
        self._token_expires_at: float = 0

    def _get_token(self) -> str:
        """Get valid access token, refreshing if needed."""
        current_time = time.time()

        # Return cached token if still valid (5 min buffer)
        if self._token and self._token_expires_at > (current_time + 300):
            return self._token

        # Request new token
        token_url = f"https://{self.auth0_domain}/oauth/token"
        payload = {
            "client_id": self.client_id,
            "client_secret": self.client_secret,
            "audience": self.audience,
            "grant_type": "client_credentials"
        }

        response = requests.post(token_url, json=payload)
        response.raise_for_status()

        token_data = response.json()
        self._token = token_data["access_token"]
        self._token_expires_at = current_time + token_data["expires_in"]

        return self._token

    def send_message(self, message: str, context_id: str = None):
        """Send message to Bindu agent."""
        token = self._get_token()

        payload = {
            "jsonrpc": "2.0",
            "method": "message/send",
            "params": {
                "message": {
                    "context_id": context_id,
                    "parts": [{"text": message}],
                    "role": "user"
                }
            },
            "id": f"req-{int(time.time())}"
        }

        headers = {
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json"
        }

        response = requests.post(self.agent_url, json=payload, headers=headers)
        response.raise_for_status()

        return response.json()

# Usage
if __name__ == "__main__":
    client = BinduM2MClient()
    result = client.send_message("Hello, secure agent!")
    print(result)

Environment Variables

Create a .env file:
# .env file
AUTH0_DOMAIN=your-company.auth0.com
AUTH0_CLIENT_ID=abc123xyz789
AUTH0_CLIENT_SECRET=supersecretvalue
AUTH0_AUDIENCE=https://api.bindu.ai
BINDU_AGENT_URL=http://localhost:3773
Add .env to your .gitignore to prevent committing secrets!

Node.js/TypeScript Client

import axios from 'axios';

class BinduM2MClient {
  private auth0Domain: string;
  private clientId: string;
  private clientSecret: string;
  private audience: string;
  private agentUrl: string;
  private token: string | null = null;
  private tokenExpiresAt: number = 0;

  constructor() {
    this.auth0Domain = process.env.AUTH0_DOMAIN!;
    this.clientId = process.env.AUTH0_CLIENT_ID!;
    this.clientSecret = process.env.AUTH0_CLIENT_SECRET!;
    this.audience = process.env.AUTH0_AUDIENCE!;
    this.agentUrl = process.env.BINDU_AGENT_URL || 'http://localhost:3773';
  }

  private async getToken(): Promise<string> {
    const currentTime = Date.now() / 1000;

    // Return cached token if still valid (5 min buffer)
    if (this.token && this.tokenExpiresAt > (currentTime + 300)) {
      return this.token;
    }

    // Request new token
    const tokenUrl = `https://${this.auth0Domain}/oauth/token`;
    const response = await axios.post(tokenUrl, {
      client_id: this.clientId,
      client_secret: this.clientSecret,
      audience: this.audience,
      grant_type: 'client_credentials'
    });

    this.token = response.data.access_token;
    this.tokenExpiresAt = currentTime + response.data.expires_in;

    return this.token;
  }

  async sendMessage(message: string, contextId?: string) {
    const token = await this.getToken();

    const payload = {
      jsonrpc: '2.0',
      method: 'message/send',
      params: {
        message: {
          context_id: contextId,
          parts: [{ text: message }],
          role: 'user'
        }
      },
      id: `req-${Date.now()}`
    };

    const response = await axios.post(this.agentUrl, payload, {
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });

    return response.data;
  }
}

// Usage
const client = new BinduM2MClient();
client.sendMessage('Hello, secure agent!').then(console.log);

Testing

1. Start Agent with Auth Disabled

First, test without authentication:
python examples/agno_example.py examples/simple_agent_config.json
Test public endpoint:
curl http://localhost:3773/.well-known/agent.json

2. Enable Auth in Config

Update your config with Auth0 credentials and restart the agent.

3. Test Without Token (Should Fail)

curl -X POST http://localhost:3773/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tasks/list",
    "params": {},
    "id": 1
  }'
Expected Response:
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32001,
    "message": "Authentication required"
  },
  "id": 1
}

4. Get Token from Auth0

curl --request POST \
  --url https://your-company.auth0.com/oauth/token \
  --header 'content-type: application/json' \
  --data '{
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "audience": "https://api.bindu.ai",
    "grant_type": "client_credentials"
  }'
Save the access_token from the response.

5. Test With Valid Token (Should Succeed)

TOKEN="your_access_token_here"

curl -X POST http://localhost:3773/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tasks/list",
    "params": {},
    "id": 1
  }'
Expected Response:
{
  "jsonrpc": "2.0",
  "result": {
    "tasks": []
  },
  "id": 1
}

Troubleshooting

Cause: No Authorization header provided or header format is incorrect.Solution:
  • Ensure you include the header: Authorization: Bearer <token>
  • Check that the token is not empty
  • Verify the header format is correct (Bearer prefix with space)
Cause: Token signature verification failed.Solution:
  • Verify domain in agent config matches Auth0 tenant domain
  • Verify audience matches the API identifier in Auth0
  • Check that you’re using the correct token from Auth0
  • Ensure the token hasn’t been modified
Cause: JWT exp claim has passed.Solution:
  • Implement automatic token refresh in your client
  • Request a new token from Auth0
  • Auth0 M2M tokens typically expire after 24 hours
  • Use the token caching pattern shown in client examples
Cause: Token audience doesn’t match agent configuration.Solution:
  • Verify audience in agent config matches Auth0 API identifier
  • Check that you’re requesting the token with the correct audience
  • Ensure the API is properly configured in Auth0
Cause: Token lacks required permissions for the operation.Solution:
  • Grant required permissions to M2M app in Auth0 dashboard
  • Go to Applications → APIs → Your API → Machine to Machine Applications
  • Select your M2M app and grant necessary permissions
  • Request a new token after granting permissions
Cause: Invalid Auth0 configuration.Solution:
  • Check agent logs for specific error messages
  • Verify all required fields are present: domain, audience
  • Ensure domain format is correct (e.g., tenant.auth0.com)
  • Test Auth0 connectivity: curl https://your-domain.auth0.com/.well-known/jwks.json

Security Best Practices

Follow these security practices to protect your agents and user data.
  1. ✅ Environment Variables - Store credentials in environment variables, never in code
  2. ✅ HTTPS in Production - Always use HTTPS for production deployments
  3. ✅ Rotate Secrets - Regularly rotate client secrets in Auth0
  4. ✅ Minimum Permissions - Grant only the permissions needed for each client
  5. ✅ Monitor Logs - Review Auth0 logs for suspicious activity
  6. ✅ Token Caching - Cache tokens to reduce Auth0 API calls and improve performance
  7. ✅ Separate Tenants - Use different Auth0 tenants for dev/staging/production
  8. ✅ Short-Lived Tokens - Use default token expiration (24 hours)
  9. ✅ Validate Audience - Always validate the audience claim in tokens
  10. ✅ Rate Limiting - Implement rate limiting on your agent endpoints

Development vs Production

Development (Localhost):
{
  "auth": {
    "enabled": true,
    "domain": "dev-tenant.auth0.com",
    "audience": "https://api.bindu.dev"
  },
  "deployment": {
    "url": "http://localhost:3773"
  }
}
Production:
{
  "auth": {
    "enabled": true,
    "domain": "prod-tenant.auth0.com",
    "audience": "https://api.bindu.ai"
  },
  "deployment": {
    "url": "https://agent.yourdomain.com"
  }
}

Next Steps


Additional Resources