Skip to content
Guides
Build guide

Add login, teams and paid plans to a Lovable app, and keep Lovable Cloud or Supabase for your data

New Lovable apps are TanStack Start apps, which means they can hold the three server routes BuildBase sign-in needs. Built and typechecked on 27 October 2026, with the Supabase wrinkle stated plainly.

Dharmendra Jagodana18 min read

In short

To add login, teams and paid plans to a Lovable app, ask Lovable for the @buildbase/sdk package, add three small server routes that swap the sign-in code for a session cookie, keep the client secret in Lovable Secrets, and leave your data in Lovable Cloud or Supabase. Teams, roles and plans are then SDK calls.

You are adding real sign-in, a workspace per customer, paid plans and the email that goes out when a card fails to an app Lovable built for you, without moving your data out of Lovable Cloud or your Supabase project. When it works, a stranger opens your published address in a private window, signs up with Google or a magic link, lands in their own workspace, invites a colleague who gets an email, picks a plan on a test card, and the feature they paid for appears without a reload. The client secret never leaves Lovable's server side.

The thing that makes this possible is a change Lovable made in May 2026. New Lovable apps are TanStack Start apps, which render on the server and can hold server routes, instead of React + Vite apps that render only in the browser. A server route is exactly what the sign-in code exchange needs, because it uses a secret the browser must never see. Older Lovable apps are still Vite, and this guide says what to do about that too.

What we ran on 27 October 2026: a current TanStack Start app with the official Cloudflare Workers adapter, the shape Lovable's new apps use, scaffolded with @tanstack/cli 0.72.0 (TanStack Start 1.169.4, Vite 8.3.5, React 19.3.1), with @buildbase/sdk 0.0.90 installed from npm. The three handlers from our Vite guide went in unchanged, as server routes. Typecheck passed, the production build passed, a wrangler deploy --dry-run passed, and the three routes answered correctly inside the Workers runtime, in development and in the built Worker, against a stub of our platform. What we have not run: the real hosted sign-in round trip (that needs an organization and a registered redirect address), a browser, a real deploy, or Lovable's own build pipeline. Where that last one matters, this guide says so.

Before you start

A Lovable project on a paid plan or the free plan, either stack. A BuildBase organization, and an auth client created in the console under User Management → Authentication, with its client ID and secret. Your published Lovable address (and your custom domain, if you have one) registered on that client as redirect URLs, each ending in /sign-in. A Stripe account, only when you reach plans.

Tip

The handlers in this guide are the ones from BuildBase on Vite, byte for byte. If you are comfortable in a terminal, that guide and the admin-dashboard example are the tested originals; this page is the Lovable wrapping around them.

1. Find out which Lovable app you have

Open the Code view and look at package.json. If it lists @tanstack/react-start, you have a TanStack Start app and every step below applies as written. If it does not, you have an older React + Vite app, and you need a server first.

Lovable's own docs give that test, and the date behind it: projects created from 13 May 2026 (22 June for Enterprise workspaces) use TanStack Start; earlier ones run on React with Vite and render in the browser. There is no framework choice. Every project uses the same stack, and if you want a different one, Lovable's answer is to continue outside Lovable through Git sync.

If yours is Vite, you have two honest routes:

  • Ask Lovable to upgrade the project to TanStack Start. Its upgrade page says anyone who can edit the project can start it on any plan, most upgrades cost 10 to 35 credits, the database is untouched, your published site does not change until you publish the upgraded version, and you can revert from version history. Do that first, test every page (the page warns that some browser-only libraries can break server rendering), then come back to step 2.
  • Export to GitHub and host somewhere with serverless functions. Git sync is two-way and on every plan for github.com; it always creates a new private repository. Then follow the Vite guide on Vercel, Netlify or Cloudflare, where the three handlers are one-line function files. You keep building in Lovable; the sign-in server lives on the host.

