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>
This commit is contained in:
226
docs/decisions/ADR-019-indieauth-pkce-authentication.md
Normal file
226
docs/decisions/ADR-019-indieauth-pkce-authentication.md
Normal file
@@ -0,0 +1,226 @@
|
||||
# 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:
|
||||
|
||||
3. **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
|
||||
- **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
|
||||
|
||||
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)
|
||||
Reference in New Issue
Block a user