@fluixi/auth
v1.0.0-alpha.57
Published
Server-first auth for Fluixi — native bindings over Better Auth: authMiddleware, requireUser/Role/Permission (request-context, no token parsing), SSR-seeded useUser/useSession hooks, and protectedRoute.
Readme
@fluixi/auth
Server-first auth for Fluixi — native bindings over Better Auth. Better Auth owns the security primitives (password hashing, sessions, CSRF, secure cookies, OAuth, 2FA/TOTP, organizations, RBAC, DB adapters — audited and maintained). This package owns the Fluixi integration Better Auth can't provide: request-context server helpers, SSR-seeded reactive hooks, middleware wiring, and route protection.
better-auth is a peer dependency — the bindings consume it structurally, so the app owns the version.
Server (@fluixi/auth/server)
// auth.config.ts
import { betterAuth } from 'better-auth';
import { defineAuth } from '@fluixi/auth/server';
export const auth = defineAuth(betterAuth({ /* db, providers, plugins */ }));// src/middleware.ts — resolve the session once per request → locals.auth
import { defineMiddleware } from '@fluixi/start';
import { authMiddleware } from '@fluixi/auth/server';
export default defineMiddleware([ authMiddleware() ]);// routes/api/auth/[...all].ts — mount Better Auth
import { mountAuthRoutes } from '@fluixi/auth/server';
export const ALL = mountAuthRoutes(); // (request) => auth.handler(request)// loaders / server functions — no token parsing, reads request-scoped locals
const user = await requireUser(); // throws AuthError(401, '/login')
const admin = await requireRole('admin'); // throws AuthError(403)
await requirePermission(() => can(user, 'edit', post));
const maybe = await optionalUser(); // U | nullThe app entry must forward the request so middleware locals reach the render:
export const render = (url, request) => renderRequestAsync(App, { url, request }).
Client (@fluixi/auth/client)
import { createAuthClient } from 'better-auth/client';
import { createAuthHooks } from '@fluixi/auth/client';
const authClient = createAuthClient({ baseURL: '/api/auth' });
export const { useUser, useSession, useAuth, useSignIn, useSignOut, protectedRoute } =
createAuthHooks({ client: authClient });const user = useUser(); // reactive accessor, SEEDED from SSR (no flash/refetch)
const signIn = useSignIn();
export default protectedRoute(() => <Dashboard/>); // authed only
export default protectedRoute(Login, { guestOnly: true }); // redirect authed away
export default protectedRoute(Admin, { role: 'admin' }); // role-gatedThe session is a createIsomorphicStore — resolved on the server from locals.auth,
serialized into the page, and seeded synchronously on the client. No auth flash, no
post-hydration refetch. @fluixi/auth/client never imports the server engine or Node.
