# Keep your existing sign-in

What works when your app already signs people in with Supabase, Clerk, Auth0, Firebase or its own code, and you want BuildBase's other modules.

Your app already signs people in, with Supabase, Clerk, Auth0, Firebase or code of its own, and you want BuildBase for teams, plans, credits or email. BuildBase does not read your provider's tokens: there is no setting that tells it to trust a Supabase, Clerk, Auth0 or Firebase sign-in, and no way to look a person up by your provider's user ID. Every BuildBase module works on a **BuildBase user** and a **BuildBase session**. This page says what that leaves you, so you can choose before anything changes.

## Stop and ask before migrating

If you are a coding agent: when the app already signs people in, stop and ask the person you work for which path below they want. Do not replace a working login on your own. It is the one change that can lock every existing user out.

## Three paths

| Path                                                                                                          | People sign in with                            | What BuildBase gives you                                      | Status                                                                         |
| ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| [1. Move your users to BuildBase](#1-move-your-users-to-buildbase)                                            | BuildBase's hosted sign-in                     | Everything, including the React SDK's screens and hooks       | Supported end to end. The path for an app made in Lovable, Bolt, Replit or v0  |
| [2. Your provider as a sign-in button](#2-your-provider-as-a-sign-in-button)                                  | Your provider, through BuildBase's hosted page | Everything, as path 1                                         | Supported for a provider that speaks OpenID Connect                            |
| [3. Keep your sign-in, call BuildBase from your server](#3-keep-your-sign-in-call-buildbase-from-your-server) | Your provider, as today                        | Teams, plans, credits and email, called from your own backend | Works through the API below. Not a packaged mode: you write and own the bridge |

## 1. Move your users to BuildBase

The command line tool reads the export Clerk, Auth0, Firebase or Supabase gives you and creates each person in BuildBase, with their email address, name, phone number, their old ID kept as `externalId`, and the Google, GitHub, Microsoft, Apple or LinkedIn sign-ins they used.

```bash
npx buildbase import --from supabase supabase-users.json --dry-run
```

**Passwords come with them.** The import stores each person's old password hash, never the password itself, and a plaintext password is refused. At their next sign-in, people type the password they always had: BuildBase checks it with the old system's algorithm (bcrypt, argon2, Django's PBKDF2 or Firebase's scrypt), then stores it the way it stores every password and drops the old hash. Nobody is asked to reset. Auth0 leaves password hashes out of its export; ask Auth0 support for them before you plan the move. Someone whose hash cannot be checked is imported with no password and signs in with a magic link, an email code, a social provider or **Forgot password?**.

**Sessions do not come across.** Everyone signs in once more, and adds a passkey or two-step sign-in again if they had one.

[Import your users from another service](/sign-in-setup/importing-users) walks through it from the export to the first sign-in, and [Importing users](/authentication/importing-users) is the reference.

## 2. Your provider as a sign-in button

A [custom OpenID Connect provider](/sign-in-setup/custom-oidc) puts one more button on BuildBase's hosted sign-in page, for a provider such as Auth0, Okta or Keycloak. People press it, sign in on your provider's page as they do today, and come back signed in to BuildBase. Your provider stays the place their password lives.

It still goes through BuildBase's hosted page and its redirect, so it is path 1's sign-in with your provider behind one button, not a silent hand-off. The provider must publish OpenID Connect discovery (or be plain OAuth 2.0 with a user info address). Whether Supabase, Clerk or Firebase can act as that kind of provider for your project is for their docs to say: BuildBase's guide is written and tested against a standard OIDC provider, not against those three. An organization has one custom provider, and a plan can lock it.

## 3. Keep your sign-in, call BuildBase from your server

Your provider keeps signing people in, and your backend, which already knows who is signed in, opens a BuildBase session for that person and calls BuildBase with it. Three calls, all with an org API key that stays on your server:

1. **Create the person in BuildBase once**, with `POST /api/users/import` and a one-line file. Each answer row carries the BuildBase `userId`, also when the person is already there (`skipped`). Store it beside your provider's ID: BuildBase has no lookup by `externalId`.
2. **Open a session for them** with `POST /api/v1/public/token/exchange`. The key must have the owner or admin role to name another user.
3. **Call BuildBase with that session**, through the server SDK's `withSession()` or the `x-session-id` header.

```typescript title="server/buildbase-bridge.ts"
import { randomUUID } from 'node:crypto';
import BuildBase from '@buildbase/sdk';

const serverUrl = process.env.BUILDBASE_SERVER_URL!; // https://api.console.buildbase.app
const orgId = process.env.BUILDBASE_ORG_ID!;
const apiToken = process.env.BUILDBASE_API_TOKEN!; // orgId:secret, owner or admin role

// 1. Once per person: create them in BuildBase, keep their BuildBase id.
export async function ensureBuildBaseUser(user: {
  id: string;
  email: string;
  name?: string;
}) {
  const line = JSON.stringify({
    email: user.email,
    emailVerified: true,
    name: user.name,
    externalId: user.id,
  });
  const form = new FormData();
  form.append('file', new Blob([line + '\n']), 'user.ndjson');
  form.append('source', 'supabase');
  const res = await fetch(`${serverUrl}/api/users/import`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${apiToken}` },
    body: form,
  });
  const { rows } = await res.json();
  if (!rows?.[0]?.userId)
    throw new Error(rows?.[0]?.message ?? 'import failed');
  return rows[0].userId as string; // store it with your user
}

