Files
StarPunk/docs/decisions/ADR-019-indieauth-pkce-authentication.md
Phil Skentelbery 5e50330bdf feat: Implement PKCE authentication for IndieLogin.com
This fixes critical IndieAuth authentication by implementing PKCE (Proof Key
for Code Exchange) as required by IndieLogin.com API specification.

Added:
- PKCE code_verifier and code_challenge generation (RFC 7636)
- Database column: auth_state.code_verifier for PKCE support
- Issuer validation for authentication callbacks
- Comprehensive PKCE unit tests (6 tests, all passing)
- Database migration script for code_verifier column

Changed:
- Corrected IndieLogin.com API endpoints (/authorize and /token)
- State token validation now returns code_verifier for token exchange
- Authentication flow follows IndieLogin.com API specification exactly
- Enhanced logging with code_verifier redaction

Removed:
- OAuth metadata endpoint (/.well-known/oauth-authorization-server)
  Added in v0.7.0 but not required by IndieLogin.com
- h-app microformats markup from templates
  Modified in v0.7.1 but not used by IndieLogin.com
- indieauth-metadata link from HTML head

Security:
- PKCE prevents authorization code interception attacks
- Issuer validation prevents token substitution attacks
- Code verifier securely stored, redacted in logs, and single-use

Documentation:
- Version: 0.8.0
- CHANGELOG updated with v0.8.0 entry and v0.7.x notes
- ADR-016 and ADR-017 marked as superseded by ADR-019
- Implementation report created in docs/reports/
- Test update guide created in TODO_TEST_UPDATES.md

Breaking Changes:
- Users mid-authentication will need to restart login after upgrade
- Database migration required before deployment

Related: ADR-019

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 15:43:38 -07:00

9.7 KiB

ADR-019: IndieAuth Correct Implementation Based on IndieLogin.com API

Status

Accepted

Context

StarPunk's IndieAuth authentication has been failing in production despite implementing various fixes (ADR-016, ADR-017) including OAuth metadata endpoints and h-app microformats. These implementations were based on misunderstanding the requirements of the specific service we use: IndieLogin.com.

The Core Problem

We conflated two different things:

  1. Generic IndieAuth specification - Full OAuth 2.0 with client discovery mechanisms
  2. IndieLogin.com API - Simplified authentication-only service with specific requirements

IndieLogin.com is a simplified authentication service, not a full OAuth 2.0 authorization server. It has specific API requirements that differ from the generic IndieAuth specification.

What We Misunderstood

  1. Authentication vs Authorization: IndieLogin.com provides authentication (who are you?) not authorization (what can you access?). No scopes, no access tokens for API access - just identity verification.

  2. Client Discovery Not Required: IndieLogin.com accepts any valid client_id URL without pre-registration or metadata endpoints. The OAuth metadata endpoint and h-app microformats we added are unnecessary.

  3. PKCE is Mandatory: IndieLogin.com requires PKCE (Proof Key for Code Exchange) parameters for security. Our current implementation lacks this entirely.

  4. Wrong Endpoints: We're using /auth when we should use /authorize and /token.

Critical Missing Pieces

Our current implementation in starpunk/auth.py is missing:

  • PKCE code_verifier generation and storage
  • PKCE code_challenge generation and transmission
  • code_verifier in token exchange
  • Issuer (iss) validation
  • Correct API endpoints

Why Previous Fixes Failed

  • ADR-016 (h-app microformats): Added client discovery mechanism that IndieLogin.com doesn't use
  • ADR-017 (OAuth metadata endpoint): Added OAuth endpoint that IndieLogin.com doesn't check
  • Original implementation: Missing PKCE, wrong endpoints, incomplete parameter set

Decision

Implement IndieAuth authentication following the IndieLogin.com API specification exactly, specifically:

  1. Implement PKCE Flow

    • Generate cryptographically secure code_verifier (43-character random string)
    • Generate code_challenge (SHA256 hash of verifier, base64-url encoded)
    • Store code_verifier with state token in database
    • Send code_challenge and code_challenge_method=S256 in authorization request
    • Send code_verifier in token exchange request
  2. Use Correct IndieLogin.com Endpoints

    • Authorization: https://indielogin.com/authorize (not /auth)
    • Token exchange: https://indielogin.com/token (not /auth)
  3. Required Parameters for Authorization Request

    • client_id - Our application URL
    • redirect_uri - Our callback URL (must be on same domain)
    • state - Random CSRF protection token
    • code_challenge - PKCE challenge
    • code_challenge_method - Must be S256
    • me - User's URL (optional, prompts if omitted)
  4. Required Parameters for Token Exchange

    • code - Authorization code from callback
    • client_id - Our application URL (same as authorization)
    • redirect_uri - Our callback URL (same as authorization)
    • code_verifier - Original PKCE verifier
  5. Validate Callback Parameters

    • Verify state matches stored value (CSRF protection)
    • Verify iss equals https://indielogin.com/ (issuer validation)
    • Extract code for token exchange
  6. Remove Unnecessary Components

    • Remove OAuth metadata endpoint (/.well-known/oauth-authorization-server)
    • Remove h-app microformats markup from templates
    • Remove indieauth-metadata link from HTML head
    • Remove unused response_type parameter from authorization request

