Protected Routes
Relevant source files
The following files were used as context for generating this wiki page:The protected routes system in the Adfluens web application is responsible for gating access to specific parts of the application, ensuring that only authenticated users can view sensitive content and features. This is achieved through a combination of client-side routing logic using React Router and a server-side authentication-aware API built with tRPC.
The core of this system is the ProtectedRoute component, which wraps application routes that require a valid user session. It leverages a React Context, AuthProvider, to manage and provide authentication state throughout the application. Unauthenticated users attempting to access a protected route are automatically redirected to the /sign-in page. On the backend, tRPC procedures are secured using a middleware that verifies the user's session on every protected API call.
Frontend Architecture
The client-side protection is managed by a hierarchy of React components and providers that work together to control access to routes.
Sources: apps/web/src/app.tsx, apps/web/src/shared/components/protected-route.component.tsx, apps/web/src/lib/auth.provider.tsx
Provider Hierarchy
The application's entry point, App, wraps all components within a series of providers. The AuthProvider is crucial for the protected routes system as it makes the authentication state available to all child components, including the ProtectedRoute component.
This diagram shows the provider hierarchy defined in app.tsx.
Sources: apps/web/src/app.tsx:40-67
Routing Configuration
The application's routes are defined in app.tsx using react-router-dom. Routes are split into two categories: public and protected.
- Public Routes:
/sign-inand/sign-upare accessible to everyone and render the respective authentication views. - Protected Routes: All other routes (e.g.,
/,/search,/video/:id) are nested within a parentRouteelement that usesProtectedRouteas itselement. This ensures that any attempt to access these nested routes is first validated by theProtectedRoutecomponent.
// apps/web/src/app.tsx:50-63
<Routes>
{/* Public auth routes - outside main layout */}
<Route path="/sign-in" element={<SignInView />} />
<Route path="/sign-up" element={<SignUpView />} />
{/* Protected routes - require authentication */}
<Route element={<ProtectedRoute />}>
<Route element={<AppLayout />}>
<Route path="/" element={<ChannelAnalyzerView />} />
<Route path="/search" element={<VideoSearchView />} />
<Route path="/video/:id" element={<VideoDetailView />} />
</Route>
</Route>
</Routes>
Sources: apps/web/src/app.tsx:50-63
ProtectedRoute Component
This is the central component for client-side route protection. It consumes the useAuth hook to access the authentication state.
Its logic is as follows:
- If the authentication state is still being determined (
isLoadingis true), it displays a loading spinner. - If the user is not authenticated (
isAuthenticatedis false), it redirects the user to the/sign-inpage. It also passes the user's original intended location in the state, allowing for a redirect back to that page after a successful login. - If the user is authenticated, it renders the child routes via the
<Outlet />component from React Router.
This flowchart illustrates the decision-making process within the ProtectedRoute component.
AuthProvider and useAuth Hook
The AuthProvider is a React Context provider that manages the session state. It is not directly visible in the provided files, but its usage via the useAuth hook is central. The useAuth hook exposes the following state and functions:
| Property | Type | Description |
|---|---|---|
isAuthenticated | boolean | true if the user has a valid session. |
isLoading | boolean | true while the session is being checked on initial load. |
refetchSession | () => Promise<void> | A function to manually re-check and update the session state. |
This hook is used by ProtectedRoute to make access decisions and by authentication views like SignUpView to update the session after a user signs up.
Sources: apps/web/src/shared/components/protected-route.component.tsx:13, apps/web/src/features/auth/views/sign-up.view.tsx:13,34
Backend Architecture (tRPC)
On the backend, API endpoints are protected at the procedure level using tRPC middleware. This ensures that even if a user bypasses client-side checks, they cannot access or mutate data without a valid session.
Sources: apps/api/src/trpc/init.ts, apps/api/src/features/ai/ai.router.ts
tRPC Context Creation
For every incoming API request, a createContext function is executed. This function uses auth.api.getSession from the better-auth library to validate the session from the request headers. The resulting user and session data (or null if invalid) are attached to the tRPC context (ctx).
Sources: apps/api/src/trpc/init.ts:47-63
Protected Procedures
The file apps/api/src/trpc/init.ts defines a reusable protectedProcedure. This is constructed by applying an isAuthenticated middleware to a standard tRPC procedure.
The isAuthenticated middleware performs the following check:
- If
ctx.sessionorctx.userisnull, it throws aTRPCErrorwith the codeUNAUTHORIZED. - If a session is present, it allows the procedure's resolver function to execute.
This mechanism is used to protect any tRPC router that requires authentication, such as the aiRouter.
// apps/api/src/trpc/init.ts:85-96
const isAuthenticated = t.middleware(async ({ ctx, next }) => {
if (!ctx.session || !ctx.user) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You must be logged in to access this resource",
});
}
return next({
ctx: {
user: ctx.user,
session: ctx.session,
},
});
});
// apps/api/src/trpc/init.ts:102
export const protectedProcedure = t.procedure.use(isAuthenticated);
Any router can then use this protectedProcedure to secure its endpoints.
// apps/api/src/features/ai/ai.router.ts:15-24
export const aiRouter = router({
analyze: protectedProcedure
.input(
z.object({
video: youtubeVideoSchema,
comments: z.array(youtubeCommentSchema).optional().default([]),
})
)
.mutation(async ({ input }) => {
// ... logic here is protected
}),
//...
});
Sources: apps/api/src/trpc/init.ts:85-102, apps/api/src/features/ai/ai.router.ts:15-16
Full Authentication Flow
This sequence diagram illustrates the end-to-end process when an unauthenticated user attempts to access a protected resource.
Summary
The application employs a dual-layer protection strategy for its routes and data. On the frontend, react-router-dom combined with the custom ProtectedRoute component and AuthProvider context ensures a seamless user experience by guarding pages and redirecting unauthenticated users. On the backend, tRPC's middleware system provides a robust, type-safe mechanism to secure individual API procedures, guaranteeing that data access is strictly controlled based on a valid user session. This separation of concerns creates a secure and maintainable authentication architecture.