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

    # 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 <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">
   ```

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
