Files
attune/docs/sensors/sensor-authentication-overview.md
2026-02-04 17:46:30 -06:00

12 KiB

Sensor Authentication Overview

Version: 1.0
Last Updated: 2025-01-27

Quick Summary

This document provides a quick overview of how sensors authenticate with Attune. For full details, see:

How It Works

  1. Admin creates sensor service account via API:

    POST /service-accounts
    {
      "name": "sensor:core.timer",
      "scope": "sensor",
      "ttl_days": 90
    }
    
  2. Admin receives long-lived token (shown only once):

    {
      "identity_id": 123,
      "token": "eyJhbGci...",
      "expires_at": "2025-04-27T12:34:56Z"
    }
    
  3. Token is deployed with sensor via environment variable:

    export ATTUNE_API_TOKEN="eyJhbGci..."
    export ATTUNE_API_URL="http://localhost:8080"
    export ATTUNE_SENSOR_REF="core.timer"
    ./attune-sensor
    
  4. Sensor uses token for all API calls:

    • Fetch active rules: GET /rules?trigger_type=core.timer
    • Create events: POST /events
    • Fetch trigger metadata: GET /triggers/{ref}

Token Properties

Property Value
Type JWT (stateless)
Lifetime 24-72 hours (auto-expires, REQUIRED)
Scope sensor
Permissions Create events, read rules/triggers (restricted to declared trigger types)
Revocable Yes (via /service-accounts/{id} DELETE)
Rotation Manual every 24-72 hours (sensor restart required)
Expiration All tokens MUST have exp claim to prevent revocation table bloat

Security Best Practices

DO:

  • Store tokens in environment variables or secure config management
  • Use HTTPS for API calls in production
  • Redact tokens in logs (show only last 4 characters)
  • Revoke tokens immediately if compromised
  • Use separate tokens for each sensor type
  • Set TTL to 24-72 hours for sensors (requires periodic rotation)
  • Monitor token expiration and rotate before expiry

DON'T:

  • Commit tokens to version control
  • Log full token values
  • Share tokens between sensors
  • Send tokens over unencrypted connections
  • Store tokens on disk unencrypted
  • Pass tokens in URL query parameters

Configuration Methods

export ATTUNE_API_URL="http://localhost:8080"
export ATTUNE_API_TOKEN="eyJhbGci..."
export ATTUNE_SENSOR_REF="core.timer"
export ATTUNE_MQ_URL="amqp://localhost:5672"

./attune-sensor

Method 2: stdin JSON

echo '{
  "api_url": "http://localhost:8080",
  "api_token": "eyJhbGci...",
  "sensor_ref": "core.timer",
  "mq_url": "amqp://localhost:5672"
}' | ./attune-sensor

Method 3: Configuration File + Environment Override

# sensor.yaml
api_url: http://localhost:8080
sensor_ref: core.timer
mq_url: amqp://localhost:5672
# Token provided via environment for security
export ATTUNE_API_TOKEN="eyJhbGci..."
./attune-sensor --config sensor.yaml

Token Lifecycle

┌─────────────────────────────────────────────────────────────┐
│ 1. Admin creates service account                            │
│    POST /service-accounts                                    │
└─────────────────┬───────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. API generates JWT token                                  │
│    - Sets scope: "sensor"                                    │
│    - Sets expiration (e.g., 90 days)                         │
│    - Includes identity_id, trigger_types                     │
└─────────────────┬───────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. Token stored securely by admin                           │
│    - Environment variable                                    │
│    - Secret management system (Vault, k8s secrets)           │
└─────────────────┬───────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│ 4. Sensor starts and reads token                            │
│    - From ATTUNE_API_TOKEN env var                           │
│    - Or from stdin JSON                                      │
└─────────────────┬───────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│ 5. Sensor makes API calls with token                        │
│    Authorization: Bearer eyJhbGci...                         │
└─────────────────┬───────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│ 6. API validates token on each request                      │
│    - Verify JWT signature                                    │
│    - Check expiration                                        │
│    - Check revocation list                                   │
│    - Verify scope matches endpoint requirements              │
└─────────────────┬───────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│ 7. Token eventually expires or is revoked                   │
│    - Auto-expires after TTL                                  │
│    - Or admin revokes: DELETE /service-accounts/{id}         │
└─────────────────────────────────────────────────────────────┘

JWT Token Structure