Why not run the handlers as Lovable Cloud edge functions and stay on Vite? Because the session has to be an httpOnly cookie on your app's own address, and a function on another origin cannot set one there. A server route in the same app can. That is the whole reason TanStack Start matters here.

What you should see: either @tanstack/react-start in package.json, or a decision about which of the two routes above you are taking.

2. Register where people come back to

The hosted sign-in page sends people back to one exact address per environment, so register them before you prompt: your published Lovable address with /sign-in on the end, your custom domain with /sign-in if you have one, and http://localhost:3000/sign-in if you ever run the exported code locally. An address it was not told about is refused.

This is also where the most common Lovable login complaint lives. Lovable's own sign-in docs say that when sign-in works in preview but fails on a published or custom domain, the redirect addresses are the thing to check, and that is true of BuildBase's hosted page as much as of the built-in one. A wildcard can stand in for one leftmost subdomain on a domain you own, but not on a public suffix, so *.lovable.app is refused; register each address you actually use. Preview addresses change, so most people register the published one and the custom domain and test sign-in there.

What you should see: the auth client in the console lists each address, and the client secret has been shown once. Do not paste it into the Lovable chat.

3. Ask Lovable for the package and the three routes

You do not install anything. Lovable's docs say to name the package in the project chat, and it adds it to the dependencies; there is no terminal step. Paste this prompt. It names the outcome, the rules, the files, and the one thing that must never happen to the secret.

Use the @buildbase/sdk npm package to add BuildBase sign-in, a workspace per customer, paid plans and lifecycle email to this app.
Keep Lovable Cloud (or my Supabase project) as the database; BuildBase is not the primary database. Do not add Clerk, Auth0, Resend or n8n. Stripe is bring-your-own; do not store card data.
Read https://www.buildbase.app/llms.txt first and do not invent endpoints. Take the three handlers from https://www.buildbase.app/guides/buildbase-on-vite unchanged and run them as TanStack Start server routes:
- src/server/auth.ts with the guide's verify, session and signout handlers, byte for byte
- src/routes/api/auth/verify.ts (POST), src/routes/api/auth/session.ts (GET) and src/routes/api/auth/signout.ts (POST): each a createFileRoute with server.handlers that calls the matching handler
- src/context/buildbase-provider.tsx from the guide, with import '@buildbase/sdk/css', wrapping <Outlet /> in the root route's component in src/routes/__root.tsx
Put VITE_BUILDBASE_ORG_ID, VITE_BUILDBASE_CLIENT_ID and VITE_BUILDBASE_REDIRECT_URL in .env. I will add BUILDBASE_CLIENT_SECRET under Secrets myself; never read it in the browser and never give it a VITE_ prefix.
If sign-in already exists in this app, stop and ask me before changing it.

What you should see: @buildbase/sdk in package.json, and five new or changed files. The route that matters is the one that swaps the one-time code from the hosted page for a session, and in TanStack Start it is three lines around the Vite guide's handler:

// src/routes/api/auth/verify.ts
import { createFileRoute } from '@tanstack/react-router';

import { verify } from '../../../server/auth';

export const Route = createFileRoute('/api/auth/verify')({
  server: { handlers: { POST: ({ request }) => verify(request) } },
});

session.ts is the same shape with GET: ({ request }) => session(request), and signout.ts with POST: () => signout(). A Start handler receives { request, params, context } rather than a bare Request, which is the only reason the wrapper exists; src/server/auth.ts is the guide's file. The provider wraps the root route:

// src/routes/__root.tsx (the component; the html shell stays as Lovable wrote it)
import { createRootRoute, Outlet } from '@tanstack/react-router';

import { BuildBaseProvider } from '../context/buildbase-provider';

export const Route = createRootRoute({
  // head and shellComponent as Lovable generated them
  component: () => (
    <BuildBaseProvider>
      <Outlet />
    </BuildBaseProvider>
  ),
});

Two things we checked that you do not have to: the provider renders on the server in its loading state without touching window, so server rendering does not break; and the stylesheet import is emitted as a route asset and linked in the server-rendered HTML, so the SDK's screens are styled before the page becomes interactive.

