# 403 Error Analysis - Invited Users Cannot Access Band Resources ## 🚨 **CRITICAL ISSUE IDENTIFIED** ### **The Problem** Invited users are getting 403 Forbidden errors when trying to: 1. Access band invites: `GET /api/v1/bands/{band_id}/invites` 2. Stream audio versions: `GET /api/v1/versions/{version_id}/stream` ### **Root Cause Found** ## 🔍 **Code Investigation Results** ### 1. Invite Acceptance Flow (✅ WORKING) **File:** `api/src/rehearsalhub/routers/members.py` (lines 86-120) ```python @router.post("/invites/{token}/accept", response_model=BandMemberRead) async def accept_invite(token: str, ...): # 1. Get invite by token invite = await repo.get_invite_by_token(token) # 2. Validate invite (not used, not expired) if invite.used_at: raise 409 if invite.expires_at < now: raise 410 # 3. Check if already member (idempotent) existing_role = await repo.get_member_role(invite.band_id, current_member.id) if existing_role: raise 409 # 4. ✅ Add member to band (THIS WORKS) bm = await repo.add_member(invite.band_id, current_member.id, role=invite.role) # 5. ✅ Mark invite as used (THIS WORKS) invite.used_at = datetime.now(timezone.utc) invite.used_by = current_member.id return BandMemberRead(...) ``` **✅ The invite acceptance logic is CORRECT and should work!** ### 2. Band Invites Endpoint (❌ PROBLEM FOUND) **File:** `api/src/rehearsalhub/routers/bands.py` (lines 19-70) ```python @router.get("/{band_id}/invites", response_model=BandInviteList) async def list_invites(band_id: uuid.UUID, ...): # ❌ PROBLEM: Only ADMINS can list invites! role = await repo.get_member_role(band_id, current_member.id) if role != "admin": # ← THIS IS THE BUG! raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Admin role required to manage invites" ) # Get invites... ``` **❌ BUG FOUND:** The `/bands/{band_id}/invites` endpoint requires **ADMIN** role! But **regular members** should be able to see invites for bands they're in! ### 3. Audio Stream Endpoint (❌ PROBLEM FOUND) **File:** `api/src/rehearsalhub/routers/versions.py` (lines 208-215) ```python async def _get_version_and_assert_band_membership(version_id, session, current_member): # ... get version and song ... # ❌ PROBLEM: Uses assert_membership which should work band_svc = BandService(session) try: await band_svc.assert_membership(song.band_id, current_member.id) except PermissionError: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a member") ``` **❌ BUG FOUND:** The `/versions/{version_id}/stream` endpoint uses `assert_membership` which **should** work for regular members. But if the user wasn't properly added to `band_members`, this will fail! ## 🎯 **THE ROOT CAUSE** ### **Hypothesis 1: Invite Acceptance Failed** - User accepted invite but wasn't added to `band_members` - Need to check database ### **Hypothesis 2: Permission Logic Too Strict** - `/bands/{id}/invites` requires admin (should allow members) - This is definitely a bug ### **Hypothesis 3: JWT Token Issue** - User's JWT doesn't reflect their new membership - Token needs to be refreshed after invite acceptance ## ✅ **CONFIRMED BUGS** ### **Bug #1: List Invites Requires Admin (SHOULD BE MEMBER)** **File:** `api/src/rehearsalhub/routers/bands.py:33` ```python # CURRENT (WRONG): if role != "admin": raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Admin role required") # FIXED (CORRECT): if role is None: raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Not a member") ``` ### **Bug #2: Invite Acceptance Might Not Work** Need to verify: 1. Database shows user in `band_members` 2. JWT token was refreshed 3. No errors in invite acceptance flow ## 🛠️ **RECOMMENDED FIXES** ### **Fix #1: Change Permission for List Invites** ```python # In api/src/rehearsalhub/routers/bands.py async def list_invites(band_id: uuid.UUID, ...): # Change from admin-only to member-only role = await repo.get_member_role(band_id, current_member.id) if role is None: # ← Changed from != "admin" raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not a member of this band" ) ``` ### **Fix #2: Verify Invite Acceptance** ```sql -- Check if user is in band_members SELECT * FROM band_members WHERE band_id = '96c11cfa-d6bb-4987-af80-845626880383' AND member_id = '{user_id}'; -- Check invite status SELECT * FROM band_invites WHERE band_id = '96c11cfa-d6bb-4987-af80-845626880383' AND used_by = '{user_id}'; ``` ### **Fix #3: Add Debug Logging** ```python # In accept_invite endpoint log.info(f"User {current_member.id} accepting invite to band {invite.band_id}") log.info(f"Adding member with role: {invite.role}") log.info(f"Invite marked as used at {datetime.now(timezone.utc)}") ``` ## 📋 **ACTION PLAN** ### **Step 1: Fix List Invites Permission** - Change `role != "admin"` to `role is None` - Test with regular member account ### **Step 2: Verify Database State** - Check `band_members` table - Check `band_invites` table - Verify user was added correctly ### **Step 3: Test Invite Flow** - Create new invite - Accept as test user - Verify user can access band resources ### **Step 4: Deploy Fix** - Apply permission fix - Add logging - Monitor for issues ## 🎯 **IMPACT** **Current:** Invited users cannot access band resources (403 errors) **After Fix:** Regular band members can see invites and access recordings **Files to Change:** - `api/src/rehearsalhub/routers/bands.py` (line 33) **Estimated Time:** 15-30 minutes to fix and test