Most endpoints require authentication using JWT tokens. Include the token in the Authorization header:
Authorization: Bearer <your-jwt-token>The authentication middleware (authenticateToken) will:
- Extract user information from the JWT token
- Set
req.userwith user data includingid,email, andauth_type - Store token and user data in session for backend operations
- Continue as unauthenticated if no valid token is provided
Token Storage & Access
The JWT token is not stored in the database. It lives in three places depending on context:
| Context | Where the token lives | How to access it |
|---|---|---|
| Widgets (iframe) | Never enters the iframe | Use the global agnt SDK (auth is proxied) |
| Frontend (Vue app, browser) | localStorage |
localStorage.getItem('token') (legacy callsites) |
| Backend services (Express) | req.headers.authorization or req.session.userToken |
Passed as authToken parameter between services |
| Orchestrator tools (spawned Node.js) | process.env.AGNT_AUTH_TOKEN |
Automatically injected by the orchestrator |
Widgets (browser iframe — use the agnt SDK)
The widget runtime injects a global agnt object into every widget iframe at mount time. The token never enters the iframe — every call is proxied through the parent via postMessage and executed by the parent's authenticated axios instance. Widget code should NOT read localStorage, NOT set Authorization headers, NOT write a getToken() helper. Bypassing the SDK will 401.
// Plugin / native / registry tool execution (most common widget use case)
const joke = await agnt.tool('chucknorris-get-joke', { category: 'dev' });
// Any /api/* endpoint
const agents = await agnt.fetch('/api/agents');
const created = await agnt.fetch('/api/agents', {
method: 'POST',
body: { name: 'My Agent' }, // object or JSON.stringify(...) both work
});
// User context, available synchronously
console.log(agnt.user); // { id, email, name } | nullagnt.tool returns the tool's result directly (not the { success, result } envelope) and throws on tool failure. agnt.fetch returns the parsed response body and throws on non-2xx. agnt.fetch is allowlisted to /api/* paths and to standard HTTP methods (GET/POST/PUT/PATCH/DELETE).
Frontend Vue app (browser, non-widget context)
The Vue app stores the JWT in localStorage under the key token and a global axios interceptor in frontend/src/main.js attaches Authorization: Bearer ... to every outbound request automatically. New code should use the existing axios setup; reach for localStorage.getItem('token') only when bypassing axios.
// Most code — interceptor handles auth automatically
import axios from 'axios';
const { data } = await axios.get('/api/agents');
// Bypass case (raw fetch outside axios):
const token = localStorage.getItem('token');
const res = await fetch('/api/agents', { headers: { Authorization: `Bearer ${token}` } });Backend Services (Express context)
Inside Express route handlers and services, the token arrives via the request header and is cached in the session by the authenticateToken middleware:
// In a route handler — token from the request
const authToken = req.headers.authorization; // "Bearer <token>"
// Or from session (set automatically by middleware after first auth)
const sessionData = getUserTokenFromSession(req);
// sessionData = { token: "<jwt>", user: { id, email, auth_type } }
// Services receive it as a parameter — just pass it along
const result = await someService.doWork(args, authToken);Orchestrator Tools (spawned Node.js context)
When the orchestrator's execute_javascript_code tool runs code, it spawns an isolated Node.js process. There is no req, no session, and no localStorage. The orchestrator automatically injects the user's token as an environment variable:
const API = 'http://localhost:3333/api';
const TOKEN = process.env.AGNT_AUTH_TOKEN; // automatically provided
const res = await fetch(API + '/agents/', {
headers: {
Authorization: 'Bearer ' + TOKEN,
'Content-Type': 'application/json',
},
});
const data = await res.json();
console.log(JSON.stringify(data, null, 2));Important: Each context has its own way to get the token — don't mix them.
localStorageonly exists in the browser.req.sessiononly exists in Express handlers.process.env.AGNT_AUTH_TOKENonly exists in orchestrator-spawned processes.
List Connected Providers
GET /api/auth/connected
- Authentication: Optional — env-sourced providers are install-global and return even for unauthenticated callers; per-user rows are included when a bearer token resolves a userId
- Description: List OAuth/API-key providers currently connected. Shape mirrors the remote
/auth/connectedresponse so the frontend can merge both sources without normalization. - Response:
[
{ "providerId": "openai", "connected": true }
]Notify Provider Changed
POST /api/auth/providers/notify-changed
- Authentication: None
- Description: The UI posts provider CRUD to the cloud API directly, so the local backend never sees the write. This endpoint lets a client tell the local backend "a provider changed" so every connected tab refreshes via Socket.IO.
- Body:
{
"event": "created | updated | deleted",
"providerId": "openai"
}- Response:
{ "success": true }—400ifeventis not one of the three valid values