# Going to Production

Everything between the 5-minute quickstart and a production-ready SaaS app.

The quickstart gets you a working demo. Here is what to configure before real users sign in.

## 1. Environment variables

Create a `.env.local` file in your project root. Never commit this file.

```bash
# BuildBase
NEXT_PUBLIC_BUILDBASE_SERVER_URL=https://api.console.buildbase.app
NEXT_PUBLIC_BUILDBASE_ORG_ID=your-org-id
NEXT_PUBLIC_BUILDBASE_CLIENT_ID=your-client-id
NEXT_PUBLIC_BUILDBASE_REDIRECT_URL=http://localhost:3000/callback

# Server-only — never expose with a NEXT_PUBLIC_ prefix
BUILDBASE_CLIENT_SECRET=your-client-secret

# Session secret (used to sign httpOnly cookies)
SESSION_SECRET=run-openssl-rand-hex-32
```

Update your provider to read from env:

```tsx
<SaaSOSProvider
  serverUrl={process.env.NEXT_PUBLIC_BUILDBASE_SERVER_URL!}
  version={ApiVersion.V1}
  orgId={process.env.NEXT_PUBLIC_BUILDBASE_ORG_ID!}
  auth={{
    clientId: process.env.NEXT_PUBLIC_BUILDBASE_CLIENT_ID!,
    redirectUrl: process.env.NEXT_PUBLIC_BUILDBASE_REDIRECT_URL!,
    callbacks: { /* ... */ },
  }}
>
```

## 2. Auth callback API route

The quickstart shows `fetch('/api/auth/verify')` — here's the actual implementation. This route calls BuildBase's token endpoint (`POST /api/v1/auth/token`) to exchange the auth code for a session, then sets an httpOnly cookie. The exchange requires your `clientSecret`, which is why it runs on your backend — the secret must never reach the browser.

```typescript
// app/api/auth/verify/route.ts
import { cookies } from 'next/headers';

export async function POST(req: Request) {
  const { code } = await req.json();

  // Exchange the auth code for a session
  const res = await fetch(
    `${process.env.NEXT_PUBLIC_BUILDBASE_SERVER_URL}/api/v1/auth/token`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        code,
        orgId: process.env.NEXT_PUBLIC_BUILDBASE_ORG_ID,
        clientId: process.env.NEXT_PUBLIC_BUILDBASE_CLIENT_ID,
        clientSecret: process.env.BUILDBASE_CLIENT_SECRET,
      }),
    }
  );

  if (!res.ok) {
    return Response.json({ error: 'Invalid or expired code' }, { status: 401 });
  }

  const { data } = await res.json(); // { sessionId, user }

  // Set httpOnly cookie (secure in production)
  const cookieStore = await cookies();
  cookieStore.set('bb-session', data.sessionId, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'lax',
    path: '/',
    maxAge: 60 * 60 * 24 * 30, // 30 days
  });

  return Response.json({ sessionId: data.sessionId });
}
```

```typescript
// app/api/auth/session/route.ts
import { cookies } from 'next/headers';

export async function GET() {
  const cookieStore = await cookies();
  const sessionId = cookieStore.get('bb-session')?.value ?? null;
  return Response.json({ sessionId });
}
```

```typescript
// app/api/auth/signout/route.ts
import { cookies } from 'next/headers';

export async function POST() {
  const cookieStore = await cookies();
  cookieStore.delete('bb-session');
  return Response.json({ ok: true });
}
```

## 3. Project structure

A typical Next.js + BuildBase project:

```text
app/
├── api/
│   └── auth/
│       ├── verify/route.ts     ← exchanges code for session
│       ├── session/route.ts    ← returns session from cookie
│       └── signout/route.ts    ← clears session cookie
├── (auth)/
│   ├── login/page.tsx          ← public pages
│   └── register/page.tsx
├── (app)/
│   ├── layout.tsx              ← wraps in BuildBaseProvider
│   ├── dashboard/page.tsx      ← protected pages
│   ├── settings/page.tsx
│   └── billing/page.tsx
├── layout.tsx                  ← root layout (fonts, global styles)
└── providers.tsx               ← BuildBaseProvider component
```

## 4. Protect your routes

Wrap your authenticated layout so unauthenticated users can't access app pages:

```tsx
// app/(app)/layout.tsx
'use client';

import {
  useSaaSAuth,
  WhenAuthenticated,
  WhenUnauthenticated,
} from '@buildbase/sdk/react';

export default function AppLayout({ children }: { children: React.ReactNode }) {
  const { isLoading, signIn } = useSaaSAuth();

  if (isLoading) return ;

  return (
    <>
      <WhenAuthenticated>{children}</WhenAuthenticated>
      <WhenUnauthenticated>
        <button onClick={() => signIn()}>Sign In</button>
      </WhenUnauthenticated>
    </>
  );
}
```

## 5. Dashboard setup checklist

Before going live, make sure these are configured in the [BuildBase dashboard](https://console.buildbase.app):

| Step                | Where                                               | What to do                                                            |
| ------------------- | --------------------------------------------------- | --------------------------------------------------------------------- |
| Create org          | Dashboard home                                      | Click "Create Organization"                                           |
| Get credentials     | Settings → General                                  | Copy `orgId`                                                          |
| Set up OAuth        | User Management → Access → Authentication           | Create a client, get `clientId` and `clientSecret`, add redirect URLs |
| Add auth methods    | User Management → Access → Authentication → Methods | Enable Email/Password, Google, or Magic Link                          |
| Create plans        | Billing → Plans                                     | Set up at least one plan (if using billing)                           |
| Connect Stripe      | Billing → Credentials                               | Enter Stripe API keys (test keys for dev)                             |
| Set up email sender | Messaging → Sender Accounts                         | Configure Google, Mailgun, or SMTP                                    |
| Add redirect URLs   | User Management → Access → Authentication           | Add your production URL (e.g. `https://yourapp.com/callback`)         |

## 6. Production environment

For production, update your environment variables:

```bash
# .env.production
NEXT_PUBLIC_BUILDBASE_SERVER_URL=https://api.console.buildbase.app
NEXT_PUBLIC_BUILDBASE_ORG_ID=your-org-id
NEXT_PUBLIC_BUILDBASE_CLIENT_ID=your-client-id
NEXT_PUBLIC_BUILDBASE_REDIRECT_URL=https://yourapp.com/callback

BUILDBASE_CLIENT_SECRET=your-client-secret
SESSION_SECRET=your-production-secret
```

Key differences from development:

- `REDIRECT_URL` must be your production domain (not localhost)
- Add the production redirect URL in the dashboard under User Management → Access → Authentication
- Use real Stripe keys instead of test keys
- Set `secure: true` on session cookies (already handled by the `NODE_ENV` check above)

## 7. Deployment checklist

Before your first deploy:

- [ ] Environment variables set in your hosting platform (Vercel, Railway, etc.)
- [ ] Production redirect URL added in BuildBase dashboard
- [ ] Auth methods enabled (email, social, magic link)
- [ ] At least one plan created and published (if using billing)
- [ ] Stripe connected with live keys (if using billing)
- [ ] Email sender configured (for transactional emails)
- [ ] Test the full sign-in → dashboard → sign-out flow in production

## Next Steps

- [Authentication](/authentication/overview) — Deep dive into auth methods and sessions.
- [Billing](/billing/overview) — Set up plans, checkout, and subscriptions.
- [Server SDK](/server-sdk/overview) — Backend operations: usage recording, credits, notifications.
- [Self-Hosted](/self-hosted/overview) — Deploy on your own infrastructure.
