Files
StarPunk/docs/decisions/ADR-022-indieauth-authentication-endpoint-correction.md
Phil Skentelbery a6f3fbaae4 fix: Use authorization endpoint for IndieAuth code verification (v0.9.4)
IndieAuth authentication-only flows should redeem the code at the
authorization endpoint, not the token endpoint. The token endpoint
is only for authorization flows that need access tokens.

- Remove grant_type parameter (only needed for token flows)
- Change endpoint from /token to /authorize
- Update debug logging to reflect code verification flow

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 19:19:37 -07:00

7.2 KiB

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:

{
  "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):

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:

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


Document Version: 1.0 Created: 2025-11-22 Author: StarPunk Architecture Team Status: Accepted