// 2. Open a session for them. Reuse it for its life (here, one day).
export async function openSession(buildbaseUserId: string) {
  const res = await fetch(`${serverUrl}/api/v1/public/token/exchange`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      token: apiToken,
      userId: buildbaseUserId,
      expiresIn: 86400,
    }),
  });
  const { sessionId } = await res.json();
  return sessionId as string;
}

// 3. Call any module as that person.
export async function spendCredits(sessionId: string, workspaceId: string) {
  const bb = BuildBase({ serverUrl, orgId }).withSession(sessionId);
  return bb.credits.consume(workspaceId, {
    amount: 1,
    idempotencyKey: randomUUID(),
  });
}
```

With the session, the person can create and join workspaces, see their plan, spend credits and do what the [server SDK](/server-sdk/overview) lists. Some calls need only the key, with no session: granting credits, reading a named person's plan and limits (`BuildBase({ apiToken })`, `bb.entitlements.get({ user: '<userId>' })`) and emailing a user.

Every field and refusal of the exchange is in [opening a session as one of your users](/authentication/sessions-and-tokens#opening-a-session-as-one-of-your-users).

## Which to choose

- **An app made in an AI app builder, with few users so far:** path 1. Everything in the [AI app builders](/ai-builders/overview) pages assumes BuildBase sign-in.
- **Your provider is an OIDC provider and people should keep its page:** path 2.
- **The sign-in cannot move yet, and you need billing or teams now:** path 3, knowing you own the bridge, and plan the move to path 1 or 2 when you can.

## Things to watch out for

Path 3 leaves these to you, all checked in the code:

- **No automatic user.** A person who signs up with your provider does not exist in BuildBase until step 1 runs; the exchange answers `404 user_not_found` until then. They count toward your plan's end-user limit like anyone else.
- **No sign-up events.** A person created this way emits `user.imported`, never `user.registered`, and gets no welcome or verification email. A workflow or webhook that waits for sign-ups does not run for them; listen for `user.imported`.
- **A powerful key on your server.** Only an owner or admin key may open a session for another user, so a leak of it is a leak of every user. Keep it out of the browser and out of your AI builder's frontend code.
- **Never a recent sign-in.** Nobody signed in to an exchanged session, so a change that asks the person to confirm it's them is refused with `403 reauth_required`. It is listed among the person's sessions as opened by your team (`method: 'app_team'`), and a company that uses [enterprise SSO](/authentication/enterprise-sso) can refuse these sessions for its people.
- **The React SDK's screens expect BuildBase's sign-in.** `SaaSOSProvider` is built around the hosted sign-in. Its `getSession` option restores a session your app hands it, so passing the exchanged session there may run its hooks without the hosted page, but that is inferred from the SDK's types, not documented or tested. Build this path on the server SDK first.

## Next steps

- [Set up sign-in, step by step](/sign-in-setup/overview) - every sign-in guide, and the order to set them up in.
- [Import your users from another service](/sign-in-setup/importing-users) - path 1, step by step.
- [Add a custom OpenID Connect provider](/sign-in-setup/custom-oidc) - path 2.
- [Server SDK overview](/server-sdk/overview) - the calls path 3 makes with a session.
