Authentication
Secure user sessions, roles, and permissions—pre-configured using Better Auth.
So, we're running Better Auth with a Drizzle adapter. It's a solid, production ready setup. We've got Email & Password sign in, session management, and Role Based Access Control (RBAC) ready to rock.
Pre-Built Pages
Good news: the core authentication pages are already done and integrated into the router. You don't need to build them from scratch.
- Sign In
- Sign Up
- Forgot Password
- Sign Out
They're fully functional and wired up to the authentication client, so you can focus on building the actual application.
User Auth & Sessions
The Middleware
The main guard for protected routes is the isAuthenticated middleware. It's simple:
- It checks for a valid user and token in the session context.
- If it fails the check, it automatically bounces the user to
/sign-in(that'sLinks.signIn). - If it passes, it stuffs the
userandtokeninto the route context so your server functions can use them.
Session Helpers
All the session stuff is in src/features/auth/session.ts, which sits on top of the main session store (@/lib/session).
| Task | Core Function |
|---|---|
| Get current user/token | useAppSession() / getSessionUserOrRedirect |
| Save/Update session data | saveSessionUser(data) |
| Clear session (Log Out) | removeSessionUser() |
Roles & Permissions (RBAC)
You'll mainly use the hasPermission middleware or the verifyPermission server action to check access.
hasPermission(permission): This is a TanStack Router middleware. It runs before the route/handler executes, checks the user's permissions, and sets anisPermitted: booleanflag in the context.withPermission(context, handler): A simple wrapper. It runs a securehandlerfunction only ifcontext.isPermittedis true.
Look at this: Any secure server action, like
getRoles, uses this pattern:
.middleware([isAuthenticated, hasPermission({user: ["read"]})]).handler(async ({ context }) => withPermission(context, () => { /* safe action code goes here */ }))