Files
StarPunk/docs/decisions/ADR-022-auth-route-prefix-fix.md
Phil Skentelbery 44a97e4ffa fix: Change auth blueprint prefix from /admin to /auth (v0.9.2)
The auth routes were registered under /admin/* but the IndieAuth
redirect_uri was configured as /auth/callback, causing 404 errors
when providers redirected back after authentication.

- Change auth blueprint url_prefix from "/admin" to "/auth"
- Update test expectations for new auth route paths
- Add ADR-022 documenting the architectural decision

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

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

6.8 KiB

ADR-022: Fix IndieAuth Callback Route Mismatch

Status

Proposed

Context

We have discovered a critical routing mismatch in our IndieAuth implementation that causes a 404 error when IndieAuth providers redirect back to our application.

The Problem

The auth blueprint is currently registered with url_prefix="/admin" in /starpunk/routes/auth.py line 30:

bp = Blueprint("auth", __name__, url_prefix="/admin")

This means all auth routes are actually served under /admin:

  • /admin/login - Login form
  • /admin/callback - OAuth callback endpoint
  • /admin/logout - Logout endpoint

However, in /starpunk/auth.py lines 325 and 414, the redirect_uri sent to IndieAuth providers is:

redirect_uri = f"{current_app.config['SITE_URL']}auth/callback"

This mismatch causes IndieAuth providers to redirect users to /auth/callback, which doesn't exist, resulting in a 404 error.

Current Route Structure

  • Auth Blueprint (with /admin prefix):
    • /admin/login - Login form
    • /admin/callback - OAuth callback
    • /admin/logout - Logout endpoint
  • Admin Blueprint (with /admin prefix):
    • /admin/ - Dashboard
    • /admin/new - Create note
    • /admin/edit/<id> - Edit note
    • /admin/delete/<id> - Delete note

Decision

Change the auth blueprint URL prefix from /admin to /auth to match the redirect_uri being sent to IndieAuth providers.

Rationale

1. Separation of Concerns

Authentication routes (/auth/*) should be semantically separate from administration routes (/admin/*). This creates a cleaner architecture where:

  • /auth/* handles authentication flows (login, callback, logout)
  • /admin/* handles protected administrative functions (dashboard, CRUD operations)

2. Standards Compliance

IndieAuth and OAuth2 conventions typically use /auth/callback for OAuth callbacks:

  • Most OAuth documentation and examples use this pattern
  • IndieAuth implementations commonly expect callbacks at /auth/callback
  • Follows RESTful URL design principles

3. Security Benefits

Clear separation provides:

  • Easier application of different security policies (rate limiting on auth vs admin)
  • Clearer audit trails and access logs
  • Reduced cognitive load when reviewing security configurations
  • Better principle of least privilege implementation

4. Minimal Impact

Analysis of the codebase shows:

  • No hardcoded URLs to /admin/login in external-facing documentation
  • All internal redirects use url_for('auth.login_form') which will automatically adjust
  • Templates use named routes: url_for('auth.login_initiate'), url_for('auth.logout')
  • No stored auth_state data is tied to the URL path

5. Future Flexibility

If we later need public authentication for other features:

  • API token generation could live at /auth/tokens
  • OAuth provider functionality could use /auth/authorize
  • WebAuthn endpoints could use /auth/webauthn
  • All auth-related functionality stays organized under /auth

Consequences

Positive

  • Fixes the immediate bug: IndieAuth callbacks will work correctly
  • Cleaner architecture: Proper separation between auth and admin concerns
  • Standards alignment: Matches common OAuth/IndieAuth patterns
  • No breaking changes: All internal routes use named endpoints
  • Better organization: More intuitive URL structure

Negative

  • Documentation updates needed: Must update docs showing /admin/login paths
  • Potential user confusion: Users who bookmarked /admin/login will get 404
    • Mitigation: Could add a redirect from /admin/login to /auth/login for transition period

Migration Requirements

  • No database migrations required
  • No session invalidation needed
  • No configuration changes needed
  • Simply update the blueprint registration

Alternatives Considered

Alternative 1: Change redirect_uri to /admin/callback

Rejected because:

  • Mixes authentication concerns with administration in URL structure
  • Goes against common OAuth/IndieAuth URL patterns
  • Less intuitive - callbacks aren't "admin" functions
  • Requires changes in two places in auth.py (lines 325 and 414)

Alternative 2: Create a separate /auth blueprint just for callback

Rejected because:

  • Splits related authentication logic across multiple blueprints
  • More complex routing configuration
  • Harder to maintain - auth logic spread across files
  • Violates single responsibility principle at module level

Alternative 3: Use root-level routes (/login, /callback, /logout)

Rejected because:

  • Pollutes the root namespace
  • No logical grouping of related routes
  • Harder to apply auth-specific middleware
  • Less scalable as application grows

Alternative 4: Keep current structure and add redirect

Rejected because:

  • Doesn't fix the underlying architectural issue
  • Adds unnecessary HTTP redirect overhead
  • Makes debugging more complex
  • Band-aid solution rather than proper fix

Implementation

Required Change

Update line 30 in /home/phil/Projects/starpunk/starpunk/routes/auth.py:

# From:
bp = Blueprint("auth", __name__, url_prefix="/admin")

# To:
bp = Blueprint("auth", __name__, url_prefix="/auth")

Results

This single change will:

  • Make the callback available at /auth/callback (matching the redirect_uri)
  • Move login to /auth/login
  • Move logout to /auth/logout
  • All template references using url_for() will automatically resolve correctly

Optional Transition Support

If desired, add temporary redirects in starpunk/routes/admin.py:

@bp.route("/login")
def old_login_redirect():
    """Temporary redirect for bookmarks"""
    return redirect(url_for("auth.login_form"), 301)

Documentation Updates Required

Files to update:

  • /home/phil/Projects/starpunk/TECHNOLOGY-STACK-SUMMARY.md - Update route table
  • /home/phil/Projects/starpunk/docs/design/phase-4-web-interface.md - Update route documentation
  • /home/phil/Projects/starpunk/docs/designs/phase-5-quick-reference.md - Update admin access instructions

Testing Verification

After implementation:

  1. Verify /auth/login displays login form
  2. Verify /auth/callback accepts IndieAuth redirects
  3. Verify /auth/logout destroys session
  4. Verify all admin routes still require authentication
  5. Test full IndieAuth flow with real provider

References


Document Version: 1.0 Created: 2025-11-22 Author: StarPunk Architecture Team (agent-architect) Review Required By: agent-developer before implementation