# ADR-021: IndieAuth Provider Strategy ## Status Accepted ## Context StarPunk currently uses IndieLogin.com for authentication (ADR-005), but there is a critical misunderstanding about how IndieAuth works that needs to be addressed. ### The Problem The user reported that IndieLogin.com requires manual client_id registration, making it unsuitable for self-hosted software where each installation has a different domain. This concern is based on a fundamental misunderstanding of how IndieAuth differs from traditional OAuth2. ### How IndieAuth Actually Works Unlike traditional OAuth2 providers (GitHub, Google, etc.), **IndieAuth does not require pre-registration**: 1. **DNS-Based Client Identification**: IndieAuth uses DNS as a replacement for client registration. A client application identifies itself using its own URL (e.g., `https://starpunk.example.com`), which serves as a unique identifier. 2. **No Secrets Required**: All clients are public clients. There are no client secrets to manage or register. 3. **Dynamic Redirect URI Verification**: Instead of pre-registered redirect URIs, applications publish their valid redirect URLs at their client_id URL, which authorization servers can discover. 4. **Client Metadata Discovery**: Authorization servers can optionally fetch the client_id URL to display application information (name, logo) to users during authorization. ### StarPunk's Authentication Architecture It is critical to understand that StarPunk has **two distinct authentication flows**: #### Flow 1: Admin Authentication (Current Misunderstanding) **Purpose**: Authenticate the StarPunk admin user to access the admin interface **Current Implementation**: Uses IndieLogin.com as described in ADR-005 **How it works**: 1. Admin visits `/admin/login` 2. StarPunk redirects to IndieLogin.com with its own URL as `client_id` 3. IndieLogin.com verifies the admin's identity 4. Admin receives session cookie to access StarPunk admin **Registration Required?** NO - IndieAuth never requires registration #### Flow 2: Micropub Client Authorization (The Real Architecture) **Purpose**: Allow external Micropub clients to publish to StarPunk **How it works**: 1. User configures their personal website (e.g., `https://alice.com`) with links to StarPunk's Micropub endpoint 2. User opens Micropub client (Quill, Indigenous, etc.) 3. Client discovers authorization/token endpoints from `https://alice.com` (NOT from StarPunk) 4. Client gets access token from the discovered authorization server 5. Client uses token to POST to StarPunk's Micropub endpoint 6. StarPunk verifies the token **Who Provides Authorization?** The USER's chosen authorization server, not StarPunk ### The Real Question StarPunk faces two architectural decisions: 1. **Admin Authentication**: How should StarPunk administrators authenticate to the admin interface? 2. **User Authorization**: Should StarPunk provide authorization/token endpoints for its users, or should users bring their own? ## Research Findings ### Alternative IndieAuth Services **IndieLogin.com** (Current) - Actively maintained by Aaron Parecki (IndieAuth spec editor) - Supports multiple auth methods: RelMeAuth, email, PGP, BlueSky OAuth (added 2025) - **No registration required** - this was the key misunderstanding - Free, community service - High availability **tokens.indieauth.com** - Provides token endpoint functionality - Separate from authorization endpoint - Also maintained by IndieWeb community - Also requires no registration **Other Services** - No other widely-used public IndieAuth providers found - Most implementations are self-hosted (see below) ### Self-Hosted IndieAuth Implementations **Taproot/IndieAuth** (PHP) - Complexity: Moderate (7/10) - Full-featured: Authorization + token endpoints - PSR-7 compatible, well-tested (100% coverage) - Lightweight dependencies (Guzzle, mf2) - Production-ready since v0.1.0 **Selfauth** (PHP) - Complexity: Low (3/10) - **Limitation**: Authorization endpoint ONLY (no token endpoint) - Cannot be used for Micropub (requires token endpoint) - Suitable only for simple authentication use cases **hacdias/indieauth** (Go) - Complexity: Moderate (6/10) - Provides both server and client libraries - Modern Go implementation - Used in production by author **Custom Implementation** (Python) - Complexity: High (8/10) - Must implement IndieAuth spec 1.1 - Required endpoints: - Authorization endpoint (authentication + code generation) - Token endpoint (token issuance + verification) - Metadata endpoint (server discovery) - Introspection endpoint (token verification) - Must support: - PKCE (required by spec) - Client metadata discovery - Profile URL validation - Scope-based permissions - Token revocation - Estimated effort: 40-60 hours for full implementation - Ongoing maintenance burden for security updates ## Decision **Recommendation: Continue Using IndieLogin.com with Clarified Architecture** StarPunk should: 1. **For Admin Authentication**: Continue using IndieLogin.com (no changes needed) - No registration required - Works out of the box for self-hosted installations - Each StarPunk instance uses its own domain as client_id - Zero maintenance burden 2. **For Micropub Authorization**: Document that users must provide their own authorization server - User configures their personal domain with IndieAuth endpoints - User can choose: - IndieLogin.com (easiest) - Self-hosted IndieAuth server (advanced) - Any other IndieAuth-compliant service - StarPunk only verifies tokens, doesn't issue them 3. **For V2 Consideration**: Optionally provide built-in authorization server - Would allow StarPunk to be a complete standalone solution - Users could use StarPunk's domain as their identity - Requires implementing full IndieAuth server (40-60 hours) - Only pursue if there is strong user demand ## Rationale ### Why Continue with IndieLogin.com **Simplicity Score: 10/10** - Zero configuration required - No registration process - Works immediately for any domain - Battle-tested by IndieWeb community - The original concern (manual registration) does not exist **Fitness Score: 10/10** - Perfect for single-user CMS - Aligns with IndieWeb principles - User controls their identity - No lock-in (user can switch authorization servers) **Maintenance Score: 10/10** - Externally maintained - Security updates handled by community - No code to maintain in StarPunk - Proven reliability and uptime **Standards Compliance: Pass** - Full IndieAuth spec compliance - OAuth 2.0 compatible - Supports modern extensions (PKCE, client metadata) ### Why Not Self-Host (for V1) **Complexity vs Benefit** - Self-hosting adds 40-60 hours of development - Ongoing security maintenance burden - Solves a problem that doesn't exist (no registration required) - Violates "every line of code must justify its existence" **User Perspective** - Users already need a domain for IndieWeb - Most users will use IndieLogin.com or similar service - Advanced users can self-host their own IndieAuth server - StarPunk doesn't need to solve this problem **Alternative Philosophy** - StarPunk is a Micropub SERVER, not an authorization server - Separation of concerns: publishing vs identity - Users should control their own identity infrastructure - StarPunk focuses on doing one thing well: publishing notes ## Architectural Clarification ### Current Architecture (Correct Understanding) ``` ┌─────────────────────────────────────────────────────────────┐ │ Flow 1: Admin Authentication │ │ │ │ StarPunk Admin │ │ ↓ │ │ StarPunk (/admin/login) │ │ ↓ (redirect with client_id=https://starpunk.example) │ │ IndieLogin.com (verifies admin identity) │ │ ↓ (returns verified "me" URL) │ │ StarPunk (creates session) │ └─────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────┐ │ Flow 2: Micropub Publishing │ │ │ │ User's Website (https://alice.com) │ │ Links to: │ │ - authorization_endpoint (IndieLogin or self-hosted) │ │ - token_endpoint (tokens.indieauth.com or self-hosted) │ │ - micropub endpoint (StarPunk) │ │ ↓ │ │ Micropub Client (Quill, Indigenous) │ │ ↓ (discovers endpoints from alice.com) │ │ Authorization Server (user's choice, NOT StarPunk) │ │ ↓ (issues access token) │ │ Micropub Client │ │ ↓ (POST with Bearer token) │ │ StarPunk Micropub Endpoint │ │ ↓ (verifies token with authorization server) │ │ StarPunk (creates note) │ └─────────────────────────────────────────────────────────────┘ ``` ### What StarPunk Implements **Currently Implemented** (ADR-005): - Session-based admin authentication via IndieLogin.com - CSRF protection (state tokens) - Session management - Admin route protection **Must Be Implemented** (for Micropub): - Token verification endpoint (query user's token endpoint) - Bearer token extraction from Authorization header - Scope verification (check token has "create" permission) - Token storage/caching (optional, for performance) **Does NOT Implement** (users provide these): - Authorization endpoint (users use IndieLogin.com or self-hosted) - Token endpoint (users use tokens.indieauth.com or self-hosted) - User identity management (users own their domains) ## Implementation Outline ### No Changes Needed for Admin Auth The current IndieLogin.com integration (ADR-005) is correct and requires no changes. Each self-hosted StarPunk installation uses its own domain as `client_id` without any registration. ### Required for Micropub Support #### 1. Token Verification ```python def verify_micropub_token(bearer_token, expected_me): """ Verify access token by querying the token endpoint Args: bearer_token: Token from Authorization header expected_me: Expected user identity (from StarPunk config) Returns: dict: Token info (me, client_id, scope) if valid None: If token is invalid """ # Discover token endpoint from expected_me domain token_endpoint = discover_token_endpoint(expected_me) # Verify token response = httpx.get( token_endpoint, headers={'Authorization': f'Bearer {bearer_token}'}, params={'token': bearer_token} ) if response.status_code != 200: return None data = response.json() # Verify token is for expected user if data.get('me') != expected_me: return None # Verify token has required scope scope = data.get('scope', '') if 'create' not in scope: return None return data ``` #### 2. Endpoint Discovery ```python def discover_token_endpoint(me_url): """ Discover token endpoint from user's profile URL Checks for: 1. indieauth-metadata endpoint 2. Fallback to direct token_endpoint link """ response = httpx.get(me_url) # Check HTTP Link header link_header = response.headers.get('Link', '') # Parse link header for indieauth-metadata # Check HTML tags # Parse HTML for # Fetch metadata endpoint # Return token_endpoint URL ``` #### 3. Micropub Endpoint Protection ```python @app.route('/api/micropub', methods=['POST']) def micropub_endpoint(): # Extract bearer token auth_header = request.headers.get('Authorization', '') if not auth_header.startswith('Bearer '): return {'error': 'unauthorized'}, 401 bearer_token = auth_header[7:] # Remove "Bearer " # Verify token token_info = verify_micropub_token(bearer_token, ADMIN_ME) if not token_info: return {'error': 'forbidden'}, 403 # Process Micropub request # Create note # Return 201 with Location header ``` ### Documentation Updates #### For Users (Setup Guide) ```markdown # Setting Up Your IndieWeb Identity To publish to StarPunk via Micropub clients: 1. **Add Links to Your Website** Add these to your personal website's
: ```html ``` 2. **Configure StarPunk** Set your website URL in StarPunk configuration: ``` ADMIN_ME=https://your-website.com ``` 3. **Use a Micropub Client** - Quill: https://quill.p3k.io - Indigenous (mobile app) - Or any Micropub-compatible client 4. **Advanced: Self-Host Authorization** Instead of IndieLogin.com, you can run your own IndieAuth server. See: https://indieweb.org/IndieAuth#Software ``` #### For Developers (Architecture Docs) Update `/home/phil/Projects/starpunk/docs/architecture/overview.md` to clarify the two authentication flows and explain that StarPunk is a Micropub server, not an authorization server. ## Consequences ### Positive - **No development needed**: Current architecture is correct - **No registration required**: Works for self-hosted installations out of the box - **User control**: Users choose their own authorization provider - **Standards compliant**: Proper separation of Micropub server and authorization server - **Simple**: StarPunk focuses on publishing, not identity management - **Flexible**: Users can switch authorization providers without affecting StarPunk ### Negative - **User education required**: Must explain that they need to configure their domain - **Not standalone**: StarPunk cannot function completely independently (requires external auth) - **Dependency**: Relies on external services (mitigated: user chooses service) ### Neutral - **Architectural purity**: Follows IndieWeb principle of separation of concerns - **Complexity distribution**: Moves authorization complexity to where it belongs (identity provider) ## V2 Considerations If there is user demand for a more integrated solution, V2 could add: ### Option A: Embedded IndieAuth Server **Pros**: - StarPunk becomes completely standalone - Users can use StarPunk domain as their identity - One-step setup for non-technical users **Cons**: - 40-60 hours development effort - Ongoing security maintenance - Adds complexity to codebase - May violate simplicity principle **Decision**: Only implement if users request it ### Option B: Hybrid Mode **Pros**: - Advanced users can use external auth (current behavior) - Simple users can use built-in auth - Best of both worlds **Cons**: - Even more complexity - Two codepaths to maintain - Configuration complexity **Decision**: Defer until V2 user feedback ### Option C: StarPunk-Hosted Service **Pros**: - One StarPunk authorization server for all installations - Users register their StarPunk instance once - Simple for end users **Cons**: - Centralized service (not indie) - Single point of failure - Hosting/maintenance burden - Violates IndieWeb principles **Decision**: Rejected - not aligned with IndieWeb values ## Alternatives Considered ### Alternative 1: Self-Host IndieAuth (Taproot/PHP) **Evaluation**: - Complexity: Would require running PHP alongside Python - Deployment: Two separate applications to manage - Maintenance: Security updates for both Python and PHP - Verdict: **Rejected** - adds unnecessary complexity ### Alternative 2: Port Taproot to Python **Evaluation**: - Effort: 40-60 hours development - Maintenance: Full responsibility for security - Value: Solves a non-existent problem (no registration needed) - Verdict: **Rejected** - violates simplicity principle ### Alternative 3: Use OAuth2 Service (GitHub, Google) **Evaluation**: - Simplicity: Very simple to implement - IndieWeb Compliance: **FAIL** - not IndieWeb compatible - User Ownership: **FAIL** - users don't own their identity - Verdict: **Rejected** - violates core requirements ### Alternative 4: Password Authentication **Evaluation**: - Simplicity: Moderate (password hashing, reset flows) - IndieWeb Compliance: **FAIL** - not IndieWeb authentication - Security: Must implement password best practices - Verdict: **Rejected** - not aligned with IndieWeb principles ### Alternative 5: Use IndieAuth as Library (Client Side) **Evaluation**: - Would make StarPunk act as IndieAuth client to discover user's auth server - Current architecture already does this for Micropub - Admin interface uses simpler session-based auth - Verdict: **Already implemented** for Micropub flow ## Migration Plan ### From Current Broken Understanding → Correct Understanding **No Code Changes Required** 1. **Update Documentation** - Clarify that no registration is needed - Explain the two authentication flows - Document Micropub setup for users 2. **Complete Micropub Implementation** - Implement token verification - Implement endpoint discovery - Add Bearer token authentication 3. **User Education** - Create setup guide explaining domain configuration - Provide example HTML snippets - Link to IndieWeb resources ### Timeline - Documentation updates: 2 hours - Micropub token verification: 8 hours - Testing with real Micropub clients: 4 hours - Total: ~14 hours ## References ### IndieAuth Specifications - [IndieAuth Spec](https://www.w3.org/TR/indieauth/) - Official W3C specification - [OAuth 2.0](https://oauth.net/2/) - Underlying OAuth 2.0 foundation - [Client Identifier](https://www.oauth.com/oauth2-servers/indieauth/) - How client_id works in IndieAuth ### Services - [IndieLogin.com](https://indielogin.com/) - Public IndieAuth service (no registration) - [IndieLogin API Docs](https://indielogin.com/api) - Integration documentation - [tokens.indieauth.com](https://tokens.indieauth.com/token) - Public token endpoint service ### Self-Hosted Implementations - [Taproot/IndieAuth](https://github.com/Taproot/indieauth) - PHP implementation - [hacdias/indieauth](https://github.com/hacdias/indieauth) - Go implementation - [Selfauth](https://github.com/Inklings-io/selfauth) - Simple auth-only PHP ### IndieWeb Resources - [IndieWeb Wiki: IndieAuth](https://indieweb.org/IndieAuth) - Community documentation - [IndieWeb Wiki: Micropub](https://indieweb.org/Micropub) - Micropub overview - [IndieWeb Wiki: authorization-endpoint](https://indieweb.org/authorization-endpoint) - Endpoint details ### Related ADRs - [ADR-005: IndieLogin Authentication](/home/phil/Projects/starpunk/docs/decisions/ADR-005-indielogin-authentication.md) - Original auth decision - [ADR-010: Authentication Module Design](/home/phil/Projects/starpunk/docs/decisions/ADR-010-authentication-module-design.md) - Auth module structure ### Community Examples - [Aaron Parecki's IndieAuth Notes](https://aaronparecki.com/2025/10/08/4/cimd) - Client ID metadata adoption - [Jamie Tanna's IndieAuth Server](https://www.jvt.me/posts/2020/12/09/personal-indieauth-server/) - Self-hosted implementation - [Micropub Servers](https://indieweb.org/Micropub/Servers) - Examples of Micropub implementations --- **Document Version**: 1.0 **Created**: 2025-11-19 **Author**: StarPunk Architecture Team (agent-architect) **Status**: Accepted