Skip to main content

Authentication

ASCEND Authorization Center uses API key authentication for SDK and agent integrations.

Authentication Method

API Keys are the primary authentication method for server-to-server communication and SDK usage.

MethodUse CaseHeader
API KeySDK, Agents, AutomationAuthorization: Bearer {api_key}
Session TokenWeb DashboardCookie: session_token

Generating an API Key

  1. Log in to ASCEND Dashboard
  2. Navigate to SettingsAPI Keys
  3. Click Generate New Key
  4. Provide a name (e.g., "Production Agent Key")
  5. Copy the key immediately (shown only once)

Endpoint: POST /api/keys/generate Source: /ow-ai-backend/routes/api_key_routes.py:221

Via API (Programmatic)

import requests

# Must authenticate with existing session or API key
headers = {
"Authorization": f"Bearer {existing_key}",
"Content-Type": "application/json"
}

response = requests.post(
"https://pilot.owkai.app/api/keys/generate",
headers=headers,
json={
"name": "Production Agent Key",
"description": "API key for production agent fleet",
"expires_in_days": 90
}
)

key_data = response.json()
print(f"New API Key: {key_data['api_key']}") # Save this immediately
print(f"Key Prefix: {key_data['key_prefix']}")
Critical Security Notice

API keys are shown only once at generation time. The full key is never stored in plaintext on the server (SHA-256 hashed with salt). If you lose the key, you must generate a new one.

Source: /ow-ai-backend/routes/api_key_routes.py:144-175 (cryptographic key generation)

API Key Format

API keys use a role-based prefix for easy identification:

owkai_{role}_{random_token}

Role-based prefixes:

RolePrefixExample
Super Adminowkai_super_admin_owkai_super_admin_L4WQMHTFCN7MTf...
Adminowkai_admin_owkai_admin_xK9mNpQrStUvWxYz...
Userowkai_user_owkai_user_aB3dEfG789hijKLMn...

Key structure:

  • Role prefix: 12-18 characters (varies by role)
  • Random token: 43 characters (256-bit entropy via secrets.token_urlsafe(32))
  • Total key length: 55-61 characters

Implementation: /ow-ai-backend/routes/api_key_routes.py:144-175

Key Prefix Security (SEC-096)

The first 32 characters of your key are stored as a lookup prefix. This includes the role prefix plus 14+ random characters, providing ~84 bits of entropy to prevent prefix collisions.

Using API Keys

Python

import os
import requests

# Method 1: Environment variable (recommended)
api_key = os.getenv('OWKAI_API_KEY')
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}

# Method 2: Direct initialization
client = OWKAIClient(api_key="owkai_admin_xxxx")

# Submit action with authentication
response = requests.post(
"https://pilot.owkai.app/api/v1/actions/submit",
headers=headers,
json={
"agent_id": "my-agent",
"action_type": "data_access",
"description": "Access customer records",
"tool_name": "database_query"
}
)

Node.js

const axios = require('axios');

// Method 1: Environment variable (recommended)
const apiKey = process.env.OWKAI_API_KEY;
const headers = {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
};

// Submit action with authentication
const response = await axios.post(
'https://pilot.owkai.app/api/v1/actions/submit',
{
agent_id: 'my-agent',
action_type: 'data_access',
description: 'Access customer records',
tool_name: 'database_query'
},
{ headers }
);

cURL

# Set environment variable
export OWKAI_API_KEY="owkai_admin_xxxxxxxxxxxx"

# Submit action
curl -X POST https://pilot.owkai.app/api/v1/actions/submit \
-H "Authorization: Bearer $OWKAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "my-agent",
"action_type": "data_access",
"description": "Access customer records",
"tool_name": "database_query"
}'

Authentication Flow

SDK Authentication

┌──────────┐                          ┌──────────────┐
│ SDK │ │ ASCEND API │
└────┬─────┘ └──────┬───────┘
│ │
│ 1. POST /api/v1/actions/submit │
│ Authorization: Bearer owkai_admin_xxx │
│────────────────────────────────────────►│
│ │
│ 2. Validate API key │
│ - Lookup key_hash in database │
│ - Verify not revoked │
│ - Check organization_id │
│ ◄──┤
│ │
│ 3. Return authorization decision │
│◄────────────────────────────────────│
│ { status: "approved", risk_score: 35 } │

Authentication Implementation:

  • API Key Validation: /ow-ai-backend/dependencies_api_keys.py
  • Dual Auth (JWT + API Key): /ow-ai-backend/routes/authorization_routes.py:2206 (SEC-020)

Managing API Keys

List Keys

View all your API keys (masked for security):

Endpoint: GET /api/keys/list Source: /ow-ai-backend/routes/api_key_routes.py:340

response = requests.get(
"https://pilot.owkai.app/api/keys/list",
headers={"Authorization": f"Bearer {api_key}"}
)

keys = response.json()
for key in keys['keys']:
print(f"Name: {key['name']}")
print(f"Prefix: {key['key_prefix']}") # Only first 32 chars shown
print(f"Created: {key['created_at']}")
print(f"Last Used: {key['last_used_at']}")
print(f"Status: {'Active' if key['is_active'] else 'Revoked'}")

Revoke Key

Immediately revoke an API key (soft delete with audit trail):

