AnotherWrapper includes several security features out of the box. This guide explains the existing security measures and shows how to implement optional rate limiting:
If you’ve followed the setup guides, Row Level Security (RLS) is already enabled and configured for all your tables, ensuring users can only access their own data.
Never disable RLS policies unless absolutely necessary.
User Ownership: Use auth.uid() to match the authenticated user with their data
Default Deny: Start with all access denied, then explicitly grant permissions
Minimal Access: Give users access only to what they absolutely need
Separate Policies: Create distinct policies for different operations (SELECT, INSERT, etc.)
Here’s an example of common RLS policies:
Copy
-- Enable RLSALTER TABLE "documents" ENABLE ROW LEVEL SECURITY;-- Users can only read their own documentsCREATE POLICY "Users can read own documents"ON "documents"FOR SELECTUSING (auth.uid() = user_id);-- Users can only insert documents they ownCREATE POLICY "Users can insert own documents"ON "documents"FOR INSERTWITH CHECK (auth.uid() = user_id);-- Users can only update their own documentsCREATE POLICY "Users can update own documents"ON "documents"FOR UPDATEUSING (auth.uid() = user_id)WITH CHECK (auth.uid() = user_id);-- Users can only delete their own documentsCREATE POLICY "Users can delete own documents"ON "documents"FOR DELETEUSING (auth.uid() = user_id);
These policies ensure that users can only access, modify, or delete their own data, while administrators can manage all records through superuser access.
All sensitive API routes in AnotherWrapper are protected using the centralized auth helpers in lib/auth/server.ts. Use requireApiUser() to protect API routes — it returns the authenticated user or throws a 401 response automatically:
app/api/protected/route.ts
Copy
import { requireApiUser } from "@/lib/auth/server";export async function GET(req: Request) { // Throws 401 if not authenticated const user = await requireApiUser(); // Your protected route logic here return Response.json({ message: "Protected data", userId: user.id });}
For server components and pages, use requireUser() instead — it redirects to /auth if the user is not authenticated:
app/protected/page.tsx
Copy
import { requireUser } from "@/lib/auth/server";export default async function ProtectedPage() { const user = await requireUser(); // Redirects to /auth if not logged in return <div>Welcome, {user.email}</div>;}
Update the following files to implement rate limiting:
Copy
import { type NextRequest, NextResponse } from "next/server";import { updateSession } from "@/lib/utils/supabase/middleware";import { Ratelimit } from "@upstash/ratelimit";import { Redis } from "@upstash/redis";// Initialize Redis clientconst redis = new Redis({ url: process.env.UPSTASH_REDIS_REST_URL!, token: process.env.UPSTASH_REDIS_REST_TOKEN!,});// Configure rate limiter (example: 3 requests per minute)const ratelimit = new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(3, "60 s"),});// Routes to rate limitconst urlsToRateLimit = [ "/api/ai/generate", "/api/uploads", // Add your rate-limited routes];export async function middleware(request: NextRequest) { if (urlsToRateLimit.some((url) => request.url.includes(url))) { const ip = request.ip ?? "127.0.0.1"; const { success } = await ratelimit.limit(ip); if (!success) { const url = new URL(request.url); return url.pathname.startsWith("/api/") ? NextResponse.json({ error: "Rate limit exceeded" }, { status: 429 }) : NextResponse.redirect(new URL("/blocked", request.url)); } } return await updateSession(request);}export const config = { matcher: [ /* * Match all request paths except for the ones starting with: * - _next/static (static files) * - _next/image (image optimization files) * - favicon.ico (favicon file) * Feel free to modify this pattern to include more paths. */ "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", ],};
The middleware handles the rate limiting logic, while the blocked page provides a user-friendly interface when limits are exceeded. Users will be redirected to this page when they hit the rate limit on non-API routes.
Protect against AI service abuse by setting hard limits in your AI service dashboards:
Always set up budget alerts and hard limits in your AI service dashboards to
prevent unexpected costs.
All AI providers (OpenAI, Replicate, Anthropic, ElevenLabs, Groq, Google, xAI) have built-in tools to regulate usage and set maximum spending limits. Make sure to configure these limits in each provider’s dashboard to prevent abuse and unexpected costs.Additionally, AnotherWrapper’s credit system provides application-level metering. Users must have sufficient credits to use AI features, and credits are consumed atomically via consume_user_credits() to prevent race conditions.