Files
StarPunk/docs/reports/indieauth-client-discovery-analysis.md
Phil Skentelbery 6a29b0199e Fix IndieAuth client discovery for production authentication
Add h-app microformats markup to base.html to enable IndieLogin.com
to verify StarPunk as a legitimate OAuth client. Without this markup,
IndieLogin returns "client_id is not registered" error, blocking all
production authentication.

The h-app markup provides client identification per IndieAuth legacy
standard, which is widely supported by authorization servers including
IndieLogin.com.

Changes:
- Add h-app microformats div to base.html footer (hidden)
- Update version to v0.6.1 (patch release per ADR-008)
- Update CHANGELOG.md with v0.6.1 release notes
- Add 6 comprehensive tests for h-app markup (all passing)
- Create ADR-016 documenting client discovery decision
- Create architecture analysis report
- Create implementation report

Tests: 456 total, 455 passing (99.78%)
New tests: 6 for h-app microformats (100% passing)

Fixes critical bug preventing production authentication.

Related: Phase 3 Authentication implementation, ADR-016

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

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

19 KiB

IndieAuth Client Discovery Error - Architectural Analysis

Date: 2025-11-19 Reviewer: StarPunk Architect Agent Issue: Production IndieAuth failure - "client_id is not registered" Severity: CRITICAL - Blocks all production authentication Status: Analysis complete, solution identified


Executive Summary

The proposed fix is INCORRECT and will not resolve the issue.

