Skip to content

Authentication

Uranus supports multiple authentication methods to secure agent access.

Overview

Authentication layers:

  1. Cloudflare Access - Enterprise SSO integration
  2. Email-Based Access - Role-based agent permissions
  3. API Keys - Service integrations

Cloudflare Access

Setup

  1. Enable Cloudflare Access for your domain
  2. Configure identity provider (Okta, Google, Azure AD, etc.)
  3. Create an Access application for Uranus
  4. Set the audience tag in wrangler.toml:
toml
[vars]
CF_ACCESS_AUD = "your-access-audience-tag"

JWT Validation

The worker validates Cloudflare Access JWTs:

typescript
async function validateAccessJWT(request: Request, env: Env) {
  const jwt = request.headers.get('CF-Access-JWT-Assertion')

  if (!jwt) {
    return null
  }

  // Validate JWT against Cloudflare Access
  const payload = await verifyJWT(jwt, env.CF_ACCESS_AUD)

  return {
    email: payload.email,
    name: payload.name
  }
}

Access Policies

Configure Access policies:

  • Allow specific email domains
  • Require MFA
  • Set session duration
  • Define group-based access

Email-Based Access Control

Roles

RolePermissions
ownerFull access, can delete agent, manage admins
adminFull access, cannot delete agent
viewerRead-only access

Managing Access

Add Admin

bash
curl -X POST https://your-domain.com/api/agents/{id}/admins \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "role": "admin"
  }'

Remove Admin

bash
curl -X DELETE https://your-domain.com/api/agents/{id}/admins/{adminId}

List Admins

bash
curl https://your-domain.com/api/agents/{id}/admins

Access Check Flow

┌─────────────────┐
│  Incoming       │
│  Request        │
└────────┬────────┘


┌─────────────────┐     ┌─────────────────┐
│  CF Access      │────▶│  Extract Email  │
│  JWT Present?   │ Yes │  from JWT       │
└────────┬────────┘     └────────┬────────┘
         │ No                    │
         ▼                       ▼
┌─────────────────┐     ┌─────────────────┐
│  Use Default    │     │  Check Agent    │
│  Access         │     │  Access         │
└────────┬────────┘     └────────┬────────┘
         │                       │
         ▼                       ▼
┌─────────────────────────────────────────┐
│            Agent Access Check            │
│                                          │
│  1. Is agent 'default'? → Allow          │
│  2. Is user in agent_admins? → Allow     │
│  3. Otherwise → Deny                     │
└──────────────────────────────────────────┘

Default Agent

The default agent has special behavior:

  • Always accessible without authentication
  • Cannot be deleted
  • Used for initial setup
  • Good for public/demo access

Auth Context

Frontend

Use the AuthContext:

tsx
import { useAuth } from '@/contexts/AuthContext'

function ProfilePage() {
  const { user, isAuthenticated, logout } = useAuth()

  if (!isAuthenticated) {
    return <Navigate to="/login" />
  }

  return (
    <div>
      <p>Welcome, {user.email}</p>
      <button onClick={logout}>Logout</button>
    </div>
  )
}

Protected Routes

Wrap routes with ProtectedRoute:

tsx
<Route
  path="/settings"
  element={
    <ProtectedRoute>
      <SettingsPage />
    </ProtectedRoute>
  }
/>

Backend

Check authentication in API routes:

typescript
export async function onRequestGet(context: EventContext<Env, string, any>) {
  const { env, request, params } = context

  // Get current user
  const user = await getUser(request, env)

  // Check agent access
  const agentId = params.id
  const hasAccess = await checkAgentAccess(env.DB, agentId, user?.email)

  if (!hasAccess) {
    return new Response('Forbidden', { status: 403 })
  }

  // Continue with request...
}

Service Authentication

API Keys

Store API keys securely in agent settings:

json
{
  "openai_api_key": "sk-...",
  "anthropic_api_key": "sk-ant-...",
  "telegram_bot_token": "123456:ABC..."
}

Secure Storage

API keys are stored in the database:

  • Encrypted at rest (D1 managed)
  • Accessed only by the agent
  • Never exposed in API responses

Using API Keys

typescript
// Retrieve from settings
const settings = await getAgentSettings(db, agentId)
const apiKey = settings.openai_api_key

// Use in service call
const response = await fetch('https://api.openai.com/v1/chat/completions', {
  headers: {
    'Authorization': `Bearer ${apiKey}`
  }
})

Security Best Practices

1. Enable Cloudflare Access

Use Cloudflare Access for production:

  • SSO integration
  • MFA enforcement
  • Session management
  • Audit logging

2. Principle of Least Privilege

Assign minimal permissions:

  • Use viewer role for read-only users
  • Reserve owner for trusted admins
  • Regular access reviews

3. Secure API Keys

Protect sensitive credentials:

  • Never log API keys
  • Rotate keys regularly
  • Use environment-specific keys

4. Audit Access

Monitor authentication events:

  • Track login attempts
  • Review agent access changes
  • Alert on suspicious activity

5. HTTPS Only

Always use HTTPS:

  • Cloudflare provides automatic TLS
  • Never expose HTTP endpoints
  • Use secure WebSocket (wss://)

Troubleshooting

Access Denied Errors

  1. Check user email is in agent_admins
  2. Verify Cloudflare Access JWT is valid
  3. Confirm CF_ACCESS_AUD is correct
  4. Review Access policies

JWT Validation Failures

  1. Check token expiration
  2. Verify audience tag
  3. Confirm identity provider config
  4. Review Cloudflare Access logs

Role Issues

  1. Verify role in agent_admins table
  2. Check for duplicate entries
  3. Confirm role permissions

Released under the MIT License.