Endpoint: DELETE /api/keys/{key_id}/revoke Source: /ow-ai-backend/routes/api_key_routes.py:407

response = requests.delete(
f"https://pilot.owkai.app/api/keys/{key_id}/revoke",
headers={"Authorization": f"Bearer {api_key}"},
params={"reason": "Key rotation - replacing with new key"}
)

if response.status_code == 200:
print(f"Key revoked at: {response.json()['revoked_at']}")

Key Usage Statistics

Monitor API key usage for security auditing:

Endpoint: GET /api/keys/{key_id}/usage Source: /ow-ai-backend/routes/api_key_routes.py:493

response = requests.get(
f"https://pilot.owkai.app/api/keys/{key_id}/usage",
headers={"Authorization": f"Bearer {api_key}"}
)

usage = response.json()
print(f"Total Requests: {usage['statistics']['total_requests']}")
print(f"Success Rate: {usage['statistics']['success_rate']}%")
print(f"Last Used: {usage['statistics']['last_used_at']}")

# Recent activity log
for activity in usage['recent_activity']:
print(f"{activity['timestamp']}: {activity['method']} {activity['endpoint']} - {activity['status']}")

Security Best Practices

1. Store Keys in Environment Variables

# .env file (never commit to git)
OWKAI_API_KEY=owkai_admin_xxxxxxxxxxxx
OWKAI_API_URL=https://pilot.owkai.app
# Load in application
from dotenv import load_dotenv
import os

load_dotenv()
api_key = os.getenv('OWKAI_API_KEY')

2. Rotate Keys Regularly

Recommended: Rotate keys every 90 days

# Generate new key before revoking old one (zero-downtime rotation)
new_key_response = client.generate_api_key(
name="Production Key v2",
expires_in_days=90
)

# Update application with new key
update_production_config(new_key_response['api_key'])

# Wait for deployment to complete
wait_for_deployment()

# Revoke old key
client.revoke_api_key(old_key_id, reason="Scheduled key rotation")

3. Never Expose Keys in Client-Side Code

// WRONG - Never do this
const apiKey = 'owkai_admin_xxxxxxxxxxxx';
fetch('https://pilot.owkai.app/api/v1/actions/submit', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});

// RIGHT - Use backend proxy
fetch('/api/my-backend-proxy', {
method: 'POST',
body: JSON.stringify(action)
});

4. Monitor Key Usage

Set up alerts for suspicious activity:

def check_key_usage(key_id):
usage = client.get_key_usage(key_id)

# Alert on high error rate
if usage['statistics']['success_rate'] < 90:
send_alert(f"API key {key_id} has low success rate")

# Alert on unusual activity spikes
recent_requests = len(usage['recent_activity'])
if recent_requests > 1000: # Per hour
send_alert(f"API key {key_id} showing unusual activity")

Security Implementation

Key Storage (Enterprise Grade)

Source: /ow-ai-backend/routes/api_key_routes.py:87-117

Keys are stored using SHA-256 hashing with per-key random salt:

# Generation (never stored in plaintext)
raw_key = secrets.token_urlsafe(32) # 256-bit entropy
full_key = f"owkai_{role}_{raw_key}"
salt = secrets.token_hex(16)
key_hash = hashlib.sha256((full_key + salt).encode()).hexdigest()

# Only key_hash and salt stored in database
# Full key shown ONCE at generation

Multi-Tenant Isolation

Source: /ow-ai-backend/routes/authorization_routes.py:2297-2306 (SEC-020)

All API keys are scoped to an organization:

# Action submission automatically scopes to organization
action = AgentAction(
agent_id=data["agent_id"],
organization_id=org_id, # From authenticated API key
# ... other fields
)

# Users can ONLY access their organization's data

Audit Trail

Every API key operation is logged to immutable audit log:

Source: /ow-ai-backend/routes/api_key_routes.py:178-214

# Automatic audit logging
audit_log = ImmutableAuditLog(
user_id=user_id,
action="api_key_generated",
resource_type="api_key",
resource_id=key_id,
outcome="success",
metadata={
"key_prefix": key_prefix,
"key_name": key_name
}
)

Troubleshooting

Invalid API Key

Error: HTTP 401 - Authentication required

Solution: Verify your API key is correct and hasn't been revoked. Check with GET /api/keys/list.

Expired Key

Error: HTTP 403 - API key expired

Solution: Generate a new API key. Keys expire based on expires_in_days set during generation.

Wrong Organization

Error: HTTP 404 - Action not found

Solution: Verify you're using the correct API key for your organization. API keys are organization-scoped.

Rate Limited

Error: HTTP 429 - Rate limit exceeded

Solution: Check your rate limit configuration with GET /api/keys/{key_id}/usage. Default: 1000 requests/hour.

Rate Limit Implementation: /ow-ai-backend/routes/actions_v1_routes.py:316-346

Compliance

API key management meets enterprise security standards:

StandardRequirementImplementation
PCI-DSS 8.3.1Strong cryptographySHA-256 with random salt
HIPAA 164.312(d)Access controlsOrganization-scoped keys
SOC 2 CC6.1Audit trailImmutable audit logs
NIST 800-63BKey rotationConfigurable expiration

Source: /ow-ai-backend/routes/api_key_routes.py:1-50 (compliance documentation)

Next Steps