← Back to list

auth
by oakoss
Open-source SaaS starter kit with React, TanStack, and Better Auth
⭐ 0🍴 0📅 Jan 26, 2026
SKILL.md
name: auth description: Better Auth authentication. Use for auth, login, logout, session, user, signup, register, protect, middleware, password, oauth, social
For advanced patterns, OAuth configuration, and session middleware, see reference.md.
Better Auth
Package Structure
packages/auth/
├── src/
│ ├── client.ts # Auth client (React hooks)
│ ├── server.ts # Auth server (Better Auth config)
│ └── index.ts # Public exports
└── package.json
Server Configuration
// packages/auth/src/server.ts
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
import { betterAuth } from 'better-auth';
import { tanstackStartCookies } from 'better-auth/tanstack-start';
import { db } from '@oakoss/database';
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: 'pg',
usePlural: true, // Uses 'users', 'sessions', etc.
}),
emailAndPassword: { enabled: true },
plugins: [tanstackStartCookies()], // Must be last plugin in array
});
Client Configuration
// packages/auth/src/client.ts
import { createAuthClient } from 'better-auth/client';
import { inferAdditionalFields } from 'better-auth/client/plugins';
import { type auth } from '@oakoss/auth/server';
export const authClient = createAuthClient({
baseURL: process.env.PUBLIC_APP_URL ?? 'http://localhost:3000',
plugins: [inferAdditionalFields<typeof auth>()],
});
API Route Handler
// apps/web/src/routes/api/auth/$.ts
import { createFileRoute } from '@tanstack/react-router';
import { auth } from '@oakoss/auth/server';
export const Route = createFileRoute('/api/auth/$')({
server: {
handlers: {
GET: ({ request }) => auth.handler(request),
POST: ({ request }) => auth.handler(request),
},
},
});
Sign In/Sign Up
import { authClient } from '@oakoss/auth/client';
// Sign up with email
await authClient.signUp.email({
name: 'John Doe',
email: 'john@example.com',
password: 'password123',
});
// Sign in with email
await authClient.signIn.email({
email: 'john@example.com',
password: 'password123',
callbackURL: '/dashboard',
});
// Sign in with social provider
await authClient.signIn.social({
provider: 'github',
callbackURL: '/dashboard',
});
Session Management
// Reactive hook
function UserProfile() {
const { data: session, isPending } = authClient.useSession();
if (isPending) return <Spinner />;
if (!session) return <LoginPrompt />;
return <div>Welcome, {session.user.name}</div>;
}
// One-time fetch
const { data: session } = await authClient.getSession();
Route Protection (beforeLoad)
// apps/web/src/routes/_app/route.tsx
import { createFileRoute, redirect } from '@tanstack/react-router';
import { auth } from '@oakoss/auth/server';
export const Route = createFileRoute('/_app')({
beforeLoad: async ({ context }) => {
const session = await auth.api.getSession({
headers: context.request.headers,
});
if (!session) {
throw redirect({ to: '/login' });
}
return { user: session.user };
},
component: AppLayout,
});
Server Function Auth Check
import { createServerFn } from '@tanstack/react-start';
import { auth } from '@oakoss/auth/server';
const deletePost = createServerFn({ method: 'POST' })
.inputValidator(z.object({ id: z.string() }))
.handler(async ({ data, request }) => {
const session = await auth.api.getSession({ headers: request.headers });
if (!session) {
return { error: 'Unauthorized', code: 'AUTH_REQUIRED' };
}
// Check ownership
const post = await db.query.posts.findFirst({
where: eq(posts.id, data.id),
});
if (post?.authorId !== session.user.id) {
return { error: 'Not authorized', code: 'FORBIDDEN' };
}
await db.delete(posts).where(eq(posts.id, data.id));
return { success: true };
});
Sign Out
await authClient.signOut({
fetchOptions: {
onSuccess: () => (window.location.href = '/login'),
},
});
Common Mistakes
| Mistake | Correct Pattern |
|---|---|
| Client-side auth checks only | Validate session server-side in beforeLoad |
| Missing request headers | Pass { headers: request.headers } to API |
| Not handling loading states | Check isPending before rendering |
| Hardcoding callback URLs | Use environment variables |
| Storing session in useState | Use authClient.useSession() hook |
| Missing auth handler route | Create /api/auth/$.ts catch-all route |
Not using usePlural in adapter | Set usePlural: true for Better Auth |
| Importing server code in client | Use @oakoss/auth/server only server-side |
Delegation
- Route protection: For beforeLoad patterns, see tanstack-router skill
- Server functions: For auth in server functions, see tanstack-start skill
- Database: For user queries, see database skill
- Code review: After implementing auth, delegate to
code-revieweragent
Topic References
- Auth Reference - Social providers, plugins, session types, error handling
Score
Total Score
65/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
✓最近の活動
1ヶ月以内に更新
+10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
✓タグ
1つ以上のタグが設定されている
+5
Reviews
💬
Reviews coming soon

