feat: Complete IndieAuth server removal (Phases 2-4)

Completed all remaining phases of ADR-030 IndieAuth provider removal.
StarPunk no longer acts as an authorization server - all IndieAuth
operations delegated to external providers.

Phase 2 - Remove Token Issuance:
- Deleted /auth/token endpoint
- Removed token_endpoint() function from routes/auth.py
- Deleted tests/test_routes_token.py

Phase 3 - Remove Token Storage:
- Deleted starpunk/tokens.py module entirely
- Created migration 004 to drop tokens and authorization_codes tables
- Deleted tests/test_tokens.py
- Removed all internal token CRUD operations

Phase 4 - External Token Verification:
- Created starpunk/auth_external.py module
- Implemented verify_external_token() for external IndieAuth providers
- Updated Micropub endpoint to use external verification
- Added TOKEN_ENDPOINT configuration
- Updated all Micropub tests to mock external verification
- HTTP timeout protection (5s) for external requests

Additional Changes:
- Created migration 003 to remove code_verifier from auth_state
- Fixed 5 migration tests that referenced obsolete code_verifier column
- Updated 11 Micropub tests for external verification
- Fixed test fixture and app context issues
- All 501 tests passing

Breaking Changes:
- Micropub clients must use external IndieAuth providers
- TOKEN_ENDPOINT configuration now required
- Existing internal tokens invalid (tables dropped)

Migration Impact:
- Simpler codebase: -500 lines of code
- Fewer database tables: -2 tables (tokens, authorization_codes)
- More secure: External providers handle token security
- More maintainable: Less authentication code to maintain

Standards Compliance:
- W3C IndieAuth specification
- OAuth 2.0 Bearer token authentication
- IndieWeb principle: delegate to external services

Related:
- ADR-030: IndieAuth Provider Removal Strategy
- ADR-050: Remove Custom IndieAuth Server
- Migration 003: Remove code_verifier from auth_state
- Migration 004: Drop tokens and authorization_codes tables

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-24 17:23:46 -07:00
parent 869402ab0d
commit a3bac86647
36 changed files with 5597 additions and 2670 deletions

View File

@@ -1,81 +0,0 @@
{% extends "base.html" %}
{% block title %}Authorize Application - StarPunk{% endblock %}
{% block content %}
<div class="authorization-container">
<h2>Authorization Request</h2>
<div class="authorization-info">
<p class="auth-intro">
An application is requesting access to your StarPunk site.
</p>
<div class="client-info">
<h3>Application Details</h3>
<dl>
<dt>Client:</dt>
<dd><code>{{ client_id }}</code></dd>
<dt>Your Identity:</dt>
<dd><code>{{ me }}</code></dd>
{% if scope %}
<dt>Requested Permissions:</dt>
<dd>
<ul class="scope-list">
{% for s in scope.split() %}
<li><strong>{{ s }}</strong> - {% if s == 'create' %}Create new posts{% endif %}</li>
{% endfor %}
</ul>
</dd>
{% else %}
<dt>Requested Permissions:</dt>
<dd><em>No permissions requested (read-only access)</em></dd>
{% endif %}
</dl>
</div>
<div class="authorization-warning">
<p><strong>Warning:</strong> Only authorize applications you trust.</p>
<p>This application will be able to perform the above actions on your behalf.</p>
</div>
</div>
<form action="{{ url_for('auth.authorization_endpoint') }}" method="POST" class="authorization-form">
<!-- Pass through all parameters as hidden fields -->
<input type="hidden" name="client_id" value="{{ client_id }}">
<input type="hidden" name="redirect_uri" value="{{ redirect_uri }}">
<input type="hidden" name="state" value="{{ state }}">
<input type="hidden" name="scope" value="{{ scope }}">
<input type="hidden" name="me" value="{{ me }}">
<input type="hidden" name="response_type" value="{{ response_type }}">
{% if code_challenge %}
<input type="hidden" name="code_challenge" value="{{ code_challenge }}">
<input type="hidden" name="code_challenge_method" value="{{ code_challenge_method }}">
{% endif %}
<div class="authorization-actions">
<button type="submit" name="approve" value="yes" class="button button-primary">
Authorize
</button>
<button type="submit" name="approve" value="no" class="button button-secondary">
Deny
</button>
</div>
</form>
<div class="authorization-help">
<h3>What does this mean?</h3>
<p>
By clicking "Authorize", you allow this application to access your StarPunk site
with the permissions listed above. You can revoke access at any time from your
admin dashboard.
</p>
<p>
If you don't recognize this application or didn't intend to authorize it,
click "Deny" to reject the request.
</p>
</div>
</div>
{% endblock %}