Route Protection
Blogify employs a multi-layered security strategy to protect user data and restrict access to private routes. This is handled through a combination of frontend component wrappers in Next.js and JWT verification in the FastAPI backend.
Frontend Route Protection
In the Next.js application, routes are protected at the component level to ensure that only authenticated users can view or interact with sensitive pages like the feed, post creation, and profile settings.
The RequireAuth Component
The primary mechanism for frontend protection is the RequireAuth higher-order component. It wraps protected page content and checks the user's session status before rendering.
Usage Example:
To protect a new page, wrap the client component content with RequireAuth.
import { RequireAuth } from '@/components';
export default function ProtectedPage() {
return (
<RequireAuth>
<main>
{/* Only visible to logged-in users */}
<h1>Your Private Dashboard</h1>
</main>
</RequireAuth>
);
}
Redirection Logic
- Unauthenticated Users: If a user attempts to access a protected route (e.g.,
/posts/new),RequireAuthredirects them to the/loginpage, often appending areturnToquery parameter to improve user experience after login. - Authenticated Users: The
LoginPageincludes a redirect hook that detects an active session and automatically moves the user to the/feedif they attempt to access/loginor/signupwhile already logged in.
Resource Ownership (Client-side)
For routes involving data modification (like /posts/[id]/edit), the application performs an additional ownership check. Even if a user is authenticated, the UI verifies that their user.id matches the user_id of the post being edited.
useEffect(() => {
getPost(id).then((post) => {
if (user && user.id !== post.user_id) {
router.push('/feed'); // Redirect unauthorized editors
}
});
}, [id, user]);
Backend API Security
While the frontend handles UI visibility, the FastAPI backend enforces hard security by verifying the Supabase JWT on every state-changing request.
Authenticated Headers
The frontend client automatically attaches the current user's session token to API requests using the authHeaders utility in lib/api.ts.
async function authHeaders(): Promise<HeadersInit> {
const { data: { session } } = await supabase.auth.getSession();
if (!session?.access_token) throw new Error('Not authenticated');
return {
'Content-Type': 'application/json',
'Authorization': `Bearer ${session.access_token}`,
};
}
FastAPI JWT Verification
On the server side, the backend uses a dependency-based approach to secure routes.
- Extraction: The API extracts the Bearer token from the
Authorizationheader. - Validation: Using
PyJWT, the backend decodes the token using theSUPABASE_JWT_SECRET. - Identification: If valid, the
subclaim (the User ID) is extracted and used to ensure the requester has permission to perform the action (e.g., deleting a post).
Protected Endpoints:
POST /posts: Requires valid JWT.PUT /posts/{id}: Requires valid JWT + ownership check.DELETE /posts/{id}: Requires valid JWT + ownership check.GET /users/me: Requires valid JWT.
Storage Protection
Images and avatars are stored in Supabase Storage. Access is controlled via Row Level Security (RLS) policies:
- Read Access: Publicly readable (using
.getPublicUrl). - Write Access: Restricted to authenticated users whose
auth.uid()matches the file path or metadata, preventing users from overwriting others' uploads.