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>
178 lines
6.8 KiB
Markdown
178 lines
6.8 KiB
Markdown
# 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/<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`:
|
|
```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 |