Files
StarPunk/docs/decisions/ADR-010-authentication-module-design.md
Phil Skentelbery d4f1bfb198 feat: Implement Phase 3 authentication module with IndieLogin support
Implement complete authentication system following ADR-010 and Phase 3 design specs.
This is a MINOR version increment (0.3.0 -> 0.4.0) as it adds new functionality.

Authentication Features:
- IndieLogin authentication flow via indielogin.com
- Secure session management with SHA-256 token hashing
- CSRF protection with single-use state tokens
- Session lifecycle (create, verify, destroy)
- require_auth decorator for protected routes
- Automatic cleanup of expired sessions
- IP address and user agent tracking

Security Measures:
- Cryptographically secure token generation (secrets module)
- Token hashing for storage (never plaintext)
- SQL injection prevention (prepared statements)
- Single-use CSRF state tokens
- 30-day session expiry with activity refresh
- Comprehensive security logging

Implementation Details:
- starpunk/auth.py: 406 lines, 6 core functions, 4 helpers, 4 exceptions
- tests/test_auth.py: 648 lines, 37 tests, 96% coverage
- Database schema updates for sessions and auth_state tables
- URL validation utility added to utils.py

Test Coverage:
- 37 authentication tests
- 96% code coverage (exceeds 90% target)
- All security features tested
- Edge cases and error paths covered

Documentation:
- Implementation report in docs/reports/
- Updated CHANGELOG.md with detailed changes
- Version incremented to 0.4.0
- ADR-010 and Phase 3 design docs included

Follows project standards:
- Black code formatting (88 char lines)
- Flake8 linting (no errors)
- Python coding standards
- Type hints on all functions
- Comprehensive docstrings

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 20:35:36 -07:00

6.9 KiB

ADR-010: Authentication Module Design

Status

Accepted

Context

With the core utilities and notes management complete, StarPunk needs an authentication system for the admin interface. ADR-005 already decided on using IndieLogin.com as the authentication provider. This ADR defines the specific module architecture and implementation approach for the authentication system.

The authentication module must:

  1. Integrate with IndieLogin.com for identity verification
  2. Manage secure sessions for authenticated users
  3. Protect admin routes from unauthorized access
  4. Handle CSRF protection for the authentication flow
  5. Support single-user authorization (V1 requirement)

Decision

Module Architecture

Single Module Approach: Implement all authentication logic in a single starpunk/auth.py module.

Rationale:

  • Authentication is ~200-300 lines of focused code
  • Single responsibility: manage authentication and sessions
  • No need to split into multiple files for V1
  • Easier to understand and maintain

Session Storage Strategy

Database-Backed Sessions with these characteristics:

  • Session tokens stored in SQLite (not Redis/Memcached)
  • Cryptographically secure token generation (32 bytes)
  • 30-day expiry with activity-based refresh
  • Automatic cleanup of expired sessions

Schema:

CREATE TABLE sessions (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    session_token TEXT UNIQUE NOT NULL,
    me TEXT NOT NULL,
    created_at TIMESTAMP NOT NULL,
    expires_at TIMESTAMP NOT NULL,
    last_used_at TIMESTAMP,
    user_agent TEXT,
    ip_address TEXT
);

CREATE TABLE auth_state (
    state TEXT PRIMARY KEY,
    created_at TIMESTAMP NOT NULL,
    expires_at TIMESTAMP NOT NULL,
    redirect_uri TEXT
);

Security Architecture

Token Security:

  • Use secrets.token_urlsafe(32) for session tokens
  • Store token hash in database (SHA-256), not plaintext
  • HttpOnly, Secure, SameSite=Lax cookies
  • No JavaScript access to session tokens

CSRF Protection:

  • Generate state token for each auth request
  • Store in auth_state table with 5-minute expiry
  • Verify state on callback before code exchange
  • Single-use tokens (delete after verification)

Authorization Model:

  • Single admin user (configured via ADMIN_ME environment variable)
  • Strict equality check (no substring matching)
  • Reject any user whose me URL doesn't exactly match ADMIN_ME
  • Log all authentication attempts for security audit

