원클릭으로
auth
Better Auth authentication. Use for auth, login, logout, session, user, signup, register, protect, middleware, password, oauth, social
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Better Auth authentication. Use for auth, login, logout, session, user, signup, register, protect, middleware, password, oauth, social
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Git workflow for branches, commits, and PRs. Use for commit, branch, pr, pull request, conventional, push, feat, fix, chore, merge, rebase
Storybook stories and interaction tests. Use for story, stories, storybook, chromatic, visual test, interaction test, play function, component test
TypeScript and unicorn linting patterns. Use for typescript, array, for-of, reduce, forEach, throw, catch, modern js, es modules, string, number, error handling
Zod v4 schema validation. Use for zod, schema, validation, parse, safeParse, infer, coerce, transform, refine, z.object, z.string, z.email, z.url
Drizzle ORM + PostgreSQL database layer. Use for db, database, query, schema, table, migrate, sql, postgres, drizzle, model, relation
React error boundaries + fallback UIs. Use for error, catch, boundary, fallback, recovery, crash, ErrorBoundary, errorComponent, reset
| 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.
packages/auth/
├── src/
│ ├── client.ts # Auth client (React hooks)
│ ├── server.ts # Auth server (Better Auth config)
│ └── index.ts # Public exports
└── package.json
// 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
});
// 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>()],
});
// 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),
},
},
});
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',
});
// 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();
// 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,
});
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 };
});
await authClient.signOut({
fetchOptions: {
onSuccess: () => (window.location.href = '/login'),
},
});
| 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 |
code-reviewer agent