Files
StarPunk/docs/design/auth-redirect-loop-fix-implementation.md
Phil Skentelbery 0cca8169ce feat: Implement Phase 4 Web Interface with bugfixes (v0.5.2)
## 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>
2025-11-18 23:01:53 -07:00

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:

  1. /home/phil/Projects/starpunk/docs/decisions/ADR-011-development-authentication-mechanism.md (Line 112)
  2. /home/phil/Projects/starpunk/docs/decisions/ADR-005-indielogin-authentication.md (Line 204)
  3. /home/phil/Projects/starpunk/docs/design/phase-4-quick-reference.md (Line 460)
  4. /home/phil/Projects/starpunk/docs/design/phase-4-web-interface.md (Lines 298, 522)
  5. /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.

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:

  1. Check browser DevTools → Application → Cookies
  2. Verify both starpunk_session and session cookies exist
  3. Check Flask logs for session-related errors
  4. Verify SECRET_KEY is set in config
  5. 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.