Authentication enables secure access control for agents, managing user identities, permissions, OAuth flows, and multi-tenant isolation to ensure agents operate within proper security boundaries.
Overview
The Authentication primitive provides comprehensive identity and access management for agent operations. Whether you’re building user-facing applications, multi-tenant platforms, or enterprise integrations, authentication ensures agents respect security boundaries and access controls.
Authentication is essential for:
User Identity : Link agent executions to specific users
Access Control : Enforce permissions and role-based access
OAuth Integration : Handle OAuth flows for third-party services
Multi-Tenancy : Isolate data and operations by organization
Session Management : Maintain secure user sessions
API Security : Authenticate API requests and webhooks
OAuth 2.0 Complete OAuth 2.0 flow support for external integrations
JWT Tokens Secure token-based authentication and authorization
RBAC Role-based access control for fine-grained permissions
SSO Support Single sign-on with SAML, OAuth, and OpenID Connect
How Authentication Works
When you implement authentication:
Identity Establishment : User authenticates via credentials, OAuth, or SSO
Token Generation : System issues secure JWT or session token
Context Injection : User identity and permissions attached to agent context
Authorization Check : Agent verifies permissions before operations
Scope Enforcement : Operations restricted to user’s authorized scope
Audit Logging : All authenticated actions logged for compliance
Security First : All authentication tokens are encrypted, have expiration times, and support automatic rotation.
Authentication Methods
API Key Authentication
{
type : "api_key" ,
apiKey : "ab_sk_..." ,
permissions : [ "read" , "write" ]
}
JWT Token Authentication
{
type : "jwt" ,
token : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ,
claims : {
userId : "user_123" ,
role : "admin"
}
}
OAuth 2.0
{
type : "oauth" ,
provider : "google" | "github" | "custom" ,
scope : [ "profile" , "email" ],
redirectUri : "https://app.com/callback"
}
SSO (SAML/OpenID)
{
type : "sso" ,
provider : "okta" | "auth0" | "custom" ,
assertionUrl : "https://sso.company.com/saml"
}
Code Examples
Basic API Key Authentication
import { Agentbase } from '@agentbase/sdk' ;
// Initialize with API key
const agentbase = new Agentbase ({
apiKey: process . env . AGENTBASE_API_KEY
});
// Run agent with authentication
const result = await agentbase . runAgent ({
message: "Get my user profile" ,
auth: {
userId: "user_123" , // Link to specific user
sessionId: "sess_abc456"
}
});
// Agent operates in context of authenticated user
console . log ( 'Result:' , result );
JWT Token Authentication
// Create authenticated session with JWT
const session = await agentbase . createSession ({
auth: {
type: "jwt" ,
token: userJwtToken ,
verify: true // Verify token signature
}
});
// Run agent with JWT authentication
const result = await agentbase . runAgent ({
message: "Access my protected data" ,
sessionId: session . id
});
// Agent has access to user claims from JWT
OAuth Integration
// Initialize OAuth flow
const oauthUrl = await agentbase . initiateOAuth ({
provider: "google" ,
scopes: [ "profile" , "email" , "drive" ],
redirectUri: "https://yourapp.com/oauth/callback" ,
state: "random_state_string"
});
// Redirect user to oauthUrl
// Handle OAuth callback
app . get ( '/oauth/callback' , async ( req , res ) => {
const { code , state } = req . query ;
// Exchange code for tokens
const tokens = await agentbase . completeOAuth ({
provider: "google" ,
code ,
state
});
// Save tokens for user
await saveUserTokens ( userId , tokens );
// Now agent can access Google services on behalf of user
const result = await agentbase . runAgent ({
message: "List my Google Drive files" ,
auth: {
userId: userId ,
oauth: {
provider: "google" ,
accessToken: tokens . accessToken
}
},
integrations: {
google_drive: { enabled: true }
}
});
res . json ( result );
});
Role-Based Access Control
// Define user with roles and permissions
const result = await agentbase . runAgent ({
message: "Delete customer record" ,
auth: {
userId: "user_123" ,
role: "admin" ,
permissions: [
"read:customers" ,
"write:customers" ,
"delete:customers"
]
},
system: `You have the following permissions: {{auth.permissions}}
Only perform operations if user has required permission.
For delete operations, require 'delete:customers' permission.`
});
// Agent checks permissions before executing
Multi-Tenant Isolation
// Isolate data by organization/tenant
const result = await agentbase . runAgent ({
message: "List all customers" ,
auth: {
userId: "user_123" ,
tenantId: "org_acme" , // Tenant/organization ID
role: "member"
},
dataConnectors: {
postgres: {
enabled: true ,
connectionString: process . env . DATABASE_URL ,
tenantIsolation: {
enabled: true ,
tenantColumn: "organization_id" ,
tenantValue: "{{auth.tenantId}}"
}
}
},
system: `You can only access data for organization: {{auth.tenantId}}
All queries must filter by organization_id = {{auth.tenantId}}`
});
// Agent automatically filters all queries by tenant
Session Management
// Create authenticated session
const session = await agentbase . createSession ({
auth: {
userId: "user_123" ,
role: "admin"
},
ttl: 3600 , // 1 hour
refreshable: true
});
// Use session for multiple agent calls
const result1 = await agentbase . runAgent ({
message: "First task" ,
sessionId: session . id
});
const result2 = await agentbase . runAgent ({
message: "Second task" ,
sessionId: session . id
});
// Refresh session before expiry
if ( session . expiresIn < 300 ) {
await agentbase . refreshSession ( session . id );
}
// End session when done
await agentbase . endSession ( session . id );
API Key Management
// Create scoped API key for user
const apiKey = await agentbase . createApiKey ({
userId: "user_123" ,
name: "Production API Key" ,
scopes: [ "agents:run" , "sessions:create" ],
rateLimit: {
requestsPerMinute: 60
},
expiresAt: "2025-12-31T23:59:59Z"
});
console . log ( 'API Key:' , apiKey . key );
console . log ( 'Expires:' , apiKey . expiresAt );
// List user's API keys
const keys = await agentbase . listApiKeys ({
userId: "user_123"
});
// Revoke API key
await agentbase . revokeApiKey ( apiKey . id );
Webhook Signature Verification
// Verify webhook signatures
app . post ( '/webhook/stripe' , async ( req , res ) => {
const signature = req . headers [ 'stripe-signature' ];
// Verify webhook authenticity
const verified = await agentbase . verifyWebhookSignature ({
provider: "stripe" ,
payload: req . body ,
signature: signature ,
secret: process . env . STRIPE_WEBHOOK_SECRET
});
if ( ! verified ) {
return res . status ( 401 ). json ({ error: "Invalid signature" });
}
// Process verified webhook
await agentbase . runAgent ({
message: "Process Stripe webhook" ,
context: {
event: req . body
},
auth: {
webhookProvider: "stripe" ,
verified: true
}
});
res . json ({ received: true });
});
Use Cases
1. Multi-Tenant SaaS Application
Isolate data by organization:
// Organization-scoped agent execution
app . post ( '/api/agent' , async ( req , res ) => {
// Extract user from JWT
const user = await verifyJWT ( req . headers . authorization );
const result = await agentbase . runAgent ({
message: req . body . message ,
auth: {
userId: user . id ,
tenantId: user . organizationId ,
role: user . role ,
permissions: user . permissions
},
dataConnectors: {
postgres: {
enabled: true ,
tenantIsolation: {
enabled: true ,
tenantColumn: "org_id" ,
tenantValue: user . organizationId
}
}
},
memory: {
namespace: `org_ ${ user . organizationId } _user_ ${ user . id } ` ,
enabled: true
},
system: `You are operating in the context of ${ user . organization } .
You can only access data belonging to this organization.
User role: ${ user . role }
Permissions: ${ user . permissions . join ( ', ' ) } `
});
res . json ( result );
});
2. OAuth-Powered Integrations
Access user’s third-party services:
// Gmail integration with OAuth
const result = await agentbase . runAgent ({
message: "Send email to customer about their order" ,
auth: {
userId: currentUser . id ,
oauth: {
provider: "google" ,
accessToken: currentUser . googleAccessToken ,
refreshToken: currentUser . googleRefreshToken ,
autoRefresh: true
}
},
integrations: {
gmail: { enabled: true }
},
context: {
customerEmail: order . customerEmail ,
orderDetails: order
}
});
// Agent uses user's Gmail to send email
3. Enterprise SSO Integration
Support enterprise single sign-on:
// SAML SSO authentication
app . post ( '/sso/acs' , async ( req , res ) => {
// Validate SAML assertion
const samlData = await agentbase . validateSAML ({
assertion: req . body . SAMLResponse ,
certificat: process . env . IDP_CERTIFICATE
});
// Create session for SSO user
const session = await agentbase . createSession ({
auth: {
type: "sso" ,
userId: samlData . userId ,
email: samlData . email ,
tenantId: samlData . organizationId ,
role: samlData . role ,
ssoProvider: "okta"
},
ttl: 28800 // 8 hours
});
// Redirect to app with session
res . redirect ( `/app?session= ${ session . id } ` );
});
4. Permission-Based Operations
Enforce fine-grained permissions:
const result = await agentbase . runAgent ({
message: req . body . message ,
auth: {
userId: user . id ,
permissions: user . permissions
},
system: `You have these permissions: ${ user . permissions . join ( ', ' ) }
Permission requirements:
- View data: requires 'read:data'
- Edit data: requires 'write:data'
- Delete data: requires 'delete:data' AND 'admin' role
- Access analytics: requires 'read:analytics'
- Export data: requires 'export:data'
Before performing any operation, verify user has required permission.
If permission denied, explain what permission is needed.` ,
preExecutionChecks: [
{
type: "permission" ,
required: [ "read:data" ],
action: "block_if_missing"
}
]
});
5. API Rate Limiting by User
Implement user-based rate limits:
// Rate limit per authenticated user
app . post ( '/api/agent' , async ( req , res ) => {
const apiKey = req . headers [ 'x-api-key' ];
// Validate API key and get user
const user = await agentbase . validateApiKey ( apiKey );
if ( ! user ) {
return res . status ( 401 ). json ({ error: "Invalid API key" });
}
// Check rate limit
const rateLimit = await agentbase . checkRateLimit ({
userId: user . id ,
endpoint: '/api/agent' ,
limit: user . rateLimit
});
if ( rateLimit . exceeded ) {
return res . status ( 429 ). json ({
error: "Rate limit exceeded" ,
limit: rateLimit . limit ,
remaining: 0 ,
resetAt: rateLimit . resetAt
});
}
// Execute agent
const result = await agentbase . runAgent ({
message: req . body . message ,
auth: {
userId: user . id ,
apiKeyId: apiKey . id
}
});
// Add rate limit headers
res . set ({
'X-RateLimit-Limit' : rateLimit . limit ,
'X-RateLimit-Remaining' : rateLimit . remaining ,
'X-RateLimit-Reset' : rateLimit . resetAt
});
res . json ( result );
});
6. Audit Logging
Track all authenticated actions:
const result = await agentbase . runAgent ({
message: "Delete customer account" ,
auth: {
userId: user . id ,
role: user . role
},
audit: {
enabled: true ,
log: {
action: "customer.delete" ,
resourceType: "customer" ,
resourceId: customerId ,
performedBy: user . id ,
ipAddress: req . ip ,
userAgent: req . headers [ 'user-agent' ]
}
}
});
// Query audit logs
const logs = await agentbase . getAuditLogs ({
userId: user . id ,
action: "customer.delete" ,
timeRange: "7d"
});
Best Practices
Security
Never Expose Secrets : API keys, JWT secrets, and OAuth client secrets must never be exposed in client-side code.
// Good: Server-side token storage
// Store tokens in secure database
await db . userTokens . create ({
userId: user . id ,
accessToken: encrypt ( tokens . accessToken ),
refreshToken: encrypt ( tokens . refreshToken ),
expiresAt: tokens . expiresAt
});
// Bad: Client-side token storage
localStorage . setItem ( 'token' , accessToken ); // Don't do this!
{
auth : {
type : "jwt" ,
token : jwtToken ,
expiresIn : 900 // 15 minutes
},
refreshToken : refreshToken , // For obtaining new tokens
autoRefresh : true
}
// Rotate tokens on each use
async function refreshUserToken ( refreshToken : string ) {
const newTokens = await agentbase . refreshToken ({
refreshToken ,
revokeOld: true // Revoke old refresh token
});
// Save new tokens
await saveTokens ( newTokens );
return newTokens ;
}
Access Control
Principle of Least Privilege : Grant minimum permissions necessary for each operation.
Define Clear Permission Scopes
const permissions = {
// Resource-based permissions
"read:customers" : "View customer data" ,
"write:customers" : "Create/update customers" ,
"delete:customers" : "Delete customers" ,
// Feature-based permissions
"access:analytics" : "View analytics dashboard" ,
"export:data" : "Export data to files" ,
// Admin permissions
"manage:users" : "Manage user accounts" ,
"manage:billing" : "Access billing"
};
const roles = {
viewer: [ "read:customers" , "read:orders" ],
member: [ "read:customers" , "write:customers" , "read:orders" , "write:orders" ],
admin: [ "*" ] // All permissions
};
function getUserPermissions ( role : string ) : string [] {
return roles [ role ] || [];
}
Check Permissions in Agent
system : `Before any operation, check if user has required permission.
Permission checks:
- To view data: verify 'read: ${ resource } ' permission
- To modify data: verify 'write: ${ resource } ' permission
- To delete data: verify 'delete: ${ resource } ' permission AND admin role
If permission denied:
- Explain what permission is needed
- Suggest contacting administrator
- Do not perform the operation`
OAuth Best Practices
// Generate PKCE challenge
const codeVerifier = generateRandomString ( 128 );
const codeChallenge = base64URLEncode ( sha256 ( codeVerifier ));
// Initiate OAuth with PKCE
const oauthUrl = await agentbase . initiateOAuth ({
provider: "google" ,
scopes: [ "profile" , "email" ],
pkce: {
codeChallenge ,
codeChallengeMethod: "S256"
}
});
// Complete OAuth with verifier
const tokens = await agentbase . completeOAuth ({
code ,
codeVerifier
});
async function callWithOAuth ( userId : string , action : Function ) {
let tokens = await getUserTokens ( userId );
// Check if token expired
if ( isTokenExpired ( tokens . accessToken )) {
tokens = await refreshOAuthToken ( tokens . refreshToken );
await saveUserTokens ( userId , tokens );
}
return action ( tokens . accessToken );
}
// Good: Request minimal scopes needed
{
scopes : [ "user:email" , "repo:status" ] // Only what's needed
}
// Avoid: Requesting excessive scopes
{
scopes : [ "user" , "repo" , "admin:org" ] // Too broad
}
Integration with Other Primitives
With Memory
User-scoped memory:
const result = await agentbase . runAgent ({
message: "Remember my preferences" ,
auth: {
userId: user . id ,
tenantId: user . tenantId
},
memory: {
namespace: `org_ ${ user . tenantId } _user_ ${ user . id } ` ,
enabled: true
}
});
// Memory automatically scoped to user
Learn more: Memory Primitive
With Data Connectors
Tenant-isolated database access:
const result = await agentbase . runAgent ({
message: "Query customer data" ,
auth: {
tenantId: user . tenantId
},
dataConnectors: {
postgres: {
enabled: true ,
tenantIsolation: {
enabled: true ,
tenantColumn: "tenant_id" ,
tenantValue: user . tenantId
}
}
}
});
Learn more: Data Connectors Primitive
With Integrations
OAuth-authenticated integrations:
const result = await agentbase . runAgent ({
message: "Create GitHub issue" ,
auth: {
userId: user . id ,
oauth: {
provider: "github" ,
accessToken: user . githubToken
}
},
integrations: {
github: { enabled: true }
}
});
Learn more: Integrations Primitive
Token Validation
JWT Validation : < 10ms per request
API Key Lookup : < 5ms (cached)
OAuth Token Refresh : 100-500ms
Session Management
Session Creation : < 50ms
Session Lookup : < 5ms (cached)
Concurrent Sessions : Thousands per user
Scalability
// Optimize authentication performance
{
auth : {
caching : {
enabled : true ,
ttl : 300 , // Cache auth context for 5 minutes
invalidateOn : [ "permission_change" , "role_change" ]
}
}
}
Troubleshooting
Problem : User cannot authenticateSolutions :
Verify API key is valid and not expired
Check JWT token signature
Ensure OAuth tokens not revoked
Verify user account is active
Check for clock skew issues
// Debug authentication
const debug = await agentbase . debugAuth ({
token: userToken ,
type: "jwt"
});
console . log ( 'Valid:' , debug . valid );
console . log ( 'Reason:' , debug . reason );
console . log ( 'Expires:' , debug . expiresAt );
Problem : User lacks permissions for operationSolutions :
Review user’s assigned permissions
Check role configuration
Verify tenant isolation is correct
Audit permission requirements
Update user permissions if appropriate
// Check user permissions
const permissions = await agentbase . getUserPermissions ( userId );
const hasPermission = permissions . includes ( 'write:customers' );
if ( ! hasPermission ) {
console . log ( 'User needs: write:customers' );
console . log ( 'User has:' , permissions );
}
Problem : OAuth access token expiredSolutions :
Implement automatic token refresh
Handle refresh token expiration
Re-authenticate user if needed
Check token expiration before use
// Auto-refresh expired tokens
{
auth : {
oauth : {
accessToken : token ,
refreshToken : refreshToken ,
autoRefresh : true
}
}
}
Advanced Patterns
Custom Authentication Provider
Implement custom auth:
await agentbase . registerAuthProvider ({
name: "company_sso" ,
validate : async ( credentials ) => {
// Custom validation logic
const user = await validateWithInternalSSO ( credentials );
return {
userId: user . id ,
role: user . role ,
permissions: user . permissions
};
}
});
Context-Based Access Control
Dynamic permissions based on context:
{
auth : {
userId : user . id ,
dynamicPermissions : async ( context ) => {
// Grant extra permissions based on context
if ( context . resource === "own_profile" ) {
return [ "write:profile" ];
}
return [];
}
}
}
Federated Identity
Support multiple identity providers:
const session = await agentbase . createSession ({
auth: {
federatedIdentity: {
provider: "google" ,
providerId: googleUser . sub ,
email: googleUser . email
},
mapToLocalUser: true // Link to local user account
}
});
Additional Resources
Pro Tip : Implement authentication early in development. Retrofitting auth into an existing system is much harder than building with it from the start.