Files
StarPunk/docs/architecture/indieauth-removal-plan.md
Phil Skentelbery a3bac86647 feat: Complete IndieAuth server removal (Phases 2-4)
Completed all remaining phases of ADR-030 IndieAuth provider removal.
StarPunk no longer acts as an authorization server - all IndieAuth
operations delegated to external providers.

Phase 2 - Remove Token Issuance:
- Deleted /auth/token endpoint
- Removed token_endpoint() function from routes/auth.py
- Deleted tests/test_routes_token.py

Phase 3 - Remove Token Storage:
- Deleted starpunk/tokens.py module entirely
- Created migration 004 to drop tokens and authorization_codes tables
- Deleted tests/test_tokens.py
- Removed all internal token CRUD operations

Phase 4 - External Token Verification:
- Created starpunk/auth_external.py module
- Implemented verify_external_token() for external IndieAuth providers
- Updated Micropub endpoint to use external verification
- Added TOKEN_ENDPOINT configuration
- Updated all Micropub tests to mock external verification
- HTTP timeout protection (5s) for external requests

Additional Changes:
- Created migration 003 to remove code_verifier from auth_state
- Fixed 5 migration tests that referenced obsolete code_verifier column
- Updated 11 Micropub tests for external verification
- Fixed test fixture and app context issues
- All 501 tests passing

Breaking Changes:
- Micropub clients must use external IndieAuth providers
- TOKEN_ENDPOINT configuration now required
- Existing internal tokens invalid (tables dropped)

Migration Impact:
- Simpler codebase: -500 lines of code
- Fewer database tables: -2 tables (tokens, authorization_codes)
- More secure: External providers handle token security
- More maintainable: Less authentication code to maintain

Standards Compliance:
- W3C IndieAuth specification
- OAuth 2.0 Bearer token authentication
- IndieWeb principle: delegate to external services

Related:
- ADR-030: IndieAuth Provider Removal Strategy
- ADR-050: Remove Custom IndieAuth Server
- Migration 003: Remove code_verifier from auth_state
- Migration 004: Drop tokens and authorization_codes tables

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-24 17:23:46 -07:00

13 KiB

IndieAuth Server Removal Plan

Executive Summary

This document provides a detailed, file-by-file plan for removing the custom IndieAuth authorization server from StarPunk and replacing it with external provider integration.

Files to Delete (Complete Removal)

Python Modules

/home/phil/Projects/starpunk/starpunk/tokens.py
  - Entire file (token generation, validation, storage)
  - ~300 lines of code

/home/phil/Projects/starpunk/tests/test_tokens.py
  - All token-related unit tests
  - ~200 lines of test code

/home/phil/Projects/starpunk/tests/test_routes_authorization.py
  - Authorization endpoint tests
  - ~150 lines of test code

/home/phil/Projects/starpunk/tests/test_routes_token.py
  - Token endpoint tests
  - ~150 lines of test code

/home/phil/Projects/starpunk/tests/test_auth_pkce.py
  - PKCE implementation tests
  - ~100 lines of test code

Templates

/home/phil/Projects/starpunk/templates/auth/authorize.html
  - Authorization consent UI
  - ~100 lines of HTML/Jinja2

Database Migrations

/home/phil/Projects/starpunk/migrations/002_secure_tokens_and_authorization_codes.sql
  - Table creation for authorization_codes and tokens
  - ~80 lines of SQL

Files to Modify

1. /home/phil/Projects/starpunk/starpunk/routes/auth.py

Remove:

  • Import of tokens module functions
  • authorization_endpoint() function (~150 lines)
  • token_endpoint() function (~100 lines)
  • PKCE-related helper functions

Keep:

  • Blueprint definition
  • Admin login routes
  • IndieLogin.com integration
  • Session management

New Structure:

"""
Authentication routes for StarPunk

Handles IndieLogin authentication flow for admin access.
External IndieAuth providers handle Micropub authentication.
"""

from flask import Blueprint, flash, redirect, render_template, session, url_for
from starpunk.auth import (
    handle_callback,
    initiate_login,
    require_auth,
    verify_session,
)

bp = Blueprint("auth", __name__, url_prefix="/auth")

@bp.route("/login", methods=["GET"])
def login_form():
    # Keep existing admin login

@bp.route("/callback")
def callback():
    # Keep existing callback

@bp.route("/logout")
def logout():
    # Keep existing logout

# DELETE: authorization_endpoint()
# DELETE: token_endpoint()

2. /home/phil/Projects/starpunk/starpunk/auth.py

Remove:

  • PKCE code verifier generation
  • PKCE challenge calculation
  • Authorization state management for codes

Keep:

  • Admin session management
  • IndieLogin.com integration
  • CSRF protection

3. /home/phil/Projects/starpunk/starpunk/micropub.py

Current Token Verification:

from starpunk.tokens import verify_token

def handle_request():
    token_info = verify_token(bearer_token)
    if not token_info:
        return error_response("forbidden")

New Token Verification:

import httpx
from flask import current_app