Rationale

Why This Approach is Correct

  1. Based on Official Documentation: Every decision comes directly from https://indielogin.com/api, the authoritative source for the service we use.

  2. PKCE is Non-Negotiable: IndieLogin.com requires it for security. PKCE prevents authorization code interception attacks, especially important for public clients.

  3. Simple Authentication Flow: We need identity verification (web sign-in), not resource authorization. IndieLogin.com provides exactly this.

  4. No Client Registration Required: IndieLogin.com accepts any valid client_id URL. Pre-registration mechanisms add complexity without benefit.

  5. Security Best Practices:

    • State token prevents CSRF attacks
    • PKCE prevents authorization code interception
    • Issuer validation prevents token substitution
    • Single-use tokens prevent replay attacks

Alignment with Project Principles

  1. Minimal Code: Removes ~73 lines of unnecessary code (metadata endpoint, microformats)
  2. Standards First: Follows official IndieLogin.com API specification
  3. "Every line must justify existence": Eliminates features that don't serve actual requirements
  4. No Lock-in: Standard OAuth/PKCE implementation portable to other services

Consequences

Positive

  1. Authentication Will Work: Follows IndieLogin.com API requirements exactly
  2. Simpler Codebase: Net reduction of ~23 lines after adding PKCE and removing unnecessary features
  3. Better Security: PKCE protection against authorization code attacks
  4. Standards Compliant: Proper PKCE implementation per RFC 7636
  5. More Maintainable: Clearer code with focused purpose
  6. Better Testability: Well-defined flow with clear inputs/outputs

Negative

  1. Database Migration Required: Must add code_verifier column to auth_state table

    • Mitigation: Simple ALTER TABLE, backward compatible with default value
  2. Breaking Change for In-Flight Logins: Users mid-authentication must restart

    • Mitigation: State tokens expire in 5 minutes anyway, minimal impact
    • Existing sessions remain valid (no logout of authenticated users)
  3. More Complex Auth Flow: PKCE adds generation/storage/validation steps

    • Mitigation: Security benefit justifies complexity
    • Well-encapsulated in helper functions

Neutral

  1. Code Changes: Adds ~50 lines for PKCE, removes ~73 lines of unnecessary features (net -23 lines)
  2. Testing: More test cases for PKCE, but clearer test boundaries

Superseded Decisions

This ADR supersedes:

  1. ADR-016: IndieAuth Client Discovery Mechanism

    • h-app microformats not required by IndieLogin.com
    • Status: Superseded
  2. ADR-017: OAuth Client ID Metadata Document Implementation

    • OAuth metadata endpoint not required by IndieLogin.com
    • Status: Superseded

This ADR corrects the implementation details (but not the concept) in:

  1. ADR-005: IndieLogin Authentication Integration
    • Authentication flow concept remains valid
    • Implementation corrected: added PKCE, corrected endpoints, added issuer validation
    • Status: Accepted (with implementation note)

Version Impact

Change Type: Critical bug fix (authentication completely broken in production)

Semantic Versioning Analysis:

  • Fixes broken feature: IndieAuth authentication
  • Removes features: OAuth metadata endpoint (added in v0.7.0, never functioned)
  • Adds security enhancement: PKCE implementation
  • Database schema change: Adding column (backward compatible with default)

Version Decision: See versioning guidance document for final determination based on current release state.

Compliance

IndieLogin.com API Requirements

  • Uses /authorize endpoint for authentication initiation
  • Uses /token endpoint for code exchange
  • Sends all required parameters per API documentation
  • Implements required PKCE flow
  • Validates state and issuer per security recommendations

PKCE Specification (RFC 7636)

  • code_verifier: 43-128 character URL-safe random string
  • code_challenge: Base64-URL encoded SHA256 hash
  • code_challenge_method: S256
  • Proper storage and single-use validation

Project Standards

  • Minimal code principle
  • Standards-first approach
  • Security best practices
  • Clear documentation of decisions

Implementation Notes

The technical implementation is documented in:

  • Design Document: /home/phil/Projects/starpunk/docs/designs/indieauth-pkce-authentication.md - Technical specifications, flow diagrams, PKCE implementation details
  • Implementation Guide: Included in design document - Step-by-step developer instructions, code changes, testing strategy

References

Primary Source

Supporting Specifications

Internal Documentation

  • ADR-005: IndieLogin Authentication Integration (conceptual flow)
  • ADR-010: Authentication Module Design
  • ADR-016: IndieAuth Client Discovery Mechanism (superseded)
  • ADR-017: OAuth Client ID Metadata Document (superseded)

What We Learned

  1. Read the specific API documentation first, not generic specifications
  2. Service-specific implementations matter: IndieLogin.com is not a generic IndieAuth server
  3. PKCE is increasingly required: Modern OAuth services mandate it for public clients
  4. Authentication ≠ Authorization: Different use cases require different OAuth flows
  5. Simpler is often correct: Unnecessary features indicate misunderstanding of requirements

Decided: 2025-11-19 Author: StarPunk Architect Supersedes: ADR-016, ADR-017 Corrects: ADR-005 (implementation details)