Files
StarPunk/docs/decisions/ADR-021-indieauth-provider-strategy.md
Phil Skentelbery 2eaf67279d docs: Standardize all IndieAuth spec references to W3C URL
- Updated 42 references from indieauth.spec.indieweb.org to www.w3.org/TR/indieauth
- Ensures consistency across all documentation
- Points to the authoritative W3C specification
- No functional changes, documentation update only

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-24 11:54:04 -07:00

20 KiB

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

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

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 <link> tags
    # Parse HTML for <link rel="indieauth-metadata">

    # Fetch metadata endpoint
    # Return token_endpoint URL

3. Micropub Endpoint Protection

@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)

# 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 <head>:
   ```html
   <link rel="authorization_endpoint" href="https://indielogin.com/auth">
   <link rel="token_endpoint" href="https://tokens.indieauth.com/token">
   <link rel="micropub" href="https://your-starpunk.example.com/api/micropub">
  1. Configure StarPunk Set your website URL in StarPunk configuration:

    ADMIN_ME=https://your-website.com
    
  2. Use a Micropub Client

  3. 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