This page documents the security architecture of the Prebid Sales Agent, covering authentication mechanisms, principal resolution, multi-tenant access control, encryption, audit logging, and operational security practices.
The Sales Agent uses three distinct authentication mechanisms depending on the interface being accessed.
AI agents connecting via the Model Context Protocol authenticate using the x-adcp-auth header. Each principal (advertiser) receives a unique token generated through the Admin UI.
curl -H "x-adcp-auth: <principal-token>" https://yourdomain.com/mcp/
The token is a per-principal credential that maps the request to a specific advertiser and tenant. All MCP tool calls require this header.
Agent-to-Agent protocol requests use standard Authorization: Bearer token authentication:
curl -H "Authorization: Bearer <principal-token>" https://yourdomain.com/a2a
The same principal token works for both MCP and A2A authentication, but the header format differs.
The Admin UI supports two authentication modes:
| Mode | Mechanism | Use Case |
|---|---|---|
| Setup Mode | Password (test123) |
Initial setup and evaluation only |
| OAuth SSO | Google, Microsoft, Okta, Auth0, Keycloak | Production deployments |
Setup Mode password authentication (ADCP_AUTH_TEST_MODE=true) must never be used in production. It enables well-known credentials that grant full administrative access.
Supported OAuth providers:
| Provider | Protocol | Configuration |
|---|---|---|
| OAuth 2.0 / OpenID Connect | Client ID, Client Secret | |
| Microsoft | OAuth 2.0 / OpenID Connect | Client ID, Client Secret, Tenant ID |
| Okta | OAuth 2.0 / OpenID Connect | Client ID, Client Secret, Domain |
| Auth0 | OAuth 2.0 / OpenID Connect | Client ID, Client Secret, Domain |
| Keycloak | OAuth 2.0 / OpenID Connect | Client ID, Client Secret, Realm, Server URL |
When an API request arrives, the Sales Agent resolves the principal through a chain that maps the token to a tenant and adapter configuration:
┌────────────────┐ ┌─────────────┐ ┌────────────┐ ┌─────────────┐
│ Auth Token │───▶│ Principal │───▶│ Tenant │───▶│ Adapter │
│ (header) │ │ (advertiser)│ │ (publisher) │ │ (ad server) │
└────────────────┘ └─────────────┘ └────────────┘ └─────────────┘
x-adcp-auth or Authorization: Bearer headerThis resolution chain ensures every API request is scoped to a specific advertiser operating within a specific publisher’s context, using the correct ad server integration.
Tokens are stored as hashes in the database, not in plaintext. The original token value is shown only once at generation time.
In multi-tenant deployments, the Sales Agent enforces strict data isolation between publishers.
Tenant isolation is enforced at the database level through a composite (tenant_id, email) constraint. This ensures:
| Role | Scope | Capabilities |
|---|---|---|
| Super Admin | All tenants | Create/deactivate tenants, manage cross-tenant settings, view all activity |
| Tenant Admin | Single tenant | Manage products, advertisers, creatives, workflows, and settings for their tenant |
| Principal | Single tenant, limited | Submit media buys and creatives, view own campaigns and delivery data |
The following safeguards prevent cross-tenant data access:
tenant_id in their WHERE clauseThe Sales Agent uses Fernet symmetric encryption (from the Python cryptography library) to protect sensitive data at rest. Fernet provides authenticated encryption using AES-128-CBC with HMAC-SHA256.
Encrypted fields include:
| Data | Location | Purpose |
|---|---|---|
| Principal API tokens | principals table |
Advertiser authentication credentials |
| Ad server credentials | adapter_config table |
GAM service account keys, API keys |
| OAuth client secrets | sso_config table |
Identity provider credentials |
The ENCRYPTION_KEY environment variable holds the Fernet key used for all encryption operations.
# Generate a new Fernet key
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
The ENCRYPTION_KEY is the root secret for the entire deployment. If lost, all encrypted data becomes unrecoverable. Store it securely (e.g., in a secrets manager) and back it up separately from the deployment.
Key rotation requires decrypting all data with the old key and re-encrypting with the new key. This is a manual operation – contact the development team for guidance if key rotation is needed.
Super admins have cross-tenant administrative privileges and are configured through environment variables.
| Variable | Format | Description |
|---|---|---|
SUPER_ADMIN_EMAILS |
Comma-separated emails | Specific email addresses granted super admin access |
SUPER_ADMIN_DOMAINS |
Comma-separated domains | All users from these email domains are granted super admin access |
Example:
SUPER_ADMIN_EMAILS=admin@yourcompany.com,ops@yourcompany.com
SUPER_ADMIN_DOMAINS=yourcompany.com
Super admin access is a whitelist – only emails or domains explicitly listed are granted elevated privileges. There is no default super admin. If neither variable is set, no user has cross-tenant access.
HTTPS is mandatory for all production deployments. The Sales Agent enforces HTTPS for all non-localhost connections to protect authentication tokens in transit.
Recommended SSL/TLS configuration:
For localhost development (http://localhost:8000), HTTPS enforcement is relaxed to allow evaluation without certificate setup.
Principal tokens follow a defined lifecycle managed entirely through the Admin UI:
The token is hashed before storage. The plaintext value cannot be retrieved after generation.
To rotate a token (e.g., if compromised or as part of regular security hygiene):
Token rotation is immediate. The old token stops working as soon as the new token is generated. Coordinate with the advertiser before rotating to avoid disrupting active campaigns.
To revoke access without generating a replacement token:
Revoked principals can have new tokens generated later if access needs to be restored.
The Sales Agent maintains a detailed audit log backed by the PostgreSQL database. Every significant operation is recorded for compliance, debugging, and security analysis.
| Category | Events |
|---|---|
| Authentication | Login attempts, token usage, failed authentication, SSO callbacks |
| Media Buys | Submissions, approvals, rejections, modifications |
| Creatives | Uploads, approval/rejection, format validation results |
| Products | Creation, modification, deletion, pricing changes |
| Advertisers | Principal creation, token generation/rotation/revocation |
| Configuration | Adapter changes, SSO updates, domain changes, tenant settings |
| Security | Cross-tenant access attempts, unauthorized requests, rate limit violations |
Each audit log entry includes:
| Field | Description |
|---|---|
timestamp |
UTC timestamp of the event |
tenant_id |
Tenant context for the operation |
principal_id |
The principal that performed the action (if applicable) |
user_email |
The admin user email (for Admin UI actions) |
operation |
The operation type (e.g., media_buy.created, creative.approved) |
details |
JSON payload with operation-specific data |
ip_address |
Source IP address of the request |
The audit log captures security-relevant events that can be used to detect unauthorized access patterns:
Tenant deactivation is a soft delete mechanism that immediately blocks all access while preserving data for potential reactivation.
| Aspect | Behavior |
|---|---|
| MCP/A2A API access | Immediately blocked for all principals |
| Admin UI access | Blocked for tenant admins |
| Data (campaigns, creatives, logs) | Preserved in database |
| Active campaigns | Stopped in the ad server (adapter-dependent) |
| Public discovery | Tenant removed from agent card listings |
Only super admins can reactivate a deactivated tenant:
Deactivation does not delete or invalidate tokens. Upon reactivation, existing principal tokens work immediately without regeneration.