def verify_token(bearer_token: str) -> Optional[Dict[str, Any]]:
    """
    Verify token with external token endpoint

    Uses the configured TOKEN_ENDPOINT to validate tokens.
    Caches successful validations for 5 minutes.
    """
    # Check cache first
    cached = get_cached_token(bearer_token)
    if cached:
        return cached

    # Verify with external endpoint
    token_endpoint = current_app.config.get(
        'TOKEN_ENDPOINT',
        'https://tokens.indieauth.com/token'
    )

    try:
        response = httpx.get(
            token_endpoint,
            headers={'Authorization': f'Bearer {bearer_token}'},
            timeout=5.0
        )

        if response.status_code != 200:
            return None

        data = response.json()

        # Verify it's for our user
        if data.get('me') != current_app.config['ADMIN_ME']:
            return None

        # Verify scope
        scope = data.get('scope', '')
        if 'create' not in scope.split():
            return None

        # Cache for 5 minutes
        cache_token(bearer_token, data, ttl=300)

        return data

    except Exception as e:
        current_app.logger.error(f"Token verification failed: {e}")
        return None

4. /home/phil/Projects/starpunk/starpunk/config.py

Add:

# External IndieAuth Configuration
TOKEN_ENDPOINT = os.getenv(
    'TOKEN_ENDPOINT',
    'https://tokens.indieauth.com/token'
)

# Remove internal auth endpoints
# DELETE: AUTHORIZATION_ENDPOINT
# DELETE: TOKEN_ISSUER

5. /home/phil/Projects/starpunk/templates/base.html

Add to <head> section:

<!-- IndieAuth Discovery -->
<link rel="authorization_endpoint" href="https://indieauth.com/auth">
<link rel="token_endpoint" href="{{ config.TOKEN_ENDPOINT }}">
<link rel="micropub" href="{{ url_for('micropub.micropub_endpoint', _external=True) }}">

6. /home/phil/Projects/starpunk/tests/test_micropub.py

Update token verification mocking:

@patch('starpunk.micropub.httpx.get')
def test_micropub_with_valid_token(mock_get):
    """Test Micropub with valid external token"""
    # Mock external token verification
    mock_get.return_value.status_code = 200
    mock_get.return_value.json.return_value = {
        'me': 'https://example.com',
        'client_id': 'https://quill.p3k.io',
        'scope': 'create update'
    }

    # Test Micropub request
    response = client.post(
        '/micropub',
        headers={'Authorization': 'Bearer test-token'},
        json={'type': ['h-entry'], 'properties': {'content': ['Test']}}
    )

    assert response.status_code == 201

Database Migration

Create Migration File

/home/phil/Projects/starpunk/migrations/003_remove_indieauth_server.sql:

-- Migration: Remove IndieAuth Server Tables
-- Description: Remove authorization_codes and tokens tables as we're using external providers
-- Date: 2025-11-24

-- Drop tokens table (depends on authorization_codes)
DROP TABLE IF EXISTS tokens;

-- Drop authorization_codes table
DROP TABLE IF EXISTS authorization_codes;

-- Remove any indexes
DROP INDEX IF EXISTS idx_tokens_hash;
DROP INDEX IF EXISTS idx_tokens_user_id;
DROP INDEX IF EXISTS idx_auth_codes_code;
DROP INDEX IF EXISTS idx_auth_codes_user_id;

-- Update schema version
UPDATE schema_version SET version = 3 WHERE id = 1;

Configuration Changes

Environment Variables

Remove from .env:

# DELETE THESE
AUTHORIZATION_ENDPOINT=/auth/authorization
TOKEN_ENDPOINT=/auth/token
TOKEN_ISSUER=https://starpunk.example.com

Add to .env:

# External IndieAuth Provider
TOKEN_ENDPOINT=https://tokens.indieauth.com/token
ADMIN_ME=https://your-domain.com

Docker Compose

Update docker-compose.yml environment section:

environment:
  - TOKEN_ENDPOINT=https://tokens.indieauth.com/token
  - ADMIN_ME=${ADMIN_ME}
  # Remove: AUTHORIZATION_ENDPOINT
  # Remove: TOKEN_ENDPOINT (internal)

Import Cleanup

Files with Import Changes

  1. Main app (/home/phil/Projects/starpunk/starpunk/__init__.py):

    • Remove: from starpunk import tokens
    • Remove: Registration of token-related error handlers
  2. Routes init (/home/phil/Projects/starpunk/starpunk/routes/__init__.py):

    • No changes needed (auth blueprint still exists)
  3. Test fixtures (/home/phil/Projects/starpunk/tests/conftest.py):

    • Remove: Token creation fixtures
    • Remove: Authorization code fixtures

Error Handling Updates

Remove Custom Exceptions

From various files, remove:

- InvalidAuthorizationCodeError
- ExpiredAuthorizationCodeError
- InvalidTokenError
- ExpiredTokenError
- InsufficientScopeError

Update Error Responses

In Micropub, simplify to:

if not token_info:
    return error_response("forbidden", "Invalid or expired token")

Testing Updates

Test Coverage Impact

