OAuth Integration
Secure third-party authentication for Gmail, Slack, and other services in your agents.
Overview
Fiberwise provides enterprise-grade built-in OAuth 2.0 integration that allows your agents to securely authenticate with third-party services. This comprehensive system handles the complete OAuth lifecycle including token acquisition, refresh, storage, and revocation - all with proper user consent, app isolation, and enterprise security standards.
🏗️ OAuth Architecture Components
OAuth Authenticators API
/api/v1/oauth/authenticators
App-scoped OAuth configuration management with PostgreSQL persistence and validation
BaseCredentialService
Agent dependency injection service for secure credential access with automatic token refresh
Token Management
Encrypted storage, automatic refresh, expiration handling, and secure key rotation
App Isolation
User credentials scoped per application with strict access controls and audit trails
🔗 Supported OAuth Providers
📧 Google OAuth
Gmail API, Drive API, Calendar API
Full scope support including mail.readonly, mail.send, mail.modify
Production tested with email-agent-app
💬 Slack OAuth
Slack Web API, Bot API
Bot tokens, user tokens, workspace management
chat:write, channels:read, users:read scopes
📧 Microsoft OAuth
Microsoft Graph API
Outlook mail, Calendar, OneDrive integration
mail.read, mail.send, user.read scopes
� Yahoo OAuth
Yahoo Mail API
Email access and management
mail-r, mail-w permissions
🔧 Custom OAuth 2.0
Any RFC 6749 compliant service
Configurable endpoints, custom scopes
authorize_url, token_url, refresh_url
�️ Enterprise SSO
OIDC, SAML integrations
Enterprise identity providers
Coming soon with enterprise plans
🚀 Key OAuth Features
App-Scoped OAuth 2.0 Flow - Technical Implementation
Fiberwise implements a sophisticated app-scoped OAuth authentication system where each application manages its own OAuth authenticators with complete isolation and security:
1️⃣ Authenticator Creation
Developer creates OAuth authenticator via API
POST /api/v1/oauth/authenticators/{app_id}
• Validates app ownership and permissions
• Stores encrypted client credentials in PostgreSQL
• Creates app-scoped authenticator with unique ID
2️⃣ Authorization Initiation
User triggers OAuth flow in application
CredentialService.initiateAuth(providerId)
• Builds authorization URL with PKCE challenge
• Includes app-specific scopes and state parameter
• Redirects to provider's authorization server
3️⃣ User Authorization
User grants permissions at OAuth provider
• Provider displays consent screen with requested scopes
• User approves access for the specific application
• Provider generates authorization code with limited lifetime
4️⃣ Authorization Callback
Provider redirects back with authorization code
GET {redirect_uri}?code={auth_code}&state={state}
• Validates state parameter for CSRF protection
• Verifies PKCE code verifier if enabled
• Extracts authorization code for token exchange
5️⃣ Token Exchange
Exchange authorization code for access tokens
POST {token_url}
with client credentials
• Validates authorization code with provider
• Receives access_token, refresh_token, expires_in
• Handles provider-specific token response formats
6️⃣ Secure Token Storage
Encrypt and store tokens with app/user isolation
• Encrypts tokens using AES-256-GCM encryption
• Stores with composite key: (user_id, app_id, provider)
• Sets automatic expiration and refresh schedules
7️⃣ Agent Credential Access
Agents access APIs through BaseCredentialService
credential_service.get_credentials(provider)
• Automatic token refresh before expiration
• Validates token scopes against API requirements
• Returns ready-to-use authenticated credentials
8️⃣ API Request Execution
Make authenticated API calls to third-party services
• Include Bearer token in Authorization header
• Handle rate limiting and retry logic
• Log API usage for monitoring and compliance
🔧 Technical Implementation Details
Database Schema
oauth_authenticators
table with app_id foreign key
user_credentials
table with encrypted token storage
Composite indexes for fast credential lookup
Encryption Standards
AES-256-GCM for token encryption
Key derivation using PBKDF2
Separate encryption keys per environment
Token Refresh Logic
Background refresh 5 minutes before expiration
Exponential backoff on refresh failures
Graceful degradation with user notification
Security Features
PKCE (Proof Key for Code Exchange) support
State parameter validation for CSRF protection
Scope validation and minimum permission enforcement
OAuth Authenticator Setup - Complete API Reference
Create and manage OAuth authenticators for your application using the comprehensive OAuth Authenticators API with full validation and error handling:
🔧 Google OAuth Authenticator - Production Configuration
POST /api/v1/oauth/authenticators/{app_id}
Content-Type: application/json
Authorization: Bearer your-api-token
{
"name": "Google OAuth - Production",
"authenticator_type": "google",
"client_id": "123456789-abcdef.apps.googleusercontent.com",
"client_secret": "GOCSPX-your-client-secret",
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.send",
"https://www.googleapis.com/auth/gmail.modify",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
],
"authorize_url": "https://accounts.google.com/o/oauth2/auth",
"token_url": "https://oauth2.googleapis.com/token",
"redirect_uri": "https://your-domain.com/auth/callback/google",
"configuration": {
"access_type": "offline",
"prompt": "consent",
"include_granted_scopes": true,
"approval_prompt": "force"
}
}
📋 Google OAuth Response
{
"id": "auth_google_123456789",
"app_id": "app_987654321",
"name": "Google OAuth - Production",
"authenticator_type": "google",
"client_id": "123456789-abcdef.apps.googleusercontent.com",
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.send",
"https://www.googleapis.com/auth/gmail.modify",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
],
"authorize_url": "https://accounts.google.com/o/oauth2/auth",
"token_url": "https://oauth2.googleapis.com/token",
"redirect_uri": "https://your-domain.com/auth/callback/google",
"is_active": true,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z",
"configuration": {
"access_type": "offline",
"prompt": "consent",
"include_granted_scopes": true,
"approval_prompt": "force"
}
}
💬 Slack OAuth Authenticator - Bot and User Tokens
POST /api/v1/oauth/authenticators/{app_id}
Content-Type: application/json
Authorization: Bearer your-api-token
{
"name": "Slack Bot Integration",
"authenticator_type": "slack",
"client_id": "123456789.987654321",
"client_secret": "your-slack-client-secret",
"scopes": [
"chat:write",
"chat:write.public",
"channels:read",
"groups:read",
"users:read",
"users:read.email",
"bot"
],
"authorize_url": "https://slack.com/oauth/v2/authorize",
"token_url": "https://slack.com/api/oauth.v2.access",
"redirect_uri": "https://your-domain.com/auth/callback/slack",
"configuration": {
"user_scope": "chat:write,channels:read",
"team": "optional-team-id"
}
}
📧 Microsoft OAuth Authenticator - Graph API Integration
POST /api/v1/oauth/authenticators/{app_id}
Content-Type: application/json
Authorization: Bearer your-api-token
{
"name": "Microsoft Graph OAuth",
"authenticator_type": "microsoft",
"client_id": "your-azure-app-id",
"client_secret": "your-azure-client-secret",
"scopes": [
"https://graph.microsoft.com/mail.read",
"https://graph.microsoft.com/mail.send",
"https://graph.microsoft.com/mail.modify",
"https://graph.microsoft.com/user.read",
"https://graph.microsoft.com/calendars.read"
],
"authorize_url": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
"token_url": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
"redirect_uri": "https://your-domain.com/auth/callback/microsoft",
"configuration": {
"tenant": "common",
"response_mode": "query",
"resource": "https://graph.microsoft.com"
}
}
📧 Yahoo OAuth Authenticator - Mail API
POST /api/v1/oauth/authenticators/{app_id}
Content-Type: application/json
Authorization: Bearer your-api-token
{
"name": "Yahoo Mail OAuth",
"authenticator_type": "yahoo",
"client_id": "your-yahoo-client-id",
"client_secret": "your-yahoo-client-secret",
"scopes": ["mail-r", "mail-w"],
"authorize_url": "https://api.login.yahoo.com/oauth2/request_auth",
"token_url": "https://api.login.yahoo.com/oauth2/get_token",
"redirect_uri": "https://your-domain.com/auth/callback/yahoo",
"configuration": {
"response_type": "code"
}
}
🔧 Custom OAuth Provider - Generic Configuration
POST /api/v1/oauth/authenticators/{app_id}
Content-Type: application/json
Authorization: Bearer your-api-token
{
"name": "Custom API OAuth",
"authenticator_type": "custom",
"client_id": "your-custom-client-id",
"client_secret": "your-custom-client-secret",
"scopes": ["read", "write", "admin"],
"authorize_url": "https://your-provider.com/oauth/authorize",
"token_url": "https://your-provider.com/oauth/token",
"refresh_url": "https://your-provider.com/oauth/refresh",
"revoke_url": "https://your-provider.com/oauth/revoke",
"redirect_uri": "https://your-domain.com/auth/callback/custom",
"configuration": {
"token_endpoint_auth_method": "client_secret_post",
"response_type": "code",
"grant_type": "authorization_code",
"pkce_enabled": true,
"scope_separator": " "
}
}
✅ Validation Rules and Constraints
Required Fields
name
: 1-100 characters, must be unique per appauthenticator_type
: Must be supported provider typeclient_id
: Valid OAuth client identifierclient_secret
: Encrypted and stored securelyscopes
: Array of valid OAuth scopesredirect_uri
: Must match OAuth app configuration
URL Validation
authorize_url
: Must be HTTPS in productiontoken_url
: Valid OAuth token endpointredirect_uri
: Must match registered domain- URLs validated against known provider patterns
Security Checks
- App ownership verification
- Duplicate authenticator prevention
- Scope validation against provider limits
- Client credential format validation
BaseCredentialService - Complete Implementation Guide
The BaseCredentialService is the core dependency injection service that provides OAuth credentials to agents with automatic token management, refresh handling, and security validation:
🔧 Core Service Methods
Authentication Methods
# BaseCredentialService Interface
class BaseCredentialService:
async def get_credentials(self, provider: str, app_id: str = None) -> Dict:
"""
Get authenticated OAuth credentials for a provider
Args:
provider: OAuth provider type ('google', 'slack', 'microsoft', 'yahoo')
app_id: Application ID for credential scoping
Returns:
{
'access_token': 'ya29.a0...',
'refresh_token': 'refresh_token_value',
'expires_at': datetime(2024, 12, 31, 23, 59, 59),
'scopes': ['gmail.readonly', 'gmail.send'],
'provider': 'google',
'token_type': 'Bearer'
}
Raises:
CredentialNotFoundError: No credentials found for provider
TokenExpiredError: Token expired and refresh failed
InvalidScopeError: Required scopes not available
"""
async def is_connected(self, provider: str, app_id: str = None) -> bool:
"""Check if user has valid OAuth connection for provider"""
async def list_connections(self, app_id: str = None) -> List[Dict]:
"""
List all OAuth connections for user/app
Returns:
[
{
'authenticator_id': 'auth_123',
'authenticator_type': 'google',
'name': 'Google OAuth',
'is_connected': True,
'connected_at': datetime(2024, 1, 15, 10, 30),
'last_used': datetime(2024, 1, 20, 14, 25),
'scopes': ['gmail.readonly', 'gmail.send'],
'expires_at': datetime(2024, 12, 31, 23, 59, 59)
}
]
"""
async def refresh_token(self, provider: str, app_id: str = None) -> bool:
"""Force refresh OAuth token for provider"""
async def revoke_connection(self, provider: str, app_id: str = None) -> bool:
"""Revoke OAuth connection and cleanup tokens"""
Authorization Flow Methods
async def get_auth_url(self, provider: str, app_id: str = None,
return_to: str = None) -> str:
"""
Generate OAuth authorization URL for provider
Args:
provider: OAuth provider type
app_id: Application ID for app-scoped auth
return_to: URL to redirect after successful auth
Returns:
'https://accounts.google.com/o/oauth2/auth?client_id=123&scope=gmail.readonly&...'
"""
async def initiate_auth(self, provider: str, app_id: str = None,
return_to: str = None) -> None:
"""
Initiate OAuth flow by redirecting browser to provider
Uses AppBridge router for seamless navigation
"""
async def handle_callback(self, provider: str, code: str, state: str) -> Dict:
"""
Handle OAuth callback and exchange code for tokens
Args:
provider: OAuth provider type
code: Authorization code from provider
state: State parameter for CSRF protection
Returns:
{
'success': True,
'access_token': 'ya29.a0...',
'expires_in': 3600,
'scopes': ['gmail.readonly']
}
"""
Validation and Utility Methods
async def validate_scopes(self, provider: str, required_scopes: List[str],
app_id: str = None) -> bool:
"""Validate that current token has required OAuth scopes"""
async def get_token_info(self, provider: str, app_id: str = None) -> Dict:
"""
Get detailed token information for debugging
Returns:
{
'provider': 'google',
'issued_at': datetime(2024, 1, 15, 10, 30),
'expires_at': datetime(2024, 1, 15, 11, 30),
'scopes': ['gmail.readonly', 'gmail.send'],
'is_expired': False,
'needs_refresh': False,
'last_refreshed': datetime(2024, 1, 15, 10, 25)
}
"""
async def get_user_info(self, provider: str, app_id: str = None) -> Dict:
"""
Get OAuth user information from provider
Returns:
{
'email': '[email protected]',
'name': 'John Doe',
'provider_user_id': '123456789',
'verified': True,
'picture': 'https://lh3.googleusercontent.com/...'
}
"""
🚀 Advanced Service Features
Automatic Token Refresh
- Background refresh 5 minutes before expiration
- Exponential backoff on refresh failures
- Graceful fallback to manual re-authentication
- Thread-safe refresh operations with locking
Security and Validation
- Token encryption using AES-256-GCM
- Scope validation before API access
- CSRF protection with state parameters
- Audit logging for all credential operations
Error Handling
- Comprehensive exception hierarchy
- Automatic retry logic for transient failures
- User-friendly error messages
- Detailed logging for troubleshooting
Performance Optimization
- In-memory token caching with TTL
- Connection pooling for database operations
- Async/await throughout for non-blocking I/O
- Lazy loading of provider configurations
⚠️ Exception Handling
# Credential Service Exception Hierarchy
class CredentialServiceError(Exception):
"""Base exception for credential service errors"""
class CredentialNotFoundError(CredentialServiceError):
"""No OAuth credentials found for the specified provider"""
class TokenExpiredError(CredentialServiceError):
"""OAuth token has expired and refresh failed"""
class InvalidScopeError(CredentialServiceError):
"""Token doesn't have required OAuth scopes"""
class AuthenticationFailedError(CredentialServiceError):
"""OAuth authentication process failed"""
class ProviderNotSupportedError(CredentialServiceError):
"""OAuth provider type is not supported"""
# Usage in agent code
try:
credentials = await self.credential_service.get_credentials('google')
except CredentialNotFoundError:
return {"error": "Please connect your Google account first"}
except TokenExpiredError:
return {"error": "Please re-authenticate with Google"}
except InvalidScopeError as e:
return {"error": f"Missing permissions: {e.missing_scopes}"}
Agent OAuth Integration - Complete Production Implementation
This section shows the complete EmailAgent implementation from the production email-agent-app, demonstrating real-world OAuth integration patterns with dependency injection, error handling, and multi-provider support:
🐍 EmailAgent - Complete Implementation
# email_agent.py - Production implementation from email-agent-app
import asyncio
import logging
from typing import Dict, List, Optional, Any
from datetime import datetime
from fiberwise_sdk import FiberApp, FiberAgent
from fiberwise_sdk.services import BaseCredentialService, BaseLLMService, BaseDataService
from fiberwise_sdk.exceptions import CredentialServiceError, CredentialNotFoundError
class EmailAgent(FiberAgent):
"""
Production email agent with multi-provider OAuth support
Supports Gmail, Outlook, and Yahoo Mail via OAuth 2.0
"""
def __init__(self,
fiber: FiberApp,
credential_service: BaseCredentialService,
llm_service: BaseLLMService,
data_service: BaseDataService):
"""
Initialize EmailAgent with dependency injection
Args:
fiber: FiberApp instance for app context
credential_service: OAuth credential management service
llm_service: LLM service for email analysis
data_service: Data persistence service
"""
super().__init__(fiber)
self.credential_service = credential_service
self.llm_service = llm_service
self.data_service = data_service
self.logger = logging.getLogger(__name__)
# Provider-specific configurations
self.PROVIDER_CONFIGS = {
'google': {
'api_base': 'https://www.googleapis.com/gmail/v1',
'required_scopes': [
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/gmail.send'
],
'rate_limit': 250 # requests per second
},
'microsoft': {
'api_base': 'https://graph.microsoft.com/v1.0',
'required_scopes': [
'https://graph.microsoft.com/mail.read',
'https://graph.microsoft.com/mail.send'
],
'rate_limit': 100
},
'yahoo': {
'api_base': 'https://api.mail.yahoo.com',
'required_scopes': ['mail-r', 'mail-w'],
'rate_limit': 50
}
}
async def run_agent(self, messages: List[Dict], context: Dict) -> Dict:
"""
Main agent execution with comprehensive OAuth handling
Args:
messages: Conversation history
context: Execution context with user and app information
Returns:
Agent response with email data and analysis
"""
try:
# 1. Detect and validate email provider
provider_info = await self._detect_and_validate_provider(context)
if not provider_info['connected']:
return await self._handle_authentication_required(provider_info, context)
# 2. Validate OAuth scopes
scope_validation = await self._validate_oauth_scopes(
provider_info['provider_type'],
context.get('app_id')
)
if not scope_validation['valid']:
return await self._handle_insufficient_scopes(scope_validation)
# 3. Get fresh OAuth credentials
credentials = await self.credential_service.get_credentials(
provider=provider_info['provider_type'],
app_id=context.get('app_id')
)
# 4. Parse user intent from messages
user_intent = await self._parse_user_intent(messages)
# 5. Execute email operations based on intent
email_result = await self._execute_email_operations(
intent=user_intent,
provider=provider_info['provider_type'],
credentials=credentials,
context=context
)
# 6. Use LLM to analyze and respond
llm_response = await self._generate_intelligent_response(
email_data=email_result,
user_intent=user_intent,
messages=messages
)
# 7. Log usage for analytics
await self._log_agent_usage(
provider=provider_info['provider_type'],
operation=user_intent['operation'],
context=context
)
return {
"response": llm_response,
"email_data": email_result,
"provider": provider_info['provider_type'],
"authenticated": True,
"timestamp": datetime.utcnow().isoformat(),
"usage_stats": {
"emails_processed": email_result.get('count', 0),
"api_calls": email_result.get('api_calls', 0),
"tokens_used": llm_response.get('tokens_used', 0)
}
}
except CredentialNotFoundError:
return await self._handle_credential_error(context)
except Exception as e:
self.logger.error(f"EmailAgent execution failed: {str(e)}", exc_info=True)
return {
"error": "Failed to process email request",
"error_type": type(e).__name__,
"authenticated": False,
"timestamp": datetime.utcnow().isoformat()
}
async def _detect_and_validate_provider(self, context: Dict) -> Dict:
"""
Detect email provider and validate connection status
Returns:
{
'provider_type': 'google',
'connected': True,
'connection_info': {...},
'available_providers': ['google', 'microsoft']
}
"""
app_id = context.get('app_id')
# Get all OAuth connections for this user/app
connections = await self.credential_service.list_connections(app_id=app_id)
# Filter for email providers
email_providers = [
conn for conn in connections
if conn['authenticator_type'] in self.PROVIDER_CONFIGS.keys()
and conn['is_connected']
]
if not email_providers:
available_authenticators = [
conn for conn in connections
if conn['authenticator_type'] in self.PROVIDER_CONFIGS.keys()
]
return {
'provider_type': None,
'connected': False,
'connection_info': None,
'available_providers': [auth['authenticator_type'] for auth in available_authenticators]
}
# Use the most recently connected provider
primary_provider = max(email_providers, key=lambda x: x['connected_at'])
return {
'provider_type': primary_provider['authenticator_type'],
'connected': True,
'connection_info': primary_provider,
'available_providers': [p['authenticator_type'] for p in email_providers]
}
async def _validate_oauth_scopes(self, provider_type: str, app_id: str) -> Dict:
"""
Validate that OAuth token has required scopes for email operations
Returns:
{
'valid': True,
'missing_scopes': [],
'current_scopes': ['gmail.readonly', 'gmail.send']
}
"""
try:
required_scopes = self.PROVIDER_CONFIGS[provider_type]['required_scopes']
# Check if token has required scopes
is_valid = await self.credential_service.validate_scopes(
provider=provider_type,
required_scopes=required_scopes,
app_id=app_id
)
if is_valid:
token_info = await self.credential_service.get_token_info(
provider=provider_type,
app_id=app_id
)
return {
'valid': True,
'missing_scopes': [],
'current_scopes': token_info.get('scopes', [])
}
else:
# Get current scopes to determine what's missing
token_info = await self.credential_service.get_token_info(
provider=provider_type,
app_id=app_id
)
current_scopes = set(token_info.get('scopes', []))
required_scopes_set = set(required_scopes)
missing_scopes = list(required_scopes_set - current_scopes)
return {
'valid': False,
'missing_scopes': missing_scopes,
'current_scopes': list(current_scopes)
}
except Exception as e:
self.logger.error(f"Scope validation failed: {str(e)}")
return {
'valid': False,
'missing_scopes': [],
'current_scopes': [],
'error': str(e)
}
async def _parse_user_intent(self, messages: List[Dict]) -> Dict:
"""
Parse user intent from conversation using LLM
Returns:
{
'operation': 'read_emails',
'parameters': {
'query': 'unread emails from last week',
'limit': 10,
'folder': 'inbox'
},
'confidence': 0.95
}
"""
if not messages:
return {
'operation': 'read_emails',
'parameters': {'limit': 10, 'folder': 'inbox'},
'confidence': 0.5
}
latest_message = messages[-1].get('content', '')
# Use LLM to parse intent
intent_prompt = f"""
Analyze this email-related request and extract the intent:
"{latest_message}"
Possible operations: read_emails, send_email, search_emails, manage_folders
Return JSON with operation, parameters, and confidence.
"""
try:
intent_response = await self.llm_service.analyze_text(
text=intent_prompt,
prompt="Extract structured intent from user request"
)
# Parse LLM response into structured intent
# Implementation would include JSON parsing and validation
return {
'operation': 'read_emails', # Default fallback
'parameters': {'limit': 10, 'folder': 'inbox'},
'confidence': 0.8,
'raw_response': intent_response
}
except Exception as e:
self.logger.warning(f"Intent parsing failed, using default: {str(e)}")
return {
'operation': 'read_emails',
'parameters': {'limit': 10, 'folder': 'inbox'},
'confidence': 0.5
}
async def _execute_email_operations(self, intent: Dict, provider: str,
credentials: Dict, context: Dict) -> Dict:
"""
Execute email operations based on user intent and provider
Returns:
{
'success': True,
'data': [...],
'count': 10,
'api_calls': 3,
'operation': 'read_emails'
}
"""
operation = intent['operation']
parameters = intent.get('parameters', {})
try:
if operation == 'read_emails':
return await self._fetch_emails(provider, credentials, parameters)
elif operation == 'send_email':
return await self._send_email(provider, credentials, parameters)
elif operation == 'search_emails':
return await self._search_emails(provider, credentials, parameters)
else:
raise ValueError(f"Unsupported operation: {operation}")
except Exception as e:
self.logger.error(f"Email operation failed: {str(e)}")
return {
'success': False,
'error': str(e),
'operation': operation,
'api_calls': 0
}
async def _fetch_emails(self, provider: str, credentials: Dict,
parameters: Dict) -> Dict:
"""
Fetch emails using provider-specific API implementation
"""
if provider == 'google':
return await self._fetch_gmail_emails(credentials, parameters)
elif provider == 'microsoft':
return await self._fetch_outlook_emails(credentials, parameters)
elif provider == 'yahoo':
return await self._fetch_yahoo_emails(credentials, parameters)
else:
raise ValueError(f"Unsupported email provider: {provider}")
async def _fetch_gmail_emails(self, credentials: Dict, parameters: Dict) -> Dict:
"""
Fetch emails from Gmail API using OAuth credentials
This is a simplified example - production implementation would include:
- Full Gmail API integration
- Rate limiting and retry logic
- Comprehensive error handling
- Pagination support
- Message parsing and formatting
"""
import aiohttp
access_token = credentials['access_token']
limit = parameters.get('limit', 10)
query = parameters.get('query', 'in:inbox')
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
async with aiohttp.ClientSession() as session:
# List messages
list_url = f"https://www.googleapis.com/gmail/v1/users/me/messages"
list_params = {'q': query, 'maxResults': limit}
async with session.get(list_url, headers=headers, params=list_params) as response:
if response.status == 200:
list_data = await response.json()
message_ids = [msg['id'] for msg in list_data.get('messages', [])]
# Fetch message details
messages = []
for msg_id in message_ids[:limit]:
msg_url = f"https://www.googleapis.com/gmail/v1/users/me/messages/{msg_id}"
async with session.get(msg_url, headers=headers) as msg_response:
if msg_response.status == 200:
msg_data = await msg_response.json()
messages.append(self._parse_gmail_message(msg_data))
return {
'success': True,
'data': messages,
'count': len(messages),
'api_calls': 1 + len(message_ids),
'operation': 'read_emails',
'provider': 'google'
}
else:
raise Exception(f"Gmail API error: {response.status}")
def _parse_gmail_message(self, msg_data: Dict) -> Dict:
"""Parse Gmail API message response into standardized format"""
payload = msg_data.get('payload', {})
headers = {h['name']: h['value'] for h in payload.get('headers', [])}
return {
'id': msg_data['id'],
'subject': headers.get('Subject', 'No Subject'),
'from': headers.get('From', 'Unknown Sender'),
'to': headers.get('To', ''),
'date': headers.get('Date', ''),
'snippet': msg_data.get('snippet', ''),
'labels': msg_data.get('labelIds', []),
'thread_id': msg_data.get('threadId', ''),
'size_estimate': msg_data.get('sizeEstimate', 0)
}
# Additional helper methods for other providers and operations...
async def _fetch_outlook_emails(self, credentials: Dict, parameters: Dict) -> Dict:
"""Microsoft Graph API implementation"""
# Implementation for Microsoft Graph API
pass
async def _fetch_yahoo_emails(self, credentials: Dict, parameters: Dict) -> Dict:
"""Yahoo Mail API implementation"""
# Implementation for Yahoo Mail API
pass
App-Scoped User Authentication Flow
Users authenticate with third-party services through an app-specific OAuth flow:
- App Setup - Developer creates OAuth authenticator for their app via API
- User Initiate - User clicks "Connect Gmail" in the application
- OAuth Redirect - Application redirects to provider's authorization page
- User Consent - User grants permissions to the specific application
- Authorization Callback - Provider redirects back with authorization code
- Token Exchange - FiberWise exchanges code for access/refresh tokens
- Secure Storage - Tokens stored with user and app isolation
- Agent Access - Agents access APIs through BaseCredentialService
🔄 Authentication Flow Diagram
App → OAuth Provider → User Consent → FiberWise → Agent Access
OAuth Authenticators API
Manage OAuth authenticators for your applications via the REST API:
🔗 Create OAuth Authenticator
curl -X POST /api/v1/oauth/authenticators/{app_id} \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-token" \
-d '{
"name": "Gmail OAuth",
"authenticator_type": "google",
"client_id": "your-client-id",
"client_secret": "your-client-secret",
"scopes": ["https://www.googleapis.com/auth/gmail.readonly"],
"authorize_url": "https://accounts.google.com/o/oauth2/auth",
"token_url": "https://oauth2.googleapis.com/token",
"redirect_uri": "https://your-app.com/auth/callback/google"
}'
📋 List App Authenticators
curl -X GET /api/v1/oauth/authenticators?app_id={app_id} \
-H "Authorization: Bearer your-api-token"
🔍 Get Specific Authenticator
curl -X GET /api/v1/oauth/authenticators/{authenticator_id} \
-H "Authorization: Bearer your-api-token"
🗑️ Delete Authenticator
curl -X DELETE /api/v1/oauth/authenticators/{authenticator_id} \
-H "Authorization: Bearer your-api-token"
📊 Response Format
{
"id": "auth_123456789",
"app_id": "app_987654321",
"name": "Gmail OAuth",
"authenticator_type": "google",
"scopes": ["https://www.googleapis.com/auth/gmail.readonly"],
"is_active": true,
"created_at": "2024-01-15T10:30:00Z",
"redirect_uri": "https://your-app.com/auth/callback/google"
}
App Manifest Configuration
Configure OAuth authenticators in your app manifest (example from email-agent-app):
# app_manifest.yaml
name: "Email Agent App"
description: "Email management with multi-provider OAuth support"
version: "2.0.0"
# OAuth Provider Configurations
authenticators:
- name: "Google OAuth"
authenticator_type: "google"
description: "Connect to Gmail for email management"
permissions:
- "https://www.googleapis.com/auth/gmail.readonly"
- "https://www.googleapis.com/auth/gmail.send"
- "https://www.googleapis.com/auth/gmail.modify"
- "https://www.googleapis.com/auth/userinfo.email"
is_default: true
- name: "Microsoft OAuth"
authenticator_type: "microsoft"
description: "Connect to Outlook for email management"
permissions:
- "https://graph.microsoft.com/mail.read"
- "https://graph.microsoft.com/mail.send"
is_default: false
- name: "Yahoo OAuth"
authenticator_type: "yahoo"
description: "Connect to Yahoo Mail"
permissions:
- "mail-r"
- "mail-w"
is_default: false
# Agent Dependencies
agent:
name: "EmailAgent"
dependencies:
- "BaseCredentialService"
- "BaseLLMService"
- "BaseDataService"
oauth_required: true
Security Features
🔐 Token Security
- Encrypted token storage
- Automatic refresh handling
- Secure key rotation
- Expiration management
🎯 Scope Management
- Minimal required permissions
- User consent verification
- Granular access control
- Scope validation
📊 Audit Trail
- Authentication logging
- API usage tracking
- Failed attempt monitoring
- Compliance reporting