# ADR-030: External Token Verification Architecture

## Status
Accepted

## Context

Following the decision in ADR-021 to use external IndieAuth providers, we need to define the architecture for token verification. Several critical questions arose during implementation planning:

1. How should we handle the existing database migration that creates token tables?
2. What caching strategy should we use for token verification?
3. How should we handle network errors when contacting external providers?
4. What are the security implications of caching tokens?

## Decision

### 1. Database Migration Strategy

**Keep migration 002 but document its future purpose.**

The migration creates `tokens` and `authorization_codes` tables that are not used in V1 but will be needed if V2 adds an internal provider option. Rather than removing and later re-adding these tables, we keep them empty in V1.

**Rationale**:
- Empty tables have zero performance impact
- Avoids complex migration rollback/recreation cycles
- Provides clear upgrade path to V2
- Follows principle of forward compatibility

### 2. Token Caching Architecture

**Implement a configurable memory cache with 5-minute default TTL.**

```python
class TokenCache:
    """Simple time-based token cache"""
    def __init__(self, ttl=300, enabled=True):
        self.ttl = ttl
        self.enabled = enabled
        self.cache = {}  # token_hash -> (info, expiry)
```

**Configuration**:
```ini
MICROPUB_TOKEN_CACHE_ENABLED=true  # Can disable for high security
MICROPUB_TOKEN_CACHE_TTL=300       # 5 minutes default
```

**Security Measures**:
- Store SHA256 hash of token, never plain text
- Memory-only storage (no persistence)
- Short TTL to limit revocation delay
- Option to disable entirely

### 3. Network Error Handling

**Implement clear error messages with appropriate HTTP status codes.**

| Scenario | HTTP Status | User Message |
|----------|------------|--------------|
| Auth server timeout | 503 | "Authorization server is unreachable" |
| Invalid token | 403 | "Access token is invalid or expired" |
| Network error | 503 | "Cannot connect to authorization server" |
| No token provided | 401 | "No access token provided" |

**Implementation**:
```python
try:
    response = httpx.get(endpoint, timeout=5.0)
except httpx.TimeoutError:
    raise TokenEndpointError("Authorization server is unreachable")
```

### 4. Endpoint Discovery

**Implement full IndieAuth spec discovery with fallbacks.**

Priority order:
1. HTTP Link header (highest priority)
2. HTML link elements
3. IndieAuth metadata endpoint

This ensures compatibility with all IndieAuth providers while following the specification exactly.

## Rationale

### Why Cache Tokens?

**Performance**:
- Reduces latency for Micropub posts (5ms vs 500ms)
- Reduces load on external authorization servers
- Improves user experience for rapid posting

**Trade-offs Accepted**:
- 5-minute revocation delay is acceptable for most use cases
- Can disable cache for high-security requirements
- Cache is memory-only, cleared on restart

### Why Keep Empty Tables?

**Simplicity**:
- Simpler than conditional migrations
- Cleaner upgrade path to V2
- No production impact (tables unused)
- Avoids migration complexity

**Forward Compatibility**:
- V2 might add internal provider
- Tables already have correct schema
- Migration already tested and working

### Why External-Only Verification?

**Alignment with Principles**:
- StarPunk is a Micropub server, not an auth server
- Users control their own identity infrastructure
- Reduces code complexity significantly
- Follows IndieWeb separation of concerns

## Consequences

### Positive

- **Simplicity**: No complex OAuth flows to implement
- **Security**: No tokens stored in database
- **Performance**: Cache provides fast token validation
- **Flexibility**: Users choose their auth providers
- **Compliance**: Full IndieAuth spec compliance

### Negative

- **Dependency**: Requires external auth server availability
- **Latency**: Network call for uncached tokens (mitigated by cache)
- **Revocation Delay**: Up to 5 minutes for cached tokens (configurable)

### Neutral

- **Database**: Unused tables in V1 (no impact, future-ready)
- **Configuration**: Requires ADMIN_ME setting (one-time setup)
- **Documentation**: Must explain external provider setup

## Implementation Details

### Token Verification Flow

```
1. Extract Bearer token from Authorization header
2. Check cache for valid cached result
3. If not cached:
   a. Discover token endpoint from ADMIN_ME URL
   b. Verify token with external endpoint
   c. Cache result if valid
4. Validate response:
   a. 'me' field matches ADMIN_ME
   b. 'scope' includes 'create'
5. Return validation result
```

### Security Checklist

- [ ] Never log tokens in plain text
- [ ] Use HTTPS for all token verification
- [ ] Implement timeout on HTTP requests
- [ ] Hash tokens before caching
- [ ] Validate SSL certificates
- [ ] Clear cache on configuration changes

### Performance Targets

- Cached token verification: < 10ms
- Uncached token verification: < 500ms
- Endpoint discovery: < 1000ms (cached after first)
- Cache memory usage: < 10MB for 1000 tokens

## Alternatives Considered

### Alternative 1: No Token Cache

**Pros**: Immediate revocation, simpler code
**Cons**: High latency (500ms per request), load on auth servers
**Verdict**: Rejected - poor user experience

### Alternative 2: Database Token Cache

**Pros**: Persistent cache, survives restarts
**Cons**: Complex invalidation, security concerns
**Verdict**: Rejected - unnecessary complexity

### Alternative 3: Redis Token Cache

**Pros**: Distributed cache, proven solution
**Cons**: Additional dependency, deployment complexity
**Verdict**: Rejected - violates simplicity principle

### Alternative 4: Remove Migration 002

**Pros**: Cleaner V1 codebase
**Cons**: Complex V2 upgrade, breaks existing databases
**Verdict**: Rejected - creates future problems

## Migration Impact

### For Existing Installations
- No database changes needed
- Add ADMIN_ME configuration
- Token verification switches to external

### For New Installations
- Clean V1 implementation
- Empty future-use tables
- Simple configuration

## Security Considerations

### Token Revocation Delay
- Cached tokens remain valid for TTL duration
- Maximum exposure: 5 minutes default
- Can disable cache for immediate revocation
- Document delay in security guide

### Network Security
- Always use HTTPS for token verification
- Validate SSL certificates
- Implement request timeouts
- Handle network errors gracefully

### Cache Security
- SHA256 hash tokens before storage
- Memory-only cache (no disk persistence)
- Clear cache on shutdown
- Limit cache size to prevent DoS

## References

- [IndieAuth Spec Section 6.3](https://www.w3.org/TR/indieauth/#token-verification) - Token verification
- [OAuth 2.0 Bearer Token](https://tools.ietf.org/html/rfc6750) - Bearer token usage
- [ADR-021](./ADR-021-indieauth-provider-strategy.md) - Provider strategy decision
- [ADR-029](./ADR-029-micropub-indieauth-integration.md) - Integration strategy

## Related Decisions

- ADR-021: IndieAuth Provider Strategy
- ADR-029: Micropub IndieAuth Integration Strategy
- ADR-005: IndieLogin Authentication
- ADR-010: Authentication Module Design

---

**Document Version**: 1.0
**Created**: 2024-11-24
**Author**: StarPunk Architecture Team
**Status**: Accepted