Function Architecture

Core Functions (6 total):

  1. initiate_login(me_url: str) -> str - Start IndieLogin flow
  2. handle_callback(code: str, state: str) -> Optional[str] - Process callback
  3. create_session(me: str) -> str - Create new session
  4. verify_session(token: str) -> Optional[Dict] - Validate session
  5. destroy_session(token: str) -> None - Logout
  6. require_auth(f) - Decorator for protected routes

Helper Functions (4 total):

  1. _generate_state_token() -> str - CSRF token generation
  2. _verify_state_token(state: str) -> bool - CSRF validation
  3. _cleanup_expired_sessions() -> None - Maintenance
  4. _hash_token(token: str) -> str - Security hashing

Error Handling Strategy

Custom Exceptions:

class AuthError(Exception): pass
class InvalidStateError(AuthError): pass
class UnauthorizedError(AuthError): pass
class IndieLoginError(AuthError): pass

Error Responses:

  • Invalid state: 400 Bad Request with clear error
  • Unauthorized user: 403 Forbidden
  • IndieLogin failure: 502 Bad Gateway
  • Session expired: Redirect to login

Integration Points

Flask Integration:

  • Use Flask's g object for request-scoped user data
  • Integrate with Flask's session for flash messages
  • Use Flask's before_request for session refresh
  • Leverage Flask's error handlers for auth errors

Database Integration:

  • Use existing get_db() connection management
  • Transactions for session creation/deletion
  • Prepared statements to prevent SQL injection

Rationale

Why Database Sessions Over Flask-Session?

Database sessions chosen:

  • Already have SQLite, no new dependencies
  • Persistent across server restarts
  • Can query active sessions
  • Supports session invalidation
  • Better security (server-side storage)

Flask-Session rejected:

  • Adds Redis/Memcached dependency
  • Overkill for single-user system
  • More complex deployment

Why Token Hashing?

Even though we're single-user, proper security practices:

  • Defense in depth principle
  • Prevents token leakage if database exposed
  • Industry best practice
  • No performance impact for our scale

Why 30-Day Sessions?

Balance between security and usability:

  • Long enough to avoid frequent re-authentication
  • Short enough to limit exposure window
  • Activity-based refresh keeps active sessions alive
  • Matches common web application patterns

Why Single Module?

Simplicity principle:

  • Authentication is cohesive functionality
  • ~300 lines doesn't justify splitting
  • Easier to audit security in one file
  • Reduces import complexity

Consequences

Positive

  1. Simple implementation - Single file, clear responsibilities
  2. Secure by default - Industry best practices applied
  3. Zero dependencies - Uses existing stack (SQLite, httpx)
  4. Maintainable - All auth logic in one place
  5. Testable - Clear function boundaries, mockable
  6. Production-ready - Proper session management, security
  7. IndieWeb compliant - Full IndieAuth specification support

Negative

  1. Manual session cleanup - Need periodic expired session removal
  2. No rate limiting - Could add in V2 if needed
  3. Single admin limitation - Architectural constraint for V1
  4. No 2FA - Relies entirely on IndieLogin's security

Mitigations

For session cleanup:

  • Run cleanup on each login attempt
  • Add admin command for manual cleanup
  • Document in operations guide

For rate limiting:

  • Deploy behind reverse proxy (nginx/Caddy)
  • Add to V2 if abuse detected
  • Log attempts for monitoring

Implementation Checklist

  • Create starpunk/auth.py module
  • Add session tables to database schema
  • Implement core authentication functions
  • Add custom exception classes
  • Create require_auth decorator
  • Write comprehensive tests (target: 90% coverage)
  • Add security logging
  • Document configuration requirements
  • Create integration tests with IndieLogin
  • Security audit checklist

References


ADR: 010 Date: 2025-11-18 Status: Accepted Decision: Single-module database-backed session authentication with IndieLogin Supersedes: None