Correcting potential issues. Read the change before you accept it. The ways Lovable tends to drift, and the line to paste back:

  • It wrote createServerFn functions instead of server routes. Those are for calling from your own pages, not for the three /api/auth/* addresses the provider fetches. Paste: "Replace the server functions with file routes under src/routes/api/auth, each exporting a createFileRoute whose server.handlers entry calls the matching handler, as the guide shows."
  • It put the secret in .env as VITE_BUILDBASE_CLIENT_SECRET, or read it in the provider. Paste: "BUILDBASECLIENT_SECRET is read only inside src/server/auth.ts on the server. Remove every other reference and the VITE prefix."
  • It kept, or added, a sign-in of its own next to BuildBase's. Paste: "BuildBase is the only sign-in in this app. Remove the other sign-in pages and the users table for them, and keep every other table."

4. Add the secret, and the three public values

The secret goes under More → Settings → Secrets as BUILDBASE_CLIENT_SECRET. Lovable's docs say a secret's value can never be viewed again in Lovable, only replaced or deleted, that it is not repeated in chat, logs or code, and that secrets never reach the browser. That is the right place. The three public values go in .env, which Lovable says is where VITE_-prefixed, browser-exposed variables belong, and which it asks you not to add to .gitignore.

# .env
VITE_BUILDBASE_ORG_ID=<your 24-character org id>
VITE_BUILDBASE_CLIENT_ID=<the client id>
VITE_BUILDBASE_REDIRECT_URL=https://your-app.lovable.app/sign-in
# VITE_BUILDBASE_SERVER_URL defaults to https://api.console.buildbase.app

Here is the part we could not confirm from Lovable's docs, so you have to: the secrets page says secrets are injected into edge functions and server-side integrations at runtime, and the external-hosting page says server-function secrets are set on the host. Neither names a TanStack Start server route. In our test the Workers runtime exposed the secret to the route as process.env.BUILDBASE_CLIENT_SECRET because the Worker had nodejs_compat on. Step 5 tells you whether Lovable's Worker does the same: a sign-in that fails with "Sign-in failed" on a correct code means the secret did not reach the route.

One more detail from the test. The provider reads the three public values with import.meta.env, which Vite inlines at build time, and the handlers read the same three names with process.env at request time. In a Worker that means the values have to exist as runtime variables too, not only in the build. If step 5 fails with a missing org id, the fix-up is below.

What you should see: after publishing, open https://your-app.lovable.app/api/auth/session and get {"sessionId":null}. The server half is alive before any button exists. If you get the app's HTML back instead, the routes are not there, and either the prompt did not land or this is still a Vite project.

Correcting potential issues. If the server route cannot see process.env at all, or reads the public values as empty, paste: "In src/server/auth.ts, read VITE_BUILDBASE_ORG_ID, VITE_BUILDBASE_CLIENT_ID and VITE_BUILDBASE_SERVER_URL with import.meta.env, and read BUILDBASE_CLIENT_SECRET from the Worker's env binding; keep every read inside the handler, never at module scope."

5. Publish, then sign in from a private window

Publish. Open the published address in a private window, click the button that calls signIn() from useSaaSAuth(), and you are sent to the hosted sign-in page, which offers whichever methods you switched on in the console. Sign up with a second email. You come back to /sign-in on your app, signed in.

Then the two checks a demo skips. Close the browser, reopen the site: still signed in, because the cookie is good for 30 days, which is the default session length. Sign out, then reload: asked to sign in again, because sign-out ended the session on the server rather than clearing a value in the browser. In our test the cookie came back as bb-session=...; Path=/; HttpOnly; SameSite=Lax; Max-Age=2592000, and sign-out answered with Max-Age=0.

The methods are switches, not code: email and password, a magic link, an email code, a phone sign-in code sent through your own Twilio account, passkeys, Google, LinkedIn, GitHub, Microsoft, Apple, your own OIDC provider, and Ethereum or Solana wallets. Turning on Google means creating the client in Google's console and pasting two values; nothing in the Lovable app changes.

Correcting potential issues. "The redirect address is not allowed" on the hosted page: the address in VITE_BUILDBASE_REDIRECT_URL and the one registered on the client differ, usually by a trailing slash, www, or the /sign-in path. "Sign-in failed" after a correct code: the secret did not reach the server route; check the Secrets panel, and if it is there, use the env-binding fix-up in step 4. Lovable's own 50-address limit applies to its built-in sign-in, not to this one.

6. A workspace per customer, and the colleague they invite

Every new user lands in their own workspace on first sign-in, with no setup step. That is on by default for new organizations, and in your app it is one component for the switcher and the hooks the SDK already has. Prompt Lovable for the screens; the data model is already there.

Add a workspace switcher to the app header using useSaaSWorkspaces() from @buildbase/sdk/react, and a /settings page that opens the SDK's built-in workspace, members and billing screens.
Members with the admin role may invite teammates by email; everyone else sees the member list read-only.
Do not create workspace, membership or role tables in Lovable Cloud or Supabase; BuildBase holds them.

What you should see: a switcher listing the workspaces the signed-in person belongs to, with Add team creating another if you allow it. On the members screen, inviting an address that has no account yet sends an email with a link; the person signs up and lands in the workspace with the role you chose. That flow has been in the SDK since version 0.0.71, and the reasons an invite can be refused (a seat limit, an outsider policy) come back as named errors rather than silence.

Why not let Lovable write the tables? Because every audit of a vibe-coded app finds the same bug: a role column checked on one page and nowhere else. Lovable's own end-user sign-in docs describe no roles, teams or workspaces for the people using your app, so whatever a prompt builds here is built from scratch. The SDK's roles are enforced by the API, so a member who types the address of an admin page gets refused by the server, not just a missing menu item.

Correcting potential issues. If a member can see another workspace's rows by changing an id in the address bar, Lovable wrote a page that reads your database without scoping it. That is your data and your query, and the next section is about it.

7. Your data stays where it is: the one honest wrinkle

Your tables stay in Lovable Cloud or in your Supabase project. The rule we give every agent is "do not use BuildBase as the primary database", and it applies here. The wrinkle is row-level security: policies keyed to the old sign-in (auth.uid() and friends) do not know who a BuildBase user is, because a BuildBase session is an opaque id, not a token Supabase can read.

There are two ways through, and most apps want the first.

Read and write through server routes. A server route or server function checks the BuildBase session and queries your database with a server-side credential, scoped by the BuildBase user or workspace id. This is the pattern generated apps should use anyway, because it puts the permission check where it cannot be forged. The server SDK makes the check one call:

// src/server/buildbase.ts
import BuildBase from '@buildbase/sdk';
import { getCookie } from '@tanstack/react-start/server';

export async function getSessionId() {
  return getCookie('bb-session') ?? null;
}

let client: ReturnType<typeof BuildBase> | undefined;

export function bb() {
  client ??= BuildBase({
    serverUrl:
      process.env.VITE_BUILDBASE_SERVER_URL ||
      'https://api.console.buildbase.app',
    orgId: process.env.VITE_BUILDBASE_ORG_ID!,
    getSessionId,
  });
  return client;
}

In our test a /profile route's server function called bb().users.getProfile() with that cookie, the call reached the platform, and a missing session redirected home. One note from the typecheck: TanStack Start refuses to send a value it cannot serialise out of a server function, and the SDK's user type carries free-form claims, so return the fields the page needs ({ email }) rather than the whole user.

Mint a Supabase-shaped token for browser queries. If pages query Supabase directly from the browser and you want the existing policies to keep working, add one more server route: it validates the session with the server SDK, then signs a short-lived HS256 token with your Supabase project's JWT secret (kept in Secrets), with sub set to the BuildBase user id and role and aud set to authenticated, and the browser passes it to Supabase as the bearer token. auth.uid() then matches sub, so store the BuildBase user id in your owner columns. The step-by-step is in the skill repository. You own that route and its refresh schedule. The platform change that would make it unnecessary, BuildBase publishing signing keys so Supabase can verify a user token directly, is recorded as not built.

Lovable's Quick scan still runs on every publish and still checks that tables have row rules; nothing here switches it off, and the Deep scan is worth running once after a change this size.

8. Plans people can pay for, and the email when a card fails

Plans, prices and trials live in the BuildBase console and sync to your own Stripe account at Stripe's rates, with a 0% platform fee, and no card data touches your app. The part that generated apps get wrong is what happens after the payment, so this step is mostly about the webhook route and the feature flag.

In the console, create two plans, Launch and Pro, say, each with a 7-day trial and a monthly and a yearly price. Then the webhook. It is a fourth server route, and this is the prompt for it:

Add src/routes/api/webhooks/buildbase.ts as a POST server route. Verify the signature with parseWebhookEvent from @buildbase/sdk on the raw request body, using BUILDBASE_WEBHOOK_SECRET from Secrets. Handle payment.failed with our own side effects only (flag the workspace, post to Slack); the platform sends the customer's email itself. Ignore other events.
Gate the export feature behind a BuildBase feature flag tied to the Pro plan, checked in a server route. Do not store the plan in Lovable Cloud or Supabase.

Point an endpoint in the console at https://your-app.lovable.app/api/webhooks/buildbase, add its signing secret under Secrets as BUILDBASE_WEBHOOK_SECRET, and publish.

What you should see: subscribe on a test card and the Pro feature appears without a reload, because the flag follows the workspace's plan. Let the test card fail on renewal and the email goes out from your sending domain, because payment.failed is one of the 129 events in the catalog and the console's notification or a workflow sends it; the route in your app is for whatever you want to happen besides. The console's delivery log shows each attempt, and a delivery your server was down for is retried up to five times over about two and a half minutes.

Compare that with the built-in payments, read on 27 October 2026 from Lovable's docs: Paddle at 5.0% plus 50 cents per transaction (a flat 10% under $10) or your own Stripe at Stripe's rates, on Pro and up; one active subscription per user by default, one provider per project, and once payments are on, the project cannot be forked. Trials, discount codes and monthly or yearly prices are there. Usage-based billing, seats and credits are not mentioned, and neither is a plan per team. If your app sells one subscription to one person, the built-in is simpler. If a plan belongs to a workspace and has to change what the app does, entitlements rather than flags is the ten-minute read before you prompt.

Where Lovable's built-in is the better pick

Keep Lovable's built-in sign-in when your app has one user per account and you need nothing after login. It is zero-config inside Lovable, which an external SDK will never be, it ships SAML SSO and OIDC, which BuildBase does not, so a buyer who signs in with Okta is better served by it, and its redirect addresses are managed for you on the built-in backend. Keep the built-in payments when you sell one subscription per person and want the tax side handled, which is Paddle's own pitch for it.

Choose BuildBase when the question is "what may this team do now that they paid": a workspace per customer, invites and roles enforced by the API, plans with versions, feature flags tied to the plan, credits with a hard stop, and the lifecycle email, from one SDK, with your data staying in Lovable Cloud. That is the layer Lovable's docs do not reach today, and the launch checklist is the twenty-item version of why it matters before strangers arrive.

Key takeaway

On Lovable the hard part was never the SDK, it was having a server. New Lovable apps have one. Three server routes, a secret in Secrets, and the rest is hooks, with your database exactly where it was.

What this costs in credits

Lovable prices builds by the request. Its own pricing page gives examples: 0.50 credits to make a button grey, 1.20 to add authentication, 1.70 or so for a landing page with images, and 1 credit per message in plan mode. Pro is $25 a month for 100 credits ($21 on annual billing) and Business $50 for 100, with daily build credits on top; an upgrade from Vite to TanStack Start is 10 to 35. All read on 27 October 2026, and Lovable changes these.

The prompt in step 3 is one request, with perhaps two fix-ups. Steps 6 and 8 are one each. The expensive thing on Lovable is not a feature, it is a loop: about 63% of the negative reviews we read across the builders were about credits spent on fixes that did not fix. Naming the exact files, the exact env names and the exact guide is how you avoid the loop, and it is why this page is long.

BuildBase's side: a Free plan at $0, then Launch at $19 a month, with a 7-day trial and no card on the paid plans.

What breaks, and how you can tell

Sign-in works in preview and loops after publishing. The published address, or the custom domain, is not registered with /sign-in on the end. Register each exact address; *.lovable.app is refused.

"Sign-in failed" on a correct code. The secret did not reach the server route. Check Secrets, then use the env-binding fix-up in step 4.

/api/auth/session returns the app's HTML. There are no server routes, because the project is still React + Vite, or the prompt wrote server functions instead. Step 1, then step 3's first fix-up.

The org id is empty on the server. The handlers read process.env at request time and the Worker only has the values from the build. Read the three public values with import.meta.env in src/server/auth.ts.

The secret is in the browser. Search the published JavaScript for its first characters. If it is there, it was prefixed VITE_. Rotate it in the console, move it to Secrets, publish again.

Rows vanish, or "permission denied", after switching sign-in. Row rules keyed to the old sign-in no longer match. Section 7: route the reads through the server, or mint the token.

The package install breaks the build. Lovable's docs say it cannot guarantee third-party packages. The SDK's dependencies are plain JavaScript with no native code, which is the usual cause of a broken install, so read the build error before assuming the package; and keep bun.lock in place, because Lovable installs with Bun.

Install

In Lovable, the install is a sentence in the chat: "Use the @buildbase/sdk npm package". On an exported copy, Lovable's docs say to install with Bun so the lockfile stays in sync (bun add @buildbase/sdk), and on any other host it is the usual line:

npm i @buildbase/sdk
lovable
vibe-coding
tanstack-start
auth
billing

Frequently Asked Questions

Does Lovable already have login?

Yes. Lovable Cloud signs people in by email, phone, Google, Apple, Microsoft, Sign in with Lovable, SAML SSO and OIDC, with no code from you. What its docs do not cover, as of 27 October 2026, is teams, roles, invites, plan limits, seats or credits for your end users. If you only need a login, keep the built-in one.

My Lovable app is older than May 2026. Does this work?

Not directly, because a React + Vite app has no server to hold the sign-in code exchange. Two honest routes: ask Lovable to upgrade the project to TanStack Start (its docs say most upgrades cost 10 to 35 credits and can be reverted from version history), or export to GitHub and host on a platform with serverless functions, following our Vite guide.

Can I keep Supabase or Lovable Cloud as my database?

Yes, and you should; BuildBase is not a database. The one wrinkle: row-level security policies keyed to the old sign-in do not know BuildBase users. Either read and write that data through server routes that check the BuildBase session, or add one route that mints a Supabase-shaped token. Both are in this guide.

Where does the client secret go in Lovable?

Under More, Settings, Secrets, as BUILDBASE_CLIENT_SECRET, never with a VITE_ prefix. Lovable writes secrets once and never shows them again, and anything prefixed VITE_ is public and inlined into the browser bundle at build time. The three public values go in .env, which Lovable asks you not to ignore in git.

How many credits will this cost me?

Lovable's own pricing page gives 1.20 credits as the example cost of adding authentication in build mode, and Pro is $25 a month for 100 credits as of 27 October 2026. The prompt in this guide is a few build messages. The expensive part of any Lovable change is a loop of fixes, which is what naming the exact files is meant to prevent.

Ship it

Create a project and run this guide against your own workspace.

Free plan7-day free trial on paid plansNo credit card required