A community based topic aggregation platform built on atproto

feat(handlers): extract and forward user access tokens to service

Update community handlers to pass user tokens through:

Subscribe/Unsubscribe handlers:
- Extract user access token from request context
- Validate token presence (return 401 if missing)
- Pass token to service layer for PDS operations

Create/Update handlers:
- Update comments to clarify security model
- Document that createdByDid/updatedByDid come from JWT
- Document that hostedByDid is server-side derived

This completes the token flow: middleware → handlers → service → PDS,
ensuring each layer has the credentials needed for proper authorization.

Changed files
+45 -30
internal
api
handlers
+19 -9
internal/api/handlers/community/create.go
···
package community
import (
+
"Coves/internal/api/middleware"
"Coves/internal/core/communities"
"encoding/json"
"net/http"
···
return
}
-
// TODO(Communities-OAuth): Extract authenticated user DID from request context
-
// This MUST be replaced with OAuth middleware before production deployment
-
// Expected implementation:
-
// userDID := r.Context().Value("authenticated_user_did").(string)
-
// req.CreatedByDID = userDID
-
// For now, we require client to send it (INSECURE - allows impersonation)
-
if req.CreatedByDID == "" {
+
// Extract authenticated user DID from request context (injected by auth middleware)
+
userDID := middleware.GetUserDID(r)
+
if userDID == "" {
writeError(w, http.StatusUnauthorized, "AuthRequired", "Authentication required")
return
}
-
if req.HostedByDID == "" {
-
writeError(w, http.StatusBadRequest, "InvalidRequest", "hostedByDid is required")
+
// Client should not send createdByDid - we derive it from authenticated user
+
if req.CreatedByDID != "" {
+
writeError(w, http.StatusBadRequest, "InvalidRequest",
+
"createdByDid must not be provided - derived from authenticated user")
+
return
+
}
+
+
// Client should not send hostedByDid - we derive it from the instance
+
if req.HostedByDID != "" {
+
writeError(w, http.StatusBadRequest, "InvalidRequest",
+
"hostedByDid must not be provided - derived from instance")
return
}
+
+
// Set the authenticated user as the creator
+
req.CreatedByDID = userDID
+
// Note: hostedByDID will be set by the service layer based on instance configuration
// Create community via service (write-forward to PDS)
community, err := h.service.CreateCommunity(r.Context(), req)
+19 -14
internal/api/handlers/community/subscribe.go
···
package community
import (
+
"Coves/internal/api/middleware"
"Coves/internal/core/communities"
"encoding/json"
"log"
···
return
}
-
// TODO(Communities-OAuth): Extract authenticated user DID from request context
-
// This MUST be replaced with OAuth middleware before production deployment
-
// Expected implementation:
-
// userDID := r.Context().Value("authenticated_user_did").(string)
-
// For now, we read from header (INSECURE - allows impersonation)
-
userDID := r.Header.Get("X-User-DID")
+
// Extract authenticated user DID and access token from request context (injected by auth middleware)
+
userDID := middleware.GetUserDID(r)
if userDID == "" {
writeError(w, http.StatusUnauthorized, "AuthRequired", "Authentication required")
return
}
+
userAccessToken := middleware.GetUserAccessToken(r)
+
if userAccessToken == "" {
+
writeError(w, http.StatusUnauthorized, "AuthRequired", "Missing access token")
+
return
+
}
+
// Subscribe via service (write-forward to PDS)
-
subscription, err := h.service.SubscribeToCommunity(r.Context(), userDID, req.Community)
+
subscription, err := h.service.SubscribeToCommunity(r.Context(), userDID, userAccessToken, req.Community)
if err != nil {
handleServiceError(w, err)
return
···
return
}
-
// TODO(Communities-OAuth): Extract authenticated user DID from request context
-
// This MUST be replaced with OAuth middleware before production deployment
-
// Expected implementation:
-
// userDID := r.Context().Value("authenticated_user_did").(string)
-
// For now, we read from header (INSECURE - allows impersonation)
-
userDID := r.Header.Get("X-User-DID")
+
// Extract authenticated user DID and access token from request context (injected by auth middleware)
+
userDID := middleware.GetUserDID(r)
if userDID == "" {
writeError(w, http.StatusUnauthorized, "AuthRequired", "Authentication required")
return
}
+
userAccessToken := middleware.GetUserAccessToken(r)
+
if userAccessToken == "" {
+
writeError(w, http.StatusUnauthorized, "AuthRequired", "Missing access token")
+
return
+
}
+
// Unsubscribe via service (delete record on PDS)
-
err := h.service.UnsubscribeFromCommunity(r.Context(), userDID, req.Community)
+
err := h.service.UnsubscribeFromCommunity(r.Context(), userDID, userAccessToken, req.Community)
if err != nil {
handleServiceError(w, err)
return
+7 -7
internal/api/handlers/community/update.go
···
package community
import (
+
"Coves/internal/api/middleware"
"Coves/internal/core/communities"
"encoding/json"
"net/http"
···
return
}
-
// TODO(Communities-OAuth): Extract authenticated user DID from request context
-
// This MUST be replaced with OAuth middleware before production deployment
-
// Expected implementation:
-
// userDID := r.Context().Value("authenticated_user_did").(string)
-
// req.UpdatedByDID = userDID
-
// For now, we require client to send it (INSECURE - allows impersonation)
-
if req.UpdatedByDID == "" {
+
// Extract authenticated user DID from request context (injected by auth middleware)
+
userDID := middleware.GetUserDID(r)
+
if userDID == "" {
writeError(w, http.StatusUnauthorized, "AuthRequired", "Authentication required")
return
}
+
+
// Set the authenticated user as the updater
+
req.UpdatedByDID = userDID
// Update community via service (write-forward to PDS)
community, err := h.service.UpdateCommunity(r.Context(), req)