# ADR-022: IndieAuth Authentication Endpoint Correction ## Status Accepted ## Context StarPunk is encountering authentication failures with certain IndieAuth providers (specifically gondulf.thesatelliteoflove.com). After investigation, we discovered that StarPunk is incorrectly using the **token endpoint** for authentication-only flows, when it should be using the **authorization endpoint**. ### The Problem When attempting to authenticate with gondulf.thesatelliteoflove.com, the provider returns: ```json { "error": "invalid_grant", "error_description": "Authorization code must be redeemed at the authorization endpoint" } ``` StarPunk is currently sending authentication code redemption requests to `/token` when it should be sending them to the authorization endpoint for authentication-only flows. ### IndieAuth Specification Analysis According to the W3C IndieAuth specification (https://www.w3.org/TR/indieauth/): 1. **Authentication-only flows** (Section 5.4): - Used when the client only needs to verify user identity - Code redemption happens at the **authorization endpoint** - No `grant_type` parameter is used - Response contains only `{"me": "user-url"}` 2. **Authorization flows** (Section 6.3): - Used when the client needs an access token for API access - Code redemption happens at the **token endpoint** - Requires `grant_type=authorization_code` parameter - Response contains access token and user identity ### Current StarPunk Implementation StarPunk's current code in `/home/phil/Projects/starpunk/starpunk/auth.py` (lines 410-419): ```python token_exchange_data = { "grant_type": "authorization_code", # WRONG for authentication-only "code": code, "client_id": current_app.config["SITE_URL"], "redirect_uri": f"{current_app.config['SITE_URL']}auth/callback", "code_verifier": code_verifier, # PKCE verification } token_url = f"{current_app.config['INDIELOGIN_URL']}/token" # WRONG endpoint ``` This implementation has two errors: 1. Uses `/token` endpoint instead of authorization endpoint 2. Includes `grant_type` parameter which should not be present for authentication-only flows ## Decision StarPunk must correct its IndieAuth authentication implementation to comply with the specification: 1. **Use the authorization endpoint** for code redemption in authentication-only flows 2. **Remove the `grant_type` parameter** from authentication requests 3. **Keep PKCE parameters** (`code_verifier`) as they are still required ## Rationale ### Why This Matters 1. **Standards Compliance**: The IndieAuth specification clearly distinguishes between authentication and authorization flows 2. **Provider Compatibility**: Some providers (like gondulf) strictly enforce the specification 3. **Correct Semantics**: StarPunk only needs to verify admin identity, not obtain an access token ### Authentication vs Authorization StarPunk's admin login is an **authentication-only** use case: - We only need to verify the admin's identity (`me` URL) - We don't need an access token to access external resources - We create our own session after successful authentication This is fundamentally different from Micropub client authorization where: - External clients need access tokens - Tokens are used to authorize API access - The token endpoint is the correct choice ## Implementation ### Required Changes In `/home/phil/Projects/starpunk/starpunk/auth.py`, the `handle_callback` function must be updated: ```python def handle_callback(code: str, state: str, iss: Optional[str] = None) -> Optional[str]: # ... existing state verification code ... # Prepare authentication request (NOT token exchange) auth_data = { # NO grant_type parameter for authentication-only flows "code": code, "client_id": current_app.config["SITE_URL"], "redirect_uri": f"{current_app.config['SITE_URL']}auth/callback", "code_verifier": code_verifier, # PKCE verification still required } # Use authorization endpoint (NOT token endpoint) # The same endpoint used for the initial authorization request auth_url = f"{current_app.config['INDIELOGIN_URL']}/auth" # or /authorize # Exchange code for identity (authentication-only) response = httpx.post( auth_url, data=auth_data, timeout=10.0, ) # Response will be: {"me": "https://user.example.com"} # NOT an access token response ``` ### Endpoint Discovery Consideration IndieAuth providers may use different paths for their authorization endpoint: - IndieLogin.com uses `/auth` - Some providers use `/authorize` - The gondulf provider appears to use its root domain as the authorization endpoint The correct approach is to: 1. Discover the authorization endpoint from the provider's metadata 2. Use the same endpoint for both authorization initiation and code redemption 3. Store the discovered endpoint during the initial authorization request ## Consequences ### Positive - **Specification Compliance**: Correctly implements IndieAuth authentication flow - **Provider Compatibility**: Works with strict IndieAuth implementations - **Semantic Correctness**: Uses the right flow for the use case ### Negative - **Breaking Change**: May affect compatibility with providers that accept both endpoints - **Testing Required**: Need to verify with multiple IndieAuth providers ### Migration Impact - Existing sessions remain valid (no database changes) - Only affects new login attempts - Should be transparent to users ## Testing Strategy Test with multiple IndieAuth providers: 1. **IndieLogin.com** - Current provider (should continue working) 2. **gondulf.thesatelliteoflove.com** - Strict implementation 3. **tokens.indieauth.com** - Token-only endpoint (should fail for auth) 4. **Self-hosted implementations** - Various compliance levels ## Alternatives Considered ### Alternative 1: Support Both Endpoints Attempt token endpoint first, fall back to authorization endpoint on failure. - **Pros**: Maximum compatibility - **Cons**: Not specification-compliant, adds complexity - **Verdict**: Rejected - violates standards ### Alternative 2: Make Endpoint Configurable Allow admin to configure which endpoint to use. - **Pros**: Flexible for different providers - **Cons**: Confusing for users, not needed if we follow spec - **Verdict**: Rejected - specification is clear ### Alternative 3: Always Use Token Endpoint Continue current implementation, document incompatibility. - **Pros**: No code changes needed - **Cons**: Violates specification, limits provider choice - **Verdict**: Rejected - incorrect implementation ## References - [IndieAuth Specification Section 5.4](https://www.w3.org/TR/indieauth/#authentication-response): Authorization Code Verification for authentication flows - [IndieAuth Specification Section 6.3](https://www.w3.org/TR/indieauth/#token-response): Token Endpoint for authorization flows - [IndieAuth Authentication vs Authorization](https://indieweb.org/IndieAuth#Authentication_vs_Authorization): Community documentation - [ADR-021: IndieAuth Provider Strategy](/home/phil/Projects/starpunk/docs/decisions/ADR-021-indieauth-provider-strategy.md): Related architectural decision --- **Document Version**: 1.0 **Created**: 2025-11-22 **Author**: StarPunk Architecture Team **Status**: Accepted