## Phase 4: Web Interface Implementation Implemented complete web interface with public and admin routes, templates, CSS, and development authentication. ### Core Features **Public Routes**: - Homepage with recent published notes - Note permalinks with microformats2 - Server-side rendering (Jinja2) **Admin Routes**: - Login via IndieLogin - Dashboard with note management - Create, edit, delete notes - Protected with @require_auth decorator **Development Authentication**: - Dev login bypass for local testing (DEV_MODE only) - Security safeguards per ADR-011 - Returns 404 when disabled **Templates & Frontend**: - Base layouts (public + admin) - 8 HTML templates with microformats2 - Custom responsive CSS (114 lines) - Error pages (404, 500) ### Bugfixes (v0.5.1 → v0.5.2) 1. **Cookie collision fix (v0.5.1)**: - Renamed auth cookie from "session" to "starpunk_session" - Fixed redirect loop between dev login and admin dashboard - Flask's session cookie no longer conflicts with auth 2. **HTTP 404 error handling (v0.5.1)**: - Update route now returns 404 for nonexistent notes - Delete route now returns 404 for nonexistent notes - Follows ADR-012 HTTP Error Handling Policy - Pattern consistency across all admin routes 3. **Note model enhancement (v0.5.2)**: - Exposed deleted_at field from database schema - Enables soft deletion verification in tests - Follows ADR-013 transparency principle ### Architecture **New ADRs**: - ADR-011: Development Authentication Mechanism - ADR-012: HTTP Error Handling Policy - ADR-013: Expose deleted_at Field in Note Model **Standards Compliance**: - Uses uv for Python environment - Black formatted, Flake8 clean - Follows git branching strategy - Version incremented per versioning strategy ### Test Results - 405/406 tests passing (99.75%) - 87% code coverage - All security tests passing - Manual testing confirmed working ### Documentation - Complete implementation reports in docs/reports/ - Architecture reviews in docs/reviews/ - Design documents in docs/design/ - CHANGELOG updated for v0.5.2 ### Files Changed **New Modules**: - starpunk/dev_auth.py - starpunk/routes/ (public, admin, auth, dev_auth) **Templates**: 10 files (base, pages, admin, errors) **Static**: CSS and optional JavaScript **Tests**: 4 test files for routes and templates **Docs**: 20+ architectural and implementation documents 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
13 KiB
Implementation Guide: Auth Redirect Loop Fix
Date: 2025-11-18 Related: auth-redirect-loop-diagnosis.md Assignee: Developer Agent Priority: CRITICAL
Quick Summary
Change all authentication cookie references from "session" to "starpunk_session" to avoid collision with Flask's server-side session mechanism.
Estimated Time: 30 minutes Files to Change: 5 production files + test files
Root Cause (Brief)
Flask's session object (used by flash() and session["next"]) writes to a cookie named session. StarPunk's auth also uses a cookie named session. This creates a collision where Flask overwrites the auth token, causing the redirect loop.
Solution: Rename StarPunk's auth cookie to starpunk_session.
Implementation Checklist
Phase 1: Production Code Changes
1. /home/phil/Projects/starpunk/starpunk/routes/dev_auth.py
Line 75 - Change cookie name when setting:
# OLD (Line 74-81):
response.set_cookie(
"session",
session_token,
httponly=True,
secure=False,
samesite="Lax",
max_age=30 * 24 * 60 * 60,
)
# NEW:
response.set_cookie(
"starpunk_session", # ← CHANGED
session_token,
httponly=True,
secure=False,
samesite="Lax",
max_age=30 * 24 * 60 * 60,
)
2. /home/phil/Projects/starpunk/starpunk/routes/auth.py
Line 47 - Change cookie read in login form check:
# OLD:
session_token = request.cookies.get("session")
# NEW:
session_token = request.cookies.get("starpunk_session")
Line 121 - Change cookie name when setting after IndieAuth callback:
# OLD (Lines 120-127):
response.set_cookie(
"session",
session_token,
httponly=True,
secure=secure,
samesite="Lax",
max_age=30 * 24 * 60 * 60,
)
# NEW:
response.set_cookie(
"starpunk_session", # ← CHANGED
session_token,
httponly=True,
secure=secure,
samesite="Lax",
max_age=30 * 24 * 60 * 60,
)
Line 167 - Change cookie read in logout:
# OLD:
session_token = request.cookies.get("session")
# NEW:
session_token = request.cookies.get("starpunk_session")
Line 178 - Change cookie delete in logout:
# OLD:
response.delete_cookie("session")
# NEW:
response.delete_cookie("starpunk_session")
3. /home/phil/Projects/starpunk/starpunk/auth.py
Line 390 - Change cookie read in @require_auth decorator:
# OLD:
session_token = request.cookies.get("session")
# NEW:
session_token = request.cookies.get("starpunk_session")
Phase 2: Test Code Changes
4. /home/phil/Projects/starpunk/tests/test_routes_admin.py
Line 54 - Change test cookie name:
# OLD:
client.set_cookie("session", session_token)
# NEW:
client.set_cookie("starpunk_session", session_token)
5. /home/phil/Projects/starpunk/tests/test_templates.py
Lines 234, 247, 259, 272 - Change all test cookie names:
# OLD (appears 4 times):
client.set_cookie("session", token)
# NEW (all 4 instances):
client.set_cookie("starpunk_session", token)
Phase 3: Documentation Updates
Update the following documentation files to reflect the new cookie name:
/home/phil/Projects/starpunk/docs/decisions/ADR-011-development-authentication-mechanism.md(Line 112)/home/phil/Projects/starpunk/docs/decisions/ADR-005-indielogin-authentication.md(Line 204)/home/phil/Projects/starpunk/docs/design/phase-4-quick-reference.md(Line 460)/home/phil/Projects/starpunk/docs/design/phase-4-web-interface.md(Lines 298, 522)/home/phil/Projects/starpunk/docs/design/phase-3-authentication-implementation.md(Line 313)
Note: These are documentation files, so changes are for accuracy but not critical for functionality.
Complete File Change Summary
Production Code (5 changes across 3 files)
| File | Line | Change Type | Old Value | New Value |
|---|---|---|---|---|
starpunk/routes/dev_auth.py |
75 | set_cookie name | "session" |
"starpunk_session" |
starpunk/routes/auth.py |
47 | cookies.get | "session" |
"starpunk_session" |
starpunk/routes/auth.py |
121 | set_cookie name | "session" |
"starpunk_session" |
starpunk/routes/auth.py |
167 | cookies.get | "session" |
"starpunk_session" |
starpunk/routes/auth.py |
178 | delete_cookie | "session" |
"starpunk_session" |
starpunk/auth.py |
390 | cookies.get | "session" |
"starpunk_session" |
Test Code (5 changes across 2 files)
| File | Line(s) | Change Type |
|---|---|---|
tests/test_routes_admin.py |
54 | client.set_cookie |
tests/test_templates.py |
234, 247, 259, 272 | client.set_cookie (4 instances) |
Search and Replace Strategy
IMPORTANT: Do NOT use global search and replace. Many documentation files reference the word "session" legitimately.
Recommended Approach
Use targeted search patterns:
# Find all set_cookie calls with "session"
grep -n 'set_cookie.*"session"' starpunk/**/*.py tests/**/*.py
# Find all cookies.get calls with "session"
grep -n 'cookies\.get.*"session"' starpunk/**/*.py tests/**/*.py
# Find all delete_cookie calls with "session"
grep -n 'delete_cookie.*"session"' starpunk/**/*.py tests/**/*.py
Then manually review and update each instance.
Testing Plan
Automated Tests
After making changes, run the test suite:
uv run pytest tests/ -v
Expected: All existing tests should pass with the new cookie name.
Manual Testing (CRITICAL)
Test 1: Dev Login Flow
1. Start server: uv run flask run
2. Open browser: http://localhost:5000/admin/
3. Expected: Redirect to /admin/login
4. Click "Dev Login" link (or visit http://localhost:5000/dev/login)
5. Expected: Redirect to /admin/ dashboard
6. Expected: See flash message "DEV MODE: Logged in without authentication"
7. Expected: Dashboard loads successfully (NO redirect loop)
Success Criteria:
- No redirect loop
- Flash message appears
- Dashboard displays
Browser DevTools Check:
Application → Cookies → http://localhost:5000
Should see:
- starpunk_session: {long-token-string}
- session: {flask-session-data} (for flash messages)
Test 2: Session Persistence
1. After successful login from Test 1
2. Click "New Note" in navigation
3. Expected: Form loads (no redirect to login)
4. Refresh page (F5)
5. Expected: Still authenticated, form still loads
Success Criteria:
- No authentication loss on navigation
- No authentication loss on refresh
Test 3: Logout
1. While authenticated, click "Logout" button
2. Expected: Redirect to homepage
3. Expected: Flash message "Logged out successfully"
4. Try to visit http://localhost:5000/admin/
5. Expected: Redirect to /admin/login
Browser DevTools Check:
Application → Cookies → http://localhost:5000
Should see:
- starpunk_session: (should be deleted)
- session: {may still exist for flash message}
Success Criteria:
- Cookie properly cleared
- Cannot access admin routes after logout
- Must login again to access admin
Test 4: IndieAuth Flow (if configured)
1. Logout if logged in
2. Visit /admin/login
3. Enter valid ADMIN_ME URL
4. Complete IndieAuth flow on indielogin.com
5. Expected: Redirect back to dashboard
6. Expected: starpunk_session cookie set
7. Expected: No redirect loop
Success Criteria:
- Full IndieAuth flow works
- Session persists after callback
- Flash message shows
Post-Implementation
1. Version Bump
Update version to 0.5.1 (bugfix release):
# In starpunk/config.py or wherever VERSION is defined
app.config["VERSION"] = "0.5.1"
Also update in:
pyproject.toml(if version is defined there)docs/CHANGELOG.md
2. Changelog Entry
Add to /home/phil/Projects/starpunk/docs/CHANGELOG.md:
## [0.5.1] - 2025-11-18
### Fixed
- **CRITICAL**: Fixed authentication redirect loop caused by cookie name collision between Flask's session and StarPunk's auth token
- Renamed authentication cookie from `session` to `starpunk_session` to avoid conflict with Flask's server-side session mechanism
- All authentication flows (dev login, IndieAuth, logout) now work correctly without redirect loops
### Changed
- Authentication cookie name changed from `session` to `starpunk_session` (breaking change for existing sessions - users will need to re-login)
3. Update Standards Document
Create or update /home/phil/Projects/starpunk/docs/standards/cookie-naming-convention.md:
# Cookie Naming Convention
**Status**: ACTIVE
**Date**: 2025-11-18
## Standard
All StarPunk application cookies MUST use the `starpunk_` prefix to avoid conflicts with framework-reserved names.
## Reserved Names (DO NOT USE)
- `session` - Reserved for Flask server-side session
- `csrf_token` - Reserved for CSRF protection frameworks
- `remember_token` - Common auth framework name
- Any single-word generic names
## StarPunk Cookie Names
| Cookie Name | Purpose | Security Attributes |
|-------------|---------|---------------------|
| `starpunk_session` | Authentication session token | HttpOnly, Secure (prod), SameSite=Lax |
## Future Cookies
All future cookies must:
1. Use `starpunk_` prefix
2. Be documented in this table
3. Have explicit security attributes defined
4. Be reviewed for conflicts with framework conventions
4. Create Report
Create /home/phil/Projects/starpunk/docs/reports/2025-11-18-auth-redirect-loop-fix.md:
# Auth Redirect Loop Fix - Implementation Report
**Date**: 2025-11-18
**Version**: 0.5.1
**Severity**: Critical Bug Fix
## Summary
Fixed authentication redirect loop in Phase 4 by renaming authentication cookie from `session` to `starpunk_session`.
## Root Cause
Cookie name collision between Flask's server-side session (used by flash messages) and StarPunk's authentication token.
## Implementation
- Changed 6 instances in production code
- Changed 5 instances in test code
- Updated 6 documentation files
- All tests passing
- Manual testing confirmed fix
## Testing
- Dev login flow: PASS
- Session persistence: PASS
- Logout flow: PASS
- IndieAuth flow: PASS (if applicable)
## Breaking Change
Existing authenticated users will be logged out and need to re-authenticate due to cookie name change.
## Prevention
Established cookie naming convention (starpunk_* prefix) to prevent future conflicts.
## Files Changed
[List all files modified]
## Commit
[Reference commit hash after git commit]
5. Git Commit
After all changes and testing:
# Stage all changes
git add starpunk/routes/dev_auth.py \
starpunk/routes/auth.py \
starpunk/auth.py \
tests/test_routes_admin.py \
tests/test_templates.py \
docs/
# Commit with proper message
git commit -m "$(cat <<'EOF'
Fix critical auth redirect loop by renaming session cookie
BREAKING CHANGE: Authentication cookie renamed from 'session' to 'starpunk_session'
Root cause: Cookie name collision between Flask's server-side session
(used by flash messages) and StarPunk's authentication token caused
redirect loop between /dev/login and /admin/ routes.
Changes:
- Rename auth cookie to 'starpunk_session' in all routes
- Update all cookie read/write operations
- Update test suite with new cookie name
- Establish cookie naming convention (starpunk_* prefix)
- Update documentation to reflect changes
Impact:
- Existing authenticated users will be logged out
- Users must re-authenticate after upgrade
Testing:
- All automated tests passing
- Manual testing confirms fix:
- Dev login flow works without redirect loop
- Session persistence across requests
- Logout properly clears cookie
- Flash messages work correctly
Fixes: Phase 4 authentication redirect loop
Version: 0.5.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)"
Verification Checklist
Before marking this as complete:
- All 6 production code changes made
- All 5 test code changes made
- Test suite passes:
uv run pytest tests/ -v - Manual Test 1 (Dev Login) passes
- Manual Test 2 (Session Persistence) passes
- Manual Test 3 (Logout) passes
- Manual Test 4 (IndieAuth) passes or N/A
- Version bumped to 0.5.1
- CHANGELOG.md updated
- Cookie naming convention documented
- Implementation report created
- Git commit created with proper message
- No redirect loop observed in any test
- Flash messages still work
Rollback Plan
If issues are discovered:
# Revert the commit
git revert HEAD
# Or reset if not pushed
git reset --hard HEAD~1
The old behavior will be restored, but the redirect loop will return.
Support
If you encounter issues during implementation:
- Check browser DevTools → Application → Cookies
- Verify both
starpunk_sessionandsessioncookies exist - Check Flask logs for session-related errors
- Verify SECRET_KEY is set in config
- Ensure all 6 production file changes were made correctly
Architecture Notes
This fix establishes an important principle:
Never use generic cookie names that conflict with framework conventions.
Flask owns the session cookie namespace. We must respect framework boundaries and use our own namespace (starpunk_*).
This is now codified in /docs/standards/cookie-naming-convention.md for future reference.