- 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>
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:
-
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. -
No Secrets Required: All clients are public clients. There are no client secrets to manage or register.
-
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.
-
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:
- Admin visits
/admin/login - StarPunk redirects to IndieLogin.com with its own URL as
client_id - IndieLogin.com verifies the admin's identity
- 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:
- User configures their personal website (e.g.,
https://alice.com) with links to StarPunk's Micropub endpoint - User opens Micropub client (Quill, Indigenous, etc.)
- Client discovers authorization/token endpoints from
https://alice.com(NOT from StarPunk) - Client gets access token from the discovered authorization server
- Client uses token to POST to StarPunk's Micropub endpoint
- StarPunk verifies the token
Who Provides Authorization? The USER's chosen authorization server, not StarPunk
The Real Question
StarPunk faces two architectural decisions:
- Admin Authentication: How should StarPunk administrators authenticate to the admin interface?
- 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:
-
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
-
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
-
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">
-
Configure StarPunk Set your website URL in StarPunk configuration:
ADMIN_ME=https://your-website.com -
Use a Micropub Client
- Quill: https://quill.p3k.io
- Indigenous (mobile app)
- Or any Micropub-compatible client
-
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