The error "This client_id is not registered" occurs because IndieLogin.com cannot fetch and verify the client_id URL (https://starpunk.thesatelliteoflove.com). The proposed fix of adding rel="authorization_endpoint" and rel="token_endpoint" links to the HTML head is backwards - these links advertise where a user's identity provider endpoints are, not where a client application's endpoints are.

Root Cause: StarPunk is missing client identification metadata that IndieAuth servers need to verify the application.

Correct Solution: Implement one of three IndieAuth client discovery mechanisms (detailed below).


Understanding IndieAuth Client Discovery

The Authentication Flow

When a user tries to authenticate:

  1. User submits their identity URL (me) to StarPunk
  2. StarPunk redirects user to IndieLogin.com with:
    • client_id=https://starpunk.thesatelliteoflove.com
    • redirect_uri=https://starpunk.thesatelliteoflove.com/auth/callback
    • state=<csrf-token>
  3. IndieLogin.com fetches the client_id URL to verify the client
  4. IndieLogin.com authenticates the user
  5. IndieLogin.com redirects back to StarPunk

The error occurs at step 3 - IndieLogin.com cannot verify StarPunk as a legitimate client.

What IndieAuth Servers Look For

Per the IndieAuth specification (2025 edition), authorization servers must verify clients by fetching the client_id URL and looking for one of these (in order of preference):

1. Client ID Metadata Document (Current Standard - 2022+)

A JSON document at /.well-known/oauth-authorization-server or linked via rel="indieauth-metadata":

{
  "issuer": "https://starpunk.thesatelliteoflove.com",
  "client_id": "https://starpunk.thesatelliteoflove.com",
  "client_name": "StarPunk",
  "client_uri": "https://starpunk.thesatelliteoflove.com",
  "logo_uri": "https://starpunk.thesatelliteoflove.com/static/logo.png",
  "redirect_uris": [
    "https://starpunk.thesatelliteoflove.com/auth/callback"
  ]
}

2. h-app Microformats (Legacy - Pre-2022)

HTML microformats markup in the client_id page:

<div class="h-app">
  <a href="https://starpunk.thesatelliteoflove.com" class="u-url p-name">StarPunk</a>
  <img src="/static/logo.png" class="u-logo" alt="StarPunk">
  <p class="p-summary">A minimal IndieWeb CMS for publishing notes</p>
</div>

3. Basic HTML (Minimal Fallback)

At minimum, the client_id URL must return a valid HTML page (some servers accept any 200 OK response).


Analysis of Proposed Fix

What Was Proposed

Add to templates/base.html:

<link rel="authorization_endpoint" href="https://indielogin.com/auth">
<link rel="token_endpoint" href="https://indielogin.com/token">

Why This Is Wrong

These rel values serve a completely different purpose:

  1. authorization_endpoint and token_endpoint advertise where a user's identity provider has its endpoints
  2. They would be used on a user's personal website (their me URL), not on a client application
  3. They tell IndieAuth clients "here's where to authenticate ME", not "here's information about THIS application"

Example of correct usage: If Alice's personal site is https://alice.example.com, HER website would include:

<link rel="authorization_endpoint" href="https://alice.example.com/auth">
<link rel="token_endpoint" href="https://alice.example.com/token">

This tells IndieAuth clients "to authenticate Alice, use these endpoints."

StarPunk is a client application, not an identity provider, so these links are inappropriate and won't solve the registration error.

Why It Appeared to Work (If It Did)

If adding these links appeared to resolve the issue, it's likely coincidental:

  1. The HTTP request to the client_id URL succeeded (returned 200 OK)
  2. IndieLogin.com accepted the basic HTML response
  3. The specific rel values were ignored

This would be a fragile solution that doesn't follow standards.


Correct Solutions

Recommendation: Solution 2 (h-app Microformats)

I recommend implementing h-app microformats for backward compatibility and simplicity.

Solution 1: Client ID Metadata Document (Most Standards-Compliant)

Complexity: Medium Standards: Current (2022+) Compatibility: Modern IndieAuth servers only

Implementation

  1. Create endpoint: GET /.well-known/oauth-authorization-server
  2. Return JSON metadata document
  3. Set Content-Type: application/json

Code Location: starpunk/routes/public.py

@public_bp.route('/.well-known/oauth-authorization-server')
def client_metadata():
    """OAuth Client ID Metadata Document for IndieAuth"""
    metadata = {
        "issuer": current_app.config['SITE_URL'],
        "client_id": current_app.config['SITE_URL'],
        "client_name": current_app.config.get('SITE_NAME', 'StarPunk'),
        "client_uri": current_app.config['SITE_URL'],
        "redirect_uris": [
            f"{current_app.config['SITE_URL']}/auth/callback"
        ]
    }
    return jsonify(metadata)

Pros:

  • Current standard (2022+)
  • Clean separation of concerns
  • Machine-readable
  • Easy to extend

Cons:

  • Not supported by older IndieAuth servers
  • Requires new route
  • May not be supported by IndieLogin.com if it's running older code

Complexity: Low Standards: Legacy (pre-2022) but widely supported Compatibility: All IndieAuth servers

Implementation

Add to templates/base.html in the <body> (or create a dedicated footer/header):

<div class="h-app" style="display: none;">
  <a href="{{ config.SITE_URL }}" class="u-url p-name">{{ config.SITE_NAME }}</a>
  <p class="p-summary">A minimal IndieWeb CMS for publishing notes</p>
</div>

Minimal version (if we want to keep it even simpler):

<div class="h-app" hidden>
  <a href="{{ config.SITE_URL }}" class="u-url p-name">StarPunk</a>
</div>

Where to add: In base.html, inside <body> tag, preferably in the footer area.

Pros:

  • Widely supported (backward compatible)
  • Simple to implement (3 lines of HTML)
  • No new routes needed
  • Likely what IndieLogin.com expects
  • Can be hidden from users (display: none or hidden attribute)

Cons:

  • Uses "legacy" standard (though still widely supported)
  • Mixes presentation and authentication metadata

Solution 3: Hybrid Approach (Most Robust)

Implement both solutions for maximum compatibility:

  1. Add h-app microformats to base.html (for legacy support)
  2. Add /.well-known/oauth-authorization-server endpoint (for modern support)

Pros:

  • Works with all IndieAuth servers
  • Future-proof
  • Standards-compliant

Cons:

  • Slight duplication of information
  • More implementation work

Testing the Fix

Verification Steps

  1. Test client_id fetch:

    curl -I https://starpunk.thesatelliteoflove.com
    

    Should return 200 OK

  2. Verify h-app markup (if using Solution 2):

    curl https://starpunk.thesatelliteoflove.com | grep h-app
    

    Should show the h-app div

  3. Test with IndieAuth validator: Use https://indieauth.spec.indieweb.org/validator or a similar tool

  4. Test actual auth flow:

    • Navigate to /admin/login
    • Enter your identity URL
    • Verify IndieLogin.com accepts the client_id
    • Complete authentication

Expected Results After Fix

  • IndieLogin.com should no longer show "client_id is not registered"
  • User should see authentication prompt for their identity
  • Successful auth should redirect back to StarPunk

Architecture Decision Record

This issue reveals a gap in our Phase 3 implementation - we implemented IndieAuth authentication but not IndieAuth client identification.

Should We Create an ADR?

Yes - This is an architectural decision about how StarPunk identifies itself to authorization servers.

ADR Subject: Client identification mechanism for IndieAuth

Decision Points:

  1. Which client discovery mechanism to implement
  2. Whether to support legacy h-app or modern JSON metadata
  3. Where to place the metadata (route vs template)

Decision: Implement h-app microformats in base.html (Solution 2)

Rationale:

  1. Simplicity: Aligns with project philosophy ("minimal code")
  2. Compatibility: Works with all IndieAuth servers including older ones
  3. Pragmatic: IndieLogin.com likely expects h-app (it's older software)
  4. Low Risk: 3 lines of HTML vs new route with JSON endpoint
  5. V1 Scope: Minimal viable solution for single-user system

Future Considerations:

  • V2 could add JSON metadata endpoint for standards compliance
  • Hybrid approach if we encounter compatibility issues

Version Impact Analysis

Is This a Bug or Missing Feature?

Classification: Bug (Critical)

Reasoning:

  • Phase 3/4 claimed to implement "IndieAuth authentication"
  • Production authentication is completely broken
  • Feature was tested only in DEV_MODE (bypasses IndieAuth)
  • This is a missing requirement from the IndieAuth spec

Version Number Impact

Current Version: v0.6.0 (released 2025-11-19)

Recommended Version After Fix: v0.6.1

Rationale (per ADR-008 Versioning Strategy):

  • Not v0.7.0: This is a bug fix, not a new feature
  • Not v1.0.0: Not a breaking change to API or data format
  • v0.6.1: Patch release for critical bug fix

Severity Level: CRITICAL

  • Production authentication completely broken
  • No workaround except switching to DEV_MODE (insecure)
  • Affects all production deployments

Git Strategy

Branch Strategy (per ADR-009)

Recommended Approach: Hotfix branch

git checkout -b hotfix/indieauth-client-discovery

Rationale:

  • Critical production bug
  • Needs immediate fix
  • Should be merged directly to main
  • Should be tagged as v0.6.1

Not a Feature Branch because:

  • This isn't new functionality
  • It's fixing broken production behavior
  • Hotfix process is appropriate

Commit Strategy

Single Commit vs Multiple Commits:

Recommend single atomic commit:

  • Change is small (adding h-app markup)
  • Logically cohesive
  • Easy to cherry-pick or revert if needed

Commit Message Template:

Fix IndieAuth client discovery for production authentication

Add h-app microformats markup to base.html to enable IndieLogin.com
to verify StarPunk as a legitimate OAuth client. Without this markup,
IndieLogin returns "client_id is not registered" error, blocking all
production authentication.

The h-app markup provides client identification per IndieAuth legacy
standard, which is widely supported by authorization servers including
IndieLogin.com.

Fixes critical bug preventing production authentication.

Related: Phase 3 Authentication implementation

Documentation Updates Required

Files to Update

  1. CHANGELOG.md:

    • Add v0.6.1 section
    • Document bug fix under "Fixed"
    • Reference IndieAuth client discovery
  2. docs/decisions/ADR-016-indieauth-client-discovery.md (NEW):

    • Document decision to use h-app microformats
    • Explain alternatives considered
    • Document why this was missed in Phase 3
  3. docs/design/phase-3-authentication.md (UPDATE):

    • Add section on client discovery requirements
    • Document h-app implementation
    • Note this as errata/addition to original spec
  4. docs/reports/indieauth-client-discovery-fix.md (NEW):

    • Implementation report
    • Testing results
    • Deployment notes

Acceptance Criteria for Fix

The fix is complete when:

  • h-app microformats added to base.html (or JSON endpoint implemented)
  • StarPunk homepage returns 200 OK and contains client identification
  • IndieLogin.com accepts client_id without "not registered" error
  • Full authentication flow works in production
  • Tests added to verify h-app markup presence
  • ADR-016 created documenting decision
  • CHANGELOG.md updated for v0.6.1
  • Version bumped to v0.6.1 in starpunk/init.py
  • Hotfix branch merged to main
  • Release tagged as v0.6.1
  • Production deployment tested and verified

Implementation Specification

File: templates/base.html

Location: Add in <footer> section, before closing </footer> tag

Code:

  <footer>
    <p>StarPunk v{{ config.get('VERSION', '0.6.1') }}</p>

    <!-- IndieAuth client discovery (h-app microformats) -->
    <div class="h-app" hidden aria-hidden="true">
      <a href="{{ config.SITE_URL }}" class="u-url p-name">{{ config.get('SITE_NAME', 'StarPunk') }}</a>
    </div>
  </footer>

Justification for Location:

  • Footer is semantically appropriate for metadata
  • hidden attribute hides from visual presentation
  • aria-hidden="true" hides from screen readers
  • Still parseable by IndieAuth servers
  • Doesn't affect page layout

CSS Not Required: The hidden attribute provides sufficient hiding.


Risk Assessment

Risks of Current State (No Fix)

  • CRITICAL: Production authentication completely broken
  • Users cannot access admin interface in production
  • Forces use of DEV_MODE (security risk)
  • Project cannot be deployed to production

Risks of Proposed Fix (h-app microformats)

  • LOW: Minimal risk
  • Small, localized change
  • Widely supported standard
  • Easy to revert if issues occur
  • No database migrations
  • No breaking changes

Risks of Alternative Fix (JSON metadata endpoint)

  • MEDIUM: Moderate risk
  • New route could have bugs
  • May not be supported by IndieLogin.com
  • More code to test
  • Higher chance of unintended side effects

Performance Impact

Impact: Negligible

  • Adds ~80 bytes to HTML response
  • No additional HTTP requests
  • No database queries
  • No server-side processing
  • Minimal parsing overhead for IndieAuth servers

Performance Score: No measurable impact

JSON Metadata Endpoint

Impact: Minimal

  • One additional route
  • Negligible JSON serialization overhead
  • Only called during auth flow (infrequent)
  • No database queries

Performance Score: Negligible impact


Security Considerations

Security Impact of h-app Microformats

Positive:

  • Enables proper IndieAuth client verification
  • Prevents client impersonation

Neutral:

  • Exposes client metadata (already public via HTTP)
  • No sensitive information disclosed

No Security Risks Identified

Information Disclosure

The h-app markup reveals:

  • Site URL (already public)
  • Site name (already public in page title)

Assessment: No additional information disclosure beyond what's already in public HTML.


Standards Compliance Checklist

IndieWeb Standards

  • Implements IndieAuth client discovery (currently missing)
  • Uses h-app microformats OR Client ID Metadata Document
  • Client metadata accessible via HTTP GET
  • Client_id URL returns 200 OK

Web Standards

  • Valid HTML5 (hidden attribute is standard)
  • Valid microformats2 (h-app, u-url, p-name)
  • Accessible (aria-hidden for screen readers)
  • SEO neutral (hidden content not indexed)

Testing Strategy

Unit Tests

File: tests/test_templates.py (new file or existing)

Test Cases:

  1. Test h-app markup present in base.html
  2. Test h-app contains correct URL
  3. Test h-app contains site name
  4. Test h-app is hidden from visual display
def test_h_app_microformats_present(client):
    """Verify h-app client discovery markup exists"""
    response = client.get('/')
    assert response.status_code == 200
    assert b'class="h-app"' in response.data
    assert b'class="u-url p-name"' in response.data

def test_h_app_contains_site_url(client, app):
    """Verify h-app contains correct site URL"""
    response = client.get('/')
    assert app.config['SITE_URL'].encode() in response.data

Integration Tests

Manual Testing:

  1. Deploy to production
  2. Attempt IndieAuth login
  3. Verify no "client_id not registered" error
  4. Complete authentication flow
  5. Access admin dashboard

Automated Testing:

  • Use IndieAuth validator tool
  • Verify microformats parsing

Deployment Considerations

Deployment Process

  1. Build: No build changes required
  2. Database: No migrations required
  3. Configuration: No config changes required
  4. Rollback: Simple (revert commit)

Rollout Strategy

Recommended: Direct deployment (low risk)

  1. Merge hotfix branch to main
  2. Tag as v0.6.1
  3. Deploy to production
  4. Verify authentication works
  5. Monitor for issues

No Gradual Rollout Needed:

  • Change is low risk
  • No breaking changes
  • Easy to revert

Container Impact

Container Build:

  • No Containerfile changes needed
  • Rebuild image to include template update
  • Same base image and dependencies

Container Tag: Update to v0.6.1


Lessons Learned

What Went Wrong

  1. Incomplete Specification: Phase 3 design didn't include client discovery requirements
  2. Testing Gap: Only tested with DEV_MODE (bypasses IndieAuth)
  3. Spec Understanding: Missed IndieAuth client identification requirement
  4. Documentation: IndieAuth spec has multiple versions (2020, 2022) with different requirements

Process Improvements

  1. Testing Requirements: Always test production authentication paths
  2. Spec Review: Review full IndieAuth specification, not just authentication flow
  3. Integration Testing: Test with actual IndieLogin.com, not just mocks
  4. Documentation: Cross-reference all IndieWeb specs (IndieAuth, Micropub, Webmention)

Future Prevention

  1. Create comprehensive IndieAuth compliance checklist
  2. Add integration tests with actual authorization servers
  3. Review all IndieWeb specs for hidden requirements
  4. Test in production-like environment (not just DEV_MODE)

Conclusion

Proposed Fix Assessment: INCORRECT

Correct Fix: Add h-app microformats to base.html

Severity: CRITICAL (blocks production authentication)

Recommended Action: Implement Solution 2 (h-app microformats) immediately

Version: Bump to v0.6.1 (patch release)

Branch Strategy: Use hotfix branch per ADR-009

Documentation: Create ADR-016, update CHANGELOG.md

Risk Level: LOW (simple, well-understood fix)

Timeline: Can be implemented in < 1 hour


Next Steps for Developer

  1. Create hotfix branch: hotfix/indieauth-client-discovery
  2. Add h-app microformats to templates/base.html
  3. Update version to v0.6.1 in starpunk/__init__.py
  4. Add tests for h-app markup presence
  5. Create ADR-016 documenting decision
  6. Update CHANGELOG.md with v0.6.1 entry
  7. Create implementation report
  8. Test authentication flow in production
  9. Commit with message template above
  10. Merge to main and tag v0.6.1

Analysis by: StarPunk Architect Agent Date: 2025-11-19 Document Version: 1.0 Status: Ready for implementation