home / skills / andrelandgraf / fullstackrecipes / using-authentication

using-authentication skill

/.agents/skills/using-authentication

This skill streamlines secure authentication across client and server, enabling session access, protected routes, sign in/out, and user data retrieval.

npx playbooks add skill andrelandgraf/fullstackrecipes --skill using-authentication

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.0 KB
---
name: using-authentication
description: Use Better Auth for client and server-side authentication. Covers session access, protected routes, sign in/out, and fetching user data.
---

# Working with Authentication

Use Better Auth for client and server-side authentication. Covers session access, protected routes, sign in/out, and fetching user data.

## Implement Working with Authentication

Use Better Auth for client and server-side authentication. Covers session access, protected routes, sign in/out, and fetching user data.

**See:**

- Resource: `using-authentication` in Fullstack Recipes
- URL: https://fullstackrecipes.com/recipes/using-authentication

---

### Client-Side Authentication

Use the auth client hooks in React components:

```tsx
"use client";

import { useSession, signOut } from "@/lib/auth/client";

export function UserMenu() {
  const { data: session, isPending } = useSession();

  if (isPending) return <div>Loading...</div>;
  if (!session) return <a href="/sign-in">Sign In</a>;

  return (
    <div>
      <span>{session.user.name}</span>
      <button onClick={() => signOut()}>Sign Out</button>
    </div>
  );
}
```

### Server-Side Session Access

Get the session in Server Components and API routes:

```typescript
import { auth } from "@/lib/auth/server";
import { headers } from "next/headers";

// In a Server Component
export default async function Page() {
  const session = await auth.api.getSession({
    headers: await headers(),
  });

  if (!session) {
    return <div>Not signed in</div>;
  }

  return <div>Hello, {session.user.name}</div>;
}
```

```typescript
// In an API route
export async function POST(request: Request) {
  const session = await auth.api.getSession({
    headers: await headers(),
  });

  if (!session) {
    return new Response("Unauthorized", { status: 401 });
  }

  // Use session.user.id for queries...
}
```

### Protected Pages Pattern

Redirect unauthenticated users:

```tsx
import { redirect } from "next/navigation";
import { headers } from "next/headers";
import { auth } from "@/lib/auth/server";

export default async function ProtectedPage() {
  const session = await auth.api.getSession({
    headers: await headers(),
  });

  if (!session) {
    redirect("/sign-in");
  }

  return <Dashboard user={session.user} />;
}
```

### Auth Pages Pattern

Redirect authenticated users away from auth pages:

```tsx
import { redirect } from "next/navigation";
import { headers } from "next/headers";
import { auth } from "@/lib/auth/server";

export default async function SignInPage() {
  const session = await auth.api.getSession({
    headers: await headers(),
  });

  if (session) {
    redirect("/chats"); // Already signed in
  }

  return <SignIn />;
}
```

### Signing In

```typescript
import { signIn } from "@/lib/auth/client";

// Email/password
await signIn.email({
  email: "[email protected]",
  password: "password",
  callbackURL: "/chats",
});

// Social provider
await signIn.social({
  provider: "google",
  callbackURL: "/chats",
});
```

### Signing Up

```typescript
import { signUp } from "@/lib/auth/client";

await signUp.email({
  email: "[email protected]",
  password: "password",
  name: "John Doe",
  callbackURL: "/verify-email",
});
```

### Signing Out

```typescript
import { signOut } from "@/lib/auth/client";

await signOut({
  fetchOptions: {
    onSuccess: () => {
      router.push("/");
    },
  },
});
```

### Fetching User Data After Auth

In protected pages, fetch user-specific data after validating the session:

```tsx
export default async function DashboardPage() {
  const session = await auth.api.getSession({
    headers: await headers(),
  });

  if (!session) {
    redirect("/sign-in");
  }

  const [chats, profile] = await Promise.all([
    getUserChats(session.user.id),
    getUserProfile(session.user.id),
  ]);

  return <Dashboard chats={chats} profile={profile} />;
}
```

---

## References

- [Better Auth React](https://www.better-auth.com/docs/react)
- [Better Auth Server](https://www.better-auth.com/docs/server)

Overview

This skill shows how to use Better Auth for client- and server-side authentication in TypeScript full‑stack apps. It provides production-ready patterns for session access, protected routes, sign in/up/out flows, and fetching user-specific data after authentication. The examples focus on Next.js Server and Client Components with clear, minimal code patterns.

How this skill works

Client components use auth client hooks (useSession, signIn, signOut, signUp) to display user state and trigger auth flows. Server Components and API routes call auth.api.getSession with request headers to validate sessions and gate access. Patterns include redirecting unauthenticated users from protected pages and redirecting authenticated users away from sign-in pages. After session validation, user.id is used to fetch user-specific resources.

When to use it

  • Protect server-rendered pages and API routes that require an authenticated user.
  • Show user menus and sign-in/out controls in client components.
  • Prevent signed-in users from seeing sign-in or sign-up pages.
  • Perform server-side data fetching that depends on the current user.
  • Implement email/password and social provider sign-in and sign-up flows.

Best practices

  • Always validate the session on the server before returning protected content or executing sensitive logic.
  • Use auth.api.getSession with request headers in Server Components and API routes to avoid client-side trust issues.
  • Redirect unauthenticated users early in Server Components to minimize wasted work.
  • Keep client UI minimal while session state is pending to avoid flash of incorrect content.
  • Use Promise.all to parallelize fetching multiple user-specific resources after session validation.

Example use cases

  • Render a UserMenu component that shows the user name and a sign-out button using useSession and signOut.
  • Protect a dashboard page by fetching the session server-side and redirecting to /sign-in if missing.
  • Redirect already-authenticated visitors away from the sign-in page to the main app route.
  • Implement email and social sign-in flows and redirect to a callback URL on success.
  • Secure an API route by checking the session and using session.user.id for database queries.

FAQ

How do I access the session on the server?

Call auth.api.getSession and pass request headers (e.g., headers() in Next.js) to obtain the current session in Server Components or API routes.

What should I do if the session is missing?

For pages, redirect to your sign-in route. For API routes, return 401 Unauthorized. Avoid rendering protected data when session is absent.