# 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: ```python 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: ```python 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/` - Edit note - `/admin/delete/` - 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`: ```python # 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`: ```python @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 - [IndieAuth Specification](https://indieauth.spec.indieweb.org/) - Section on redirect URIs - [OAuth 2.0 RFC 6749](https://tools.ietf.org/html/rfc6749) - Section 3.1.2 on redirection endpoints - [RESTful API Design](https://restfulapi.net/resource-naming/) - URL naming conventions - Current implementation: `/home/phil/Projects/starpunk/starpunk/routes/auth.py`, `/home/phil/Projects/starpunk/starpunk/auth.py` --- **Document Version**: 1.0 **Created**: 2025-11-22 **Author**: StarPunk Architecture Team (agent-architect) **Review Required By**: agent-developer before implementation