Description:
/api/admin/stats and /api/admin/users return sensitive platform data (total users, savings amounts, user list) with zero access control:
// ❌ src/app/api/admin/stats/route.ts
export async function GET() {
return NextResponse.json({ totalUsers: 1248, ... }) // no auth check
}
Any unauthenticated HTTP client can retrieve this data.
Fix:
Add a shared requireAdmin(req) middleware that:
Validates the Bearer JWT
Checks that the decoded user has an isAdmin: true claim or matches a configured admin wallet address list
Returns 401 Unauthorized or 403 Forbidden otherwise
Apply this middleware to all /api/admin/* routes
Add an ADMIN_ADDRESSES environment variable to configure the allowlist
Acceptance criteria:
Unauthenticated requests to /api/admin/* return 401
Non-admin authenticated requests return 403
Admin address allowlist is configurable via environment variable
Unit test for the middleware
Description:
/api/admin/stats and /api/admin/users return sensitive platform data (total users, savings amounts, user list) with zero access control:
// ❌ src/app/api/admin/stats/route.ts
export async function GET() {
return NextResponse.json({ totalUsers: 1248, ... }) // no auth check
}
Any unauthenticated HTTP client can retrieve this data.
Fix:
Add a shared requireAdmin(req) middleware that:
Validates the Bearer JWT
Checks that the decoded user has an isAdmin: true claim or matches a configured admin wallet address list
Returns 401 Unauthorized or 403 Forbidden otherwise
Apply this middleware to all /api/admin/* routes
Add an ADMIN_ADDRESSES environment variable to configure the allowlist
Acceptance criteria:
Unauthenticated requests to /api/admin/* return 401
Non-admin authenticated requests return 403
Admin address allowlist is configurable via environment variable
Unit test for the middleware