Files
attune/docs/TODO-execution-token-generation.md

303 lines
9.3 KiB
Markdown

# TODO: Execution-Scoped API Token Generation
**Priority:** High
**Status:** Not Started
**Related Work:** `work-summary/2026-02-07-env-var-standardization.md`
**Blocked By:** None
**Blocking:** Full API access from action executions
## Overview
Actions currently receive an empty `ATTUNE_API_TOKEN` environment variable. This TODO tracks the implementation of execution-scoped JWT token generation to enable actions to authenticate with the Attune API.
## Background
As of 2026-02-07, the environment variable standardization work updated the worker to provide standard environment variables to actions, including `ATTUNE_API_TOKEN`. However, token generation is not yet implemented - the variable is set to an empty string as a placeholder.
## Requirements
### Functional Requirements
1. **Token Generation**: Generate JWT tokens scoped to specific executions
2. **Token Claims**: Include execution-specific claims and permissions
3. **Token Lifecycle**: Tokens expire with execution or after timeout
4. **Security**: Tokens cannot access other executions or system resources
5. **Integration**: Seamlessly integrate into existing execution flow
### Non-Functional Requirements
1. **Performance**: Token generation should not significantly delay execution startup
2. **Security**: Follow JWT best practices and secure token scoping
3. **Consistency**: Match patterns from sensor token generation
4. **Testability**: Unit and integration tests for token generation and validation
## Design
### Token Claims Structure
```json
{
"sub": "execution:12345",
"identity_id": 42,
"execution_id": 12345,
"scopes": [
"execution:read:self",
"execution:create:child",
"secrets:read:owned"
],
"iat": 1738934400,
"exp": 1738938000,
"nbf": 1738934400
}
```
### Token Scopes
| Scope | Description | Use Case |
|-------|-------------|----------|
| `execution:read:self` | Read own execution data | Query execution status, retrieve parameters |
| `execution:create:child` | Create child executions | Workflow orchestration, sub-tasks |
| `secrets:read:owned` | Access secrets owned by execution identity | Retrieve API keys, credentials |
### Token Expiration
- **Default Expiration**: Execution timeout (from action metadata) or 5 minutes (300 seconds)
- **Maximum Expiration**: 1 hour (configurable)
- **Auto-Invalidation**: Token marked invalid when execution completes/fails/cancels
### Token Generation Flow
1. Executor receives execution request from queue
2. Executor loads action metadata (includes timeout)
3. Executor generates execution-scoped JWT token:
- Subject: `execution:{id}`
- Claims: execution ID, identity ID, scopes
- Expiration: now + timeout or max lifetime
4. Token added to environment variables (`ATTUNE_API_TOKEN`)
5. Action script uses token for API authentication
## Implementation Tasks
### Phase 1: Token Generation Service
- [ ] Create `TokenService` or add to existing auth service
- [ ] Implement `generate_execution_token(execution_id, identity_id, timeout)` method
- [ ] Use same JWT signing key as API service
- [ ] Add token generation to `ActionExecutor::prepare_execution_context()`
- [ ] Replace empty string with generated token
**Files to Modify:**
- `crates/common/src/auth.rs` (or create new token module)
- `crates/worker/src/executor.rs` (line ~220)
**Estimated Effort:** 4-6 hours
### Phase 2: Token Validation
- [ ] Update API auth middleware to recognize execution-scoped tokens
- [ ] Validate token scopes against requested resources
- [ ] Ensure execution tokens cannot access other executions
- [ ] Add scope checking to protected endpoints
**Files to Modify:**
- `crates/api/src/auth/middleware.rs`
- `crates/api/src/auth/jwt.rs`
**Estimated Effort:** 3-4 hours
### Phase 3: Token Lifecycle Management
- [ ] Track active execution tokens in memory or cache
- [ ] Invalidate tokens when execution completes
- [ ] Handle token refresh (if needed for long-running actions)
- [ ] Add cleanup for orphaned tokens
**Files to Modify:**
- `crates/worker/src/executor.rs`
- Consider adding token registry/cache
**Estimated Effort:** 2-3 hours
### Phase 4: Testing
- [ ] Unit tests for token generation
- [ ] Unit tests for token validation and scope checking
- [ ] Integration test: action calls API with generated token
- [ ] Integration test: verify token cannot access other executions
- [ ] Integration test: verify token expires appropriately
- [ ] Test child execution creation with token
**Files to Create:**
- `crates/worker/tests/token_generation_tests.rs`
- `crates/api/tests/execution_token_auth_tests.rs`
**Estimated Effort:** 4-5 hours
### Phase 5: Documentation
- [ ] Document token generation in worker architecture docs
- [ ] Update QUICKREF-execution-environment.md with token details
- [ ] Add security considerations to documentation
- [ ] Provide examples of actions using API with token
- [ ] Document troubleshooting for token-related issues
**Files to Update:**
- `docs/QUICKREF-execution-environment.md`
- `docs/architecture/worker-service.md`
- `docs/authentication/authentication.md`
- `packs/core/actions/README.md` (add API usage examples)
**Estimated Effort:** 2-3 hours
## Technical Details
### JWT Signing
Use the same JWT secret as the API service:
```rust
use jsonwebtoken::{encode, EncodingKey, Header};
let token = encode(
&Header::default(),
&claims,
&EncodingKey::from_secret(jwt_secret.as_bytes()),
)?;
```
### Token Structure Reference
Look at sensor token generation in `crates/sensor/src/api_client.rs` for patterns:
- Similar claims structure
- Similar expiration handling
- Can reuse token generation utilities
### Middleware Integration
Update `RequireAuth` extractor to handle execution-scoped tokens:
```rust
// Pseudo-code
match token_subject_type {
"user" => validate_user_token(token),
"service_account" => validate_service_token(token),
"execution" => validate_execution_token(token, execution_id_from_route),
}
```
### Scope Validation
Add scope checking helper:
```rust
fn require_scope(token: &Token, required_scope: &str) -> Result<()> {
if token.scopes.contains(&required_scope.to_string()) {
Ok(())
} else {
Err(Error::Forbidden("Insufficient scope"))
}
}
```
## Security Considerations
### Token Scoping
1. **Execution Isolation**: Token must only access its own execution
2. **No System Access**: Cannot modify system configuration
3. **Limited Secrets**: Only secrets owned by execution identity
4. **Time-Bounded**: Expires with execution or timeout
### Attack Vectors to Prevent
1. **Token Reuse**: Expired tokens must be rejected
2. **Cross-Execution Access**: Token for execution A cannot access execution B
3. **Privilege Escalation**: Cannot use token to gain admin access
4. **Token Leakage**: Never log full token value
### Validation Checklist
- [ ] Token signature verified
- [ ] Token not expired
- [ ] Execution ID matches token claims
- [ ] Required scopes present in token
- [ ] Identity owns requested resources
## Testing Strategy
### Unit Tests
```rust
#[test]
fn test_generate_execution_token() {
let token = generate_execution_token(12345, 42, 300).unwrap();
let claims = decode_token(&token).unwrap();
assert_eq!(claims.execution_id, 12345);
assert_eq!(claims.identity_id, 42);
assert!(claims.scopes.contains(&"execution:read:self".to_string()));
}
#[test]
fn test_token_cannot_access_other_execution() {
let token = generate_execution_token(12345, 42, 300).unwrap();
// Try to access execution 99999 with token for execution 12345
let result = api_client.get_execution(99999, &token).await;
assert!(result.is_err());
}
```
### Integration Tests
1. **Happy Path**: Action successfully calls API with token
2. **Scope Enforcement**: Action cannot perform unauthorized operations
3. **Token Expiration**: Expired token is rejected
4. **Child Execution**: Action can create child execution with token
## Dependencies
### Required Access
- JWT secret (same as API service)
- Access to execution data (for claims)
- Access to identity data (for ownership checks)
### Configuration
Add to worker config (or use existing values):
```yaml
security:
jwt_secret: "..." # Shared with API
execution_token_max_lifetime: 3600 # 1 hour
```
## Success Criteria
1. ✅ Actions receive valid JWT token in `ATTUNE_API_TOKEN`
2. ✅ Actions can authenticate with API using token
3. ✅ Token scopes are enforced correctly
4. ✅ Tokens cannot access other executions
5. ✅ Tokens expire appropriately
6. ✅ All tests pass
7. ✅ Documentation is complete and accurate
## References
- [Environment Variable Standardization](../work-summary/2026-02-07-env-var-standardization.md) - Background and context
- [QUICKREF: Execution Environment](./QUICKREF-execution-environment.md) - Token usage documentation
- [Worker Service Architecture](./architecture/worker-service.md) - Executor implementation details
- [Authentication Documentation](./authentication/authentication.md) - JWT patterns and security
- Sensor Token Generation: `crates/sensor/src/api_client.rs` - Reference implementation
## Estimated Total Effort
**Total:** 15-21 hours (approximately 2-3 days of focused work)
## Notes
- Consider reusing token generation utilities from API service
- Ensure consistency with sensor token generation patterns
- Document security model clearly for pack developers
- Add examples to core pack showing API usage from actions