---
title: Auth
description: Add email/password sign-in to your app — cookie sessions on the server, a useUser hook on the client.
category: sdk
order: 2
agent: "@coduck/sdk auth. Server (cookie-based, @coduck/sdk/server): getSession()->{user}|null, signUpWithPassword(email,password,name?), signInWithPassword(email,password), signOut(). Client (@coduck/sdk/client): useUser(meUrl='/api/me')->{user,loading,refresh} — needs a GET /api/me route returning {user}|null. Low-level (@coduck/sdk/auth): auth.signup/login/refresh/verify/me/logout with AuthSession{user,accessToken,refreshToken,expiresIn}. Requires CODUCK_AUTH_KEY, injected only on deploy — auth does NOT work in the in-editor preview."
---

# Auth

Email/password authentication for your deployed app. Sessions are stored in an httpOnly cookie and managed by server helpers; the client gets a small `useUser()` hook.

> **Deploy required.** Auth needs `CODUCK_AUTH_KEY`, which CoDuck only injects when the project is **deployed**. Sign-in will not work in the in-editor preview — deploy first.

## Server helpers

`@coduck/sdk/server` wraps the cookie + session handling for Next.js (App Router). Use it in route handlers and server actions.

```ts
import {
  getSession,
  signInWithPassword,
  signUpWithPassword,
  signOut,
} from '@coduck/sdk/server';
```

| Function | Returns | Notes |
|---|---|---|
| `getSession()` | `{ user } \| null` | Reads + verifies the session cookie |
| `signUpWithPassword(email, password, name?)` | `AuthSession` | Creates the user **and** sets the cookie |
| `signInWithPassword(email, password)` | `AuthSession` | Logs in **and** sets the cookie |
| `signOut()` | `void` | Clears the cookie + invalidates the refresh token |

### Sign in (server action)

```ts
'use server';
import { signInWithPassword } from '@coduck/sdk/server';
import { redirect } from 'next/navigation';

export async function login(formData: FormData) {
  await signInWithPassword(
    String(formData.get('email')),
    String(formData.get('password')),
  );
  redirect('/dashboard');
}
```

### Protect a server component

```tsx
import { getSession } from '@coduck/sdk/server';
import { redirect } from 'next/navigation';

export default async function Dashboard() {
  const session = await getSession();
  if (!session) redirect('/login');
  return <p>Welcome, {session.user.email}</p>;
}
```

### The `/api/me` route

The client hook reads the session from your own app via `GET /api/me`. Expose it with the server helper so the browser never touches the cookie directly:

```ts
// app/api/me/route.ts
import { getSession } from '@coduck/sdk/server';

export async function GET() {
  const session = await getSession();
  return Response.json(session ?? { user: null });
}
```

## Client hook

`@coduck/sdk/client` gives you the current user in a client component. It fetches `/api/me` (above).

```tsx
'use client';
import { useUser } from '@coduck/sdk/client';

export function UserBadge() {
  const { user, loading, refresh } = useUser(); // useUser('/api/me') by default
  if (loading) return null;
  if (!user) return <a href="/login">Sign in</a>;
  return <span>{user.email}</span>;
}
```

`useUser()` returns `{ user: AuthUser | null, loading: boolean, refresh: () => Promise<void> }`.

## Low-level client

For full control over tokens (custom flows, non-cookie clients), `@coduck/sdk/auth` exposes the raw client:

```ts
import { auth } from '@coduck/sdk/auth';

const session = await auth.login({ email, password });
// → { user, accessToken, refreshToken, expiresIn }

const user    = await auth.me(session.accessToken);
const renewed = await auth.refresh(session.refreshToken);
await auth.logout(session.accessToken);
```

| Method | Returns |
|---|---|
| `signup({ email, password, name? })` | `AuthSession` |
| `login({ email, password })` | `AuthSession` |
| `refresh(refreshToken)` | `AuthSession` |
| `verify(accessToken)` | `AuthUser` |
| `me(accessToken)` | `AuthUser` |
| `logout(accessToken)` | `void` |

## Types

```ts
interface AuthUser {
  id: string;
  email: string;
  name?: string | null;
  createdAt: string;
}

interface AuthSession {
  user: AuthUser;
  accessToken: string;
  refreshToken: string;
  expiresIn: number; // seconds until the access token expires
}
```