Before Removal:

  • ~20 test files
  • ~1500 lines of test code
  • Coverage: 95%

After Removal:

  • ~15 test files
  • ~1000 lines of test code
  • Expected coverage: 93%

New Test Requirements

  1. Mock External Verification:

    @pytest.fixture
    def mock_token_endpoint():
        with patch('starpunk.micropub.httpx.get') as mock:
            yield mock
    
  2. Test Scenarios:

    • Valid token from external provider
    • Invalid token (404 from provider)
    • Wrong user (me doesn't match)
    • Insufficient scope
    • Network timeout
    • Provider unavailable

Performance Considerations

Token Verification Caching

Implement simple TTL cache:

from functools import lru_cache
from time import time

token_cache = {}  # {token_hash: (data, expiry)}

def cache_token(token: str, data: dict, ttl: int = 300):
    token_hash = hashlib.sha256(token.encode()).hexdigest()
    token_cache[token_hash] = (data, time() + ttl)

def get_cached_token(token: str) -> Optional[dict]:
    token_hash = hashlib.sha256(token.encode()).hexdigest()
    if token_hash in token_cache:
        data, expiry = token_cache[token_hash]
        if time() < expiry:
            return data
        del token_cache[token_hash]
    return None

Expected Latencies

  • Without cache: 200-500ms per request (external API call)
  • With cache: <1ms for cached tokens
  • Cache hit rate: ~95% for active sessions

Documentation Updates

Files to Update

  1. README.md:

    • Remove references to built-in authorization
    • Add external provider setup instructions
  2. Architecture Overview (/home/phil/Projects/starpunk/docs/architecture/overview.md):

    • Update component diagram
    • Remove authorization server component
    • Clarify Micropub-only role
  3. API Documentation (/home/phil/Projects/starpunk/docs/api/):

    • Remove /auth/authorization endpoint docs
    • Remove /auth/token endpoint docs
    • Update Micropub authentication section
  4. Deployment Guide (/home/phil/Projects/starpunk/docs/deployment/):

    • Update environment variable list
    • Add external provider configuration

Rollback Plan

Emergency Rollback Script

Create /home/phil/Projects/starpunk/scripts/rollback-auth.sh:

#!/bin/bash
# Emergency rollback for IndieAuth removal

echo "Rolling back IndieAuth removal..."

# Restore from git
git revert HEAD~5..HEAD

# Restore database
psql $DATABASE_URL < migrations/002_secure_tokens_and_authorization_codes.sql

# Restore config
cp .env.backup .env

# Restart service
docker-compose restart

echo "Rollback complete"

Verification After Rollback

  1. Check endpoints respond:

    curl -I https://starpunk.example.com/auth/authorization
    curl -I https://starpunk.example.com/auth/token
    
  2. Run test suite:

    pytest tests/test_auth.py
    pytest tests/test_tokens.py
    
  3. Verify database tables:

    SELECT COUNT(*) FROM authorization_codes;
    SELECT COUNT(*) FROM tokens;
    

Risk Assessment

High Risk Areas

  1. Breaking existing tokens: All existing tokens become invalid
  2. External dependency: Reliance on external service availability
  3. Configuration errors: Users may misconfigure endpoints

Mitigation Strategies

  1. Clear communication: Announce breaking change prominently
  2. Graceful degradation: Cache tokens, handle timeouts
  3. Validation tools: Provide config validation script

Success Criteria

Technical Criteria

  • All listed files deleted
  • All imports cleaned up
  • Tests pass with >90% coverage
  • No references to internal auth in codebase
  • External verification working

Functional Criteria

  • Admin can log in
  • Micropub accepts valid tokens
  • Micropub rejects invalid tokens
  • Discovery links present
  • Documentation updated

Performance Criteria

  • Token verification <500ms
  • Cache hit rate >90%
  • No memory leaks from cache

Timeline

Day 1: Removal Phase

  • Hour 1-2: Remove authorization endpoint
  • Hour 3-4: Remove token endpoint
  • Hour 5-6: Delete token module
  • Hour 7-8: Update tests

Day 2: Integration Phase

  • Hour 1-2: Implement external verification
  • Hour 3-4: Add caching layer
  • Hour 5-6: Update configuration
  • Hour 7-8: Test with real providers

Day 3: Documentation Phase

  • Hour 1-2: Update technical docs
  • Hour 3-4: Create user guides
  • Hour 5-6: Update changelog
  • Hour 7-8: Final testing

Appendix: File Size Impact

Before Removal

starpunk/
  tokens.py: 8.2 KB
  routes/auth.py: 15.3 KB
  templates/auth/: 2.8 KB
tests/
  test_tokens.py: 6.1 KB
  test_routes_*.py: 12.4 KB
Total: ~45 KB

After Removal

starpunk/
  routes/auth.py: 5.1 KB (10.2 KB removed)
  micropub.py: +1.5 KB (verification)
tests/
  test_micropub.py: +0.8 KB (mocks)
Total removed: ~40 KB
Net reduction: ~38.5 KB

Document Version: 1.0 Created: 2025-11-24 Author: StarPunk Architecture Team