{
  "sub": "sensor:core.timer",
  "jti": "abc123...",
  "iat": 1706356496,
  "exp": 1714132496,
  "identity_id": 123,
  "identity_type": "service_account",
  "scope": "sensor",
  "metadata": {
    "trigger_types": ["core.timer"]
  }
}

Permissions by Scope

Scope Create Events Read Rules Read Triggers Read Keys Update Execution
sensor (restricted)*
action_execution
webhook
user
admin

* Sensor tokens can only create events for trigger types declared in their token's metadata.trigger_types. The API enforces this restriction and returns 403 Forbidden for unauthorized trigger types.

Example: Creating a Sensor Token

# 1. Create service account (admin only)
curl -X POST http://localhost:8080/service-accounts \
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "sensor:core.timer",
    "scope": "sensor",
    "description": "Timer sensor for interval-based triggers",
    "ttl_hours": 72,
    "metadata": {
      "trigger_types": ["core.timer"]
    }
  }'

# Note: This token can ONLY create events for "core.timer" trigger type.
# Attempting to create events for other trigger types will fail with 403 Forbidden.

# Response (SAVE THE TOKEN - shown only once):
{
  "identity_id": 123,
  "name": "sensor:core.timer",
  "scope": "sensor",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzZW5zb3I6Y29yZS50aW1lciIsImp0aSI6ImFiYzEyMyIsImlhdCI6MTcwNjM1NjQ5NiwiZXhwIjoxNzA2NjE1Njk2LCJpZGVudGl0eV9pZCI6MTIzLCJpZGVudGl0eV90eXBlIjoic2VydmljZV9hY2NvdW50Iiwic2NvcGUiOiJzZW5zb3IiLCJtZXRhZGF0YSI6eyJ0cmlnZ2VyX3R5cGVzIjpbImNvcmUudGltZXIiXX19.signature",
  "expires_at": "2025-01-30T12:34:56Z"
}

# 2. Deploy token with sensor
export ATTUNE_API_TOKEN="eyJhbGci..."
export ATTUNE_API_URL="http://localhost:8080"
export ATTUNE_SENSOR_REF="core.timer"
export ATTUNE_MQ_URL="amqp://localhost:5672"

./attune-sensor

# 3. Rotate token before expiration (every 24-72 hours)
# - Create new service account
# - Update ATTUNE_API_TOKEN
# - Restart sensor

Troubleshooting

Token Validation Errors

Error: "Token expired"

  • Token has exceeded its TTL
  • Solution: Create a new service account and token

Error: "Token revoked"

  • Token was manually revoked by admin
  • Solution: Create a new service account and token

Error: "Invalid signature"

  • JWT_SECRET mismatch between token creation and validation
  • Solution: Ensure all services use the same JWT_SECRET

Error: "Insufficient permissions"

  • Token scope doesn't match required endpoint permissions
  • For sensors: Attempting to create event for trigger type not in metadata.trigger_types
  • Solution: Create token with correct scope and trigger types (e.g., "sensor" scope with ["core.timer"])

Common Mistakes

  1. Using user token for sensor: User tokens have different scope, create a service account instead
  2. Hardcoding token in code: Use environment variables or config management
  3. Sharing token between sensors: Each sensor should have its own token
  4. Not revoking compromised tokens: Use DELETE /service-accounts/{id} immediately

Implementation Status

  • Database schema for service accounts (identity_type column)
  • Database schema for token revocation (token_revocation table with token_exp column)
  • API endpoint: POST /service-accounts (with TTL parameter)
  • API endpoint: GET /service-accounts
  • API endpoint: DELETE /service-accounts/{id}
  • Middleware for token validation (check expiration)
  • Middleware for revocation checking (skip expired tokens)
  • Executor creates execution tokens (TTL = action timeout)
  • Worker passes execution tokens to actions
  • CLI commands for service account management
  • Sensor accepts and uses tokens
  • Cleanup job for expired token revocations (hourly cron)
  • Monitoring alerts for token expiration (6 hours before)

Next Steps

  1. Implement database migrations for service accounts
  2. Add service account CRUD endpoints to API (with TTL parameters)
  3. Update sensor to accept and use API tokens
  4. Add token creation to executor for action executions (TTL = action timeout)
  5. Implement cleanup job for expired token revocations
  6. Document token rotation procedures (manual every 24-72 hours)
  7. Add monitoring for token expiration warnings (alert 6 hours before)
  8. Add graceful handling of token expiration in sensors