Skip to content

Commit d521ef6

Browse files
feat: GAP-42 - Implement authentication middleware on agent endpoints
- Remove user_id query parameters from agent API endpoints - Use require_auth and get_current_user dependencies - Extract user identity from authenticated session tokens - Update /proposals/pending/* endpoints to use current user - Prevent user impersonation attacks Security: Users can no longer impersonate others by changing URL parameters
1 parent 54b30db commit d521ef6

2 files changed

Lines changed: 38 additions & 16 deletions

File tree

app/api/agents.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,17 @@ async def list_proposals(
8080
agent_name: Optional[str] = Query(None, description="Filter by agent name"),
8181
proposal_type: Optional[ProposalType] = Query(None, description="Filter by proposal type"),
8282
status: Optional[ProposalStatus] = Query(None, description="Filter by status"),
83-
user_id: Optional[str] = Query(None, description="Filter by user (proposals requiring approval)"),
83+
current_user: Optional[User] = Depends(get_current_user),
8484
) -> ProposalListResponse:
8585
"""
8686
List proposals with optional filters.
8787
8888
Returns all proposals matching the filter criteria.
89+
GAP-42: Now uses authenticated user instead of user_id query parameter.
8990
"""
91+
# Use current_user.id if authenticated, otherwise None (public view)
92+
user_id = current_user.id if current_user else None
93+
9094
filter_obj = ProposalFilter(
9195
agent_name=agent_name,
9296
proposal_type=proposal_type,
@@ -102,32 +106,38 @@ async def list_proposals(
102106
)
103107

104108

105-
@router.get("/proposals/pending/{user_id}")
106-
async def get_pending_approvals(user_id: str) -> ProposalListResponse:
109+
@router.get("/proposals/pending")
110+
async def get_pending_approvals(
111+
current_user: User = Depends(require_auth),
112+
) -> ProposalListResponse:
107113
"""
108-
Get proposals pending approval from a specific user.
114+
Get proposals pending approval from the current user.
109115
110116
Returns proposals awaiting this user's decision.
117+
GAP-42: Now requires authentication and uses current user.
111118
"""
112-
proposals = await approval_tracker.get_pending_approvals(user_id)
119+
proposals = await approval_tracker.get_pending_approvals(current_user.id)
113120

114121
return ProposalListResponse(
115122
proposals=proposals,
116123
total=len(proposals),
117124
)
118125

119126

120-
@router.get("/proposals/pending/{user_id}/count")
121-
async def get_pending_count(user_id: str) -> Dict[str, int]:
127+
@router.get("/proposals/pending/count")
128+
async def get_pending_count(
129+
current_user: User = Depends(require_auth),
130+
) -> Dict[str, int]:
122131
"""
123-
Get count of proposals pending approval from a specific user.
132+
Get count of proposals pending approval from the current user.
124133
125134
Returns just the number - useful for badges and notifications.
135+
GAP-42: Now requires authentication and uses current user.
126136
"""
127-
proposals = await approval_tracker.get_pending_approvals(user_id)
137+
proposals = await approval_tracker.get_pending_approvals(current_user.id)
128138

129139
return {
130-
"user_id": user_id,
140+
"user_id": current_user.id,
131141
"pending_count": len(proposals),
132142
}
133143

openspec/changes/gap-42-authentication-system/proposal.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# GAP-42: No Authentication System (SECURITY)
22

3-
**Status**: Draft
3+
**Status**: ✅ IMPLEMENTED
44
**Priority**: P6 - Production/Security
55
**Severity**: CRITICAL
66
**Estimated Effort**: See GAP-02
7-
**Assigned**: Unclaimed
7+
**Assigned**: Claude Agent
8+
**Completed**: December 19, 2025
89

910
## Problem Statement
1011

@@ -65,10 +66,21 @@ Once GAP-02 is implemented:
6566

6667
## Success Criteria
6768

68-
- [ ] No endpoints accept `user_id` as parameter
69-
- [ ] All protected endpoints require authentication
70-
- [ ] Users cannot impersonate others
71-
- [ ] GAP-02 proposal is implemented
69+
- [x] No endpoints accept `user_id` as parameter
70+
- [x] All protected endpoints require authentication
71+
- [x] Users cannot impersonate others
72+
- [x] GAP-02 proposal is implemented
73+
74+
## Implementation Notes
75+
76+
All agent API endpoints have been updated to use the authentication middleware:
77+
- `GET /agents/proposals` - Uses `get_current_user` for optional auth
78+
- `GET /agents/proposals/pending` - Now requires auth via `require_auth`
79+
- `GET /agents/proposals/pending/count` - Now requires auth via `require_auth`
80+
- `POST /agents/proposals/{id}/approve` - Already had `require_auth`
81+
- `POST /agents/proposals/{id}/reject` - Already had `require_auth`
82+
83+
User identity now comes from the authenticated session token, not from untrusted query parameters.
7284

7385
## References
7486

0 commit comments

Comments
 (0)