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>
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:
- Generic IndieAuth specification - Full OAuth 2.0 with client discovery mechanisms
- 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
-
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.
-
Client Discovery Not Required: IndieLogin.com accepts any valid
client_idURL without pre-registration or metadata endpoints. The OAuth metadata endpoint and h-app microformats we added are unnecessary. -
PKCE is Mandatory: IndieLogin.com requires PKCE (Proof Key for Code Exchange) parameters for security. Our current implementation lacks this entirely.
-
Wrong Endpoints: We're using
/authwhen we should use/authorizeand/token.
Critical Missing Pieces
Our current implementation in starpunk/auth.py is missing:
- PKCE
code_verifiergeneration and storage - PKCE
code_challengegeneration and transmission code_verifierin 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:
-
Implement PKCE Flow
- Generate cryptographically secure
code_verifier(43-character random string) - Generate
code_challenge(SHA256 hash of verifier, base64-url encoded) - Store
code_verifierwith state token in database - Send
code_challengeandcode_challenge_method=S256in authorization request - Send
code_verifierin token exchange request
- Generate cryptographically secure
-
Use Correct IndieLogin.com Endpoints
- Authorization:
https://indielogin.com/authorize(not/auth) - Token exchange:
https://indielogin.com/token(not/auth)
- Authorization:
-
Required Parameters for Authorization Request
client_id- Our application URLredirect_uri- Our callback URL (must be on same domain)state- Random CSRF protection tokencode_challenge- PKCE challengecode_challenge_method- Must beS256me- User's URL (optional, prompts if omitted)
-
Required Parameters for Token Exchange
code- Authorization code from callbackclient_id- Our application URL (same as authorization)redirect_uri- Our callback URL (same as authorization)code_verifier- Original PKCE verifier
-
Validate Callback Parameters
- Verify
statematches stored value (CSRF protection) - Verify
issequalshttps://indielogin.com/(issuer validation) - Extract
codefor token exchange
- Verify
-
Remove Unnecessary Components
- Remove OAuth metadata endpoint (
/.well-known/oauth-authorization-server) - Remove h-app microformats markup from templates
- Remove
indieauth-metadatalink from HTML head - Remove unused
response_typeparameter from authorization request
- Remove OAuth metadata endpoint (
Rationale
Why This Approach is Correct
-
Based on Official Documentation: Every decision comes directly from https://indielogin.com/api, the authoritative source for the service we use.
-
PKCE is Non-Negotiable: IndieLogin.com requires it for security. PKCE prevents authorization code interception attacks, especially important for public clients.
-
Simple Authentication Flow: We need identity verification (web sign-in), not resource authorization. IndieLogin.com provides exactly this.
-
No Client Registration Required: IndieLogin.com accepts any valid
client_idURL. Pre-registration mechanisms add complexity without benefit. -
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
- Minimal Code: Removes ~73 lines of unnecessary code (metadata endpoint, microformats)
- Standards First: Follows official IndieLogin.com API specification
- "Every line must justify existence": Eliminates features that don't serve actual requirements
- No Lock-in: Standard OAuth/PKCE implementation portable to other services
Consequences
Positive
- Authentication Will Work: Follows IndieLogin.com API requirements exactly
- Simpler Codebase: Net reduction of ~23 lines after adding PKCE and removing unnecessary features
- Better Security: PKCE protection against authorization code attacks
- Standards Compliant: Proper PKCE implementation per RFC 7636
- More Maintainable: Clearer code with focused purpose
- Better Testability: Well-defined flow with clear inputs/outputs
Negative
-
Database Migration Required: Must add
code_verifiercolumn toauth_statetable- Mitigation: Simple
ALTER TABLE, backward compatible with default value
- Mitigation: Simple
-
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)
-
More Complex Auth Flow: PKCE adds generation/storage/validation steps
- Mitigation: Security benefit justifies complexity
- Well-encapsulated in helper functions
Neutral
- Code Changes: Adds ~50 lines for PKCE, removes ~73 lines of unnecessary features (net -23 lines)
- Testing: More test cases for PKCE, but clearer test boundaries
Superseded Decisions
This ADR supersedes:
-
ADR-016: IndieAuth Client Discovery Mechanism
- h-app microformats not required by IndieLogin.com
- Status: Superseded
-
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:
- 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
/authorizeendpoint for authentication initiation - Uses
/tokenendpoint 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
- IndieLogin.com API Documentation: https://indielogin.com/api
- Authoritative source for all implementation decisions
Supporting Specifications
- PKCE Specification (RFC 7636): https://www.rfc-editor.org/rfc/rfc7636
- OAuth 2.0 (RFC 6749): https://www.rfc-editor.org/rfc/rfc6749
- IndieAuth Specification: https://indieauth.spec.indieweb.org/ (context only)
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
- Read the specific API documentation first, not generic specifications
- Service-specific implementations matter: IndieLogin.com is not a generic IndieAuth server
- PKCE is increasingly required: Modern OAuth services mandate it for public clients
- Authentication ≠ Authorization: Different use cases require different OAuth flows
- 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)