You built an app with v0, Lovable, Bolt, or Base44 in minutes. Now your team needs to use it with different access levels. Here is how to add the entire collaboration layer without touching a single line of code yourself.
In this article, you will learn:
- Why AI-generated apps ship without user management and what that means for your team
- What real multi-user collaboration actually requires under the hood
- How to build an AI-powered changelog tool in v0 as the demo app
- How to add Kinde auth, protected routes, roles, and organization workspaces through prompts alone
- How to test that the collaboration layer actually works
Let's dive in!
What AI Builders Give You and What They Don't
v0, Lovable, Bolt, and Base44 are remarkable. You describe what you want, and in minutes you have a working, deployable application with UI, logic, and routes. This has genuinely changed what one person can ship in an afternoon.
What none of them give you by default is user management. When your AI builder generates an app, everyone who visits the URL sees the same thing. There is no concept of "Alice is an admin and can publish" or "Bob is a viewer and can only read." There are no team workspaces. There is no authentication.
Adding that layer after the fact is where most people get stuck, because it usually means diving into the generated code and understanding how it works, which defeats the whole point.
This article shows you how to do it entirely through prompts, using Kinde for auth, roles, and team management. The demo app is an AI changelog generator, a tool your team can use to turn raw GitHub commits or release notes into polished public changelogs. Admins publish. Members draft. The public reads.
What Real Team Collaboration Requires
Before you prompt anything, being clear on what collaboration actually means in a web app saves a lot of confusion later. Four things need to work together:
| Layer | What it means | What Kinde provides |
|---|---|---|
| Authentication | Users can sign in and sign out | Login, sign-up, passwordless OTP, social login |
| Identity | The app knows who is logged in | User object with name, email, ID on every request |
| Roles | Different users can do different things | Roles (admin, member, viewer) attached to users |
| Organizations | Users belong to a team workspace | Orgs that group users with isolated data |
You need all four for a real product. A lot of AI-generated apps nail authentication but skip the rest, and then wonder why every user sees every other user's data.
Kinde handles all four out of the box, free for up to 10,500 monthly active users, no credit card required.
What You Need Before You Start
A v0.app account. Sign up at v0.app. Free tier available. The prompts in this article are written for v0 but work in Lovable, Bolt, and Base44 with minor adjustments.
A Kinde account. Sign up at kinde.com. Free up to 10,500 monthly active users, no credit card.
Once you have your Kinde account, create an application for your project. Navigate to Settings → Applications → Add application. Give it a name, select Back-end web as the application type, and hit Save.
Then open the application you just created and navigate to View details to grab these values:
KINDE_CLIENT_IDKINDE_CLIENT_SECRET-
KINDE_ISSUER_URL(your Kinde domain, e.g.https://yourapp.kinde.com)
And two you define yourself:
-
KINDE_SITE_URLwhere your app runs locally (http://localhost:3000) -
KINDE_POST_LOGIN_REDIRECT_URLwhere users land after login (http://localhost:3000/dashboard) -
KINDE_POST_LOGOUT_REDIRECT_URLwhere users land after logout (http://localhost:3000)
Keep these open in a tab. You will paste them into v0 shortly.
Step #1: Build the Base App in v0
The demo app is an AI changelog generator. Teams paste in raw commit messages or release notes, the app uses an AI model to turn them into readable entries, and admins publish them to a public page.
Open v0.app and use this prompt:
Prompt:
Build an AI changelog generator for software teams. The app should have:
- A landing page with the product name "Changelog" and two buttons: "Sign in" and "Start free"
- A dashboard at /dashboard with:
- A sidebar with: Dashboard, New Entry, Published, Team, Settings
- A main area showing a list of changelog entries as cards, each with: version number, title, date, status badge (Draft / Published), and Edit + Delete buttons
- A "New Entry" page at /dashboard/new with:
- A text area labeled "Paste your commits or release notes"
- A "Generate with AI" button
- A title field, version field, and content editor that populates after generation
- A "Save as draft" and "Publish" button
- A public changelog page at /changelog showing only published entries in clean readable format
Use Next.js App Router with TypeScript. Clean, minimal design. Do not add any authentication yet.
Let v0 generate the app. Once the preview looks right, push it to GitHub using v0's built-in GitHub integration.
Step #2: Wire In Kinde Auth
Now prompt v0 to install Kinde and set up the auth infrastructure. One prompt handles everything.
Prompt:
Add Kinde authentication to this Next.js app using @kinde-oss/kinde-auth-nextjs.
Do the following:
1. Install the package
2. Create the auth route handler at app/api/auth/[kindeAuth]/route.ts using handleAuth from @kinde-oss/kinde-auth-nextjs/server
3. Wrap the root layout in app/layout.tsx with KindeAuthProvider from @kinde-oss/kinde-auth-nextjs/server
4. Create a .env.local file with these placeholder variables:
KINDE_CLIENT_ID=
KINDE_CLIENT_SECRET=
KINDE_ISSUER_URL=
KINDE_SITE_URL=http://localhost:3000
KINDE_POST_LOGIN_REDIRECT_URL=http://localhost:3000/dashboard
KINDE_POST_LOGOUT_REDIRECT_URL=http://localhost:3000
5. No UI changes yet — just the plumbing
Once v0 finishes, click on the Settings button in v0's editor and then go to Environment Variables tab to fill in your actual Kinde values. Do not commit this file to GitHub. It is gitignored by default.
Then add your callback URLs in the Kinde dashboard. Navigate to Settings → Applications → View details → Callback URLs and add:
- Allowed callback URL:
http://<your_v0_published_url>/api/auth/kinde_callback - Allowed logout redirect URL:
http://<your_v0_published_url>
Step #3: Add Login and Sign-Up Buttons
Prompt:
Update the landing page and dashboard to use Kinde auth components.
Do the following:
1. On the landing page (app/page.tsx), replace the "Sign in" button with LoginLink and "Start free" with RegisterLink:
import { LoginLink, RegisterLink } from "@kinde-oss/kinde-auth-nextjs/components";
<LoginLink postLoginRedirectURL="/dashboard">Sign in</LoginLink>
<RegisterLink postLoginRedirectURL="/dashboard">Start free</RegisterLink>
2. In the dashboard sidebar, show the logged-in user's name and email using getKindeServerSession:
import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
const { getUser } = getKindeServerSession();
const user = await getUser();
Show user.given_name and user.email in the sidebar footer.
3. Add a sign-out link at the bottom of the sidebar that links to /api/auth/logout
Step #4: Protect the Dashboard Routes
Right now anyone can navigate directly to /dashboard without logging in. The Kinde middleware fixes this with one file.
Prompt:
Add route protection using Kinde middleware.
Create a file at middleware.ts in the project root with exactly this content:
import { withAuth } from "@kinde-oss/kinde-auth-nextjs/middleware";
export default withAuth;
export const config = {
matcher: ["/dashboard/:path*"]
};
This automatically redirects any unauthenticated request to /dashboard or any sub-route to the Kinde login page.
Also add an authentication check at the top of app/dashboard/page.tsx:
import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
import { redirect } from "next/navigation";
const { isAuthenticated } = getKindeServerSession();
if (!(await isAuthenticated())) {
redirect("/api/auth/login");
}
The public /changelog page stays open to anyone. Everything under /dashboard now requires a logged-in user.
Step #5: Add Roles for Admins, Members, and Viewers
This is where the collaboration layer becomes real. Not everyone on the team should be able to publish or delete changelog entries.
First, create the roles in Kinde. Navigate to Roles → Add role and create three:
-
admin— can publish, edit, delete all entries, and manage team -
member— can create and edit drafts, cannot publish or delete -
viewer— read-only access to the dashboard
Assign yourself the admin role: navigate to Users → select your user → Roles → assign admin.
Now prompt v0 to use those roles in the UI:
Prompt:
Add role-based access control to the dashboard using Kinde roles.
The roles are: admin, member, viewer. These are configured in the Kinde dashboard.
Do the following:
1. In app/dashboard/page.tsx, get the user's roles:
const { getRoles } = getKindeServerSession();
const roles = await getRoles();
const isAdmin = roles?.some(role => role.key === "admin");
const isMember = roles?.some(role => role.key === "member" || role.key === "admin");
2. On each changelog entry card:
- Show "Edit" button for admins and members
- Show "Delete" button only for admins
- Show "Publish" button only for admins
- Viewers see no action buttons
3. In the sidebar:
- Show "New Entry" link for admins and members only
- Show "Team" link for admins only
- Viewers see Dashboard and Published only
4. On the "New Entry" page:
- Show the "Publish" button only for admins
- Members see "Save as draft" only
Use conditional rendering, not CSS visibility, so the DOM does not contain controls the user cannot access.
Step #6: Add Organization Workspaces
Roles tell your app what a user can do. Organizations tell your app what data they can see. Without organizations, every user shares every changelog entry, which means one company's unreleased changelog is visible to everyone else.
Kinde organizations are isolated team workspaces. Each organization has its own members and its own data.
Kinde allows organization creation by default when is_create_org is passed during sign-up. No dashboard setting needs to change. If you ever want to disable this behaviour, the toggle is at Settings → Environment → Policies → Allow organization creation on sign up.
Prompt:
Update the app to support Kinde organizations as team workspaces.
Do the following:
1. Update the "Start free" RegisterLink on the landing page to create an org on sign-up:
<RegisterLink
authUrlParams={{ is_create_org: "true" }}
postLoginRedirectURL="/dashboard"
>
Start free
</RegisterLink>
2. In app/dashboard/page.tsx, get the current org:
const { getOrganization } = getKindeServerSession();
const org = await getOrganization();
const orgCode = org?.orgCode;
3. Show the organization name (org?.name) at the top of the sidebar above the navigation links so users always know which workspace they are in.
4. Add a "Switch workspace" link below the org name linking to /api/auth/login?prompt=select_account for users who belong to multiple organizations.
5. For all data queries (changelog entries), add a comment: // TODO: filter by orgCode so it is clear where the org scoping goes when a real database is connected.
Step #7: Add the AI Generation Feature
The "Generate with AI" button on the New Entry page should take raw commit messages and return a polished changelog entry. v0 can wire this up using the Vercel AI SDK.
Prompt:
Add AI-powered changelog generation to the New Entry page using the Vercel AI SDK.
Do the following:
1. Install the ai and @ai-sdk/openai packages
2. Create a server action at app/actions/generate.ts:
"use server";
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
export async function generateChangelog(rawInput: string) {
const { text } = await generateText({
model: openai("gpt-4o-mini"),
prompt: `You are a technical writer. Convert the following raw commit messages or release notes into a clean, professional changelog entry. Use plain language a non-technical reader can understand. Format it as a short paragraph followed by a bullet list of changes.
Raw input:
${rawInput}
Return only the changelog text, nothing else.`,
});
return text;
}
3. On the New Entry page, wire the "Generate with AI" button to call this action with the content of the text area. Show a loading state while generating. Populate the content editor with the result when done.
4. Add OPENAI_API_KEY= to .env.local as a placeholder — I will fill this in.
Putting It All Together
Here is every prompt in sequence and what each one adds:
| Prompt | What it built | What Kinde provided |
|---|---|---|
| 1 — Base app | Changelog dashboard shell | — |
| 2 — Kinde install | Auth route handler, KindeAuthProvider | Session management, JWT |
| 3 — Login UI | LoginLink, RegisterLink, user in sidebar | User identity |
| 4 — Route protection | withAuth middleware | Unauthenticated redirect |
| 5 — Roles | getRoles(), conditional UI rendering | RBAC — admin/member/viewer |
| 6 — Organizations | getOrganization(), org name in sidebar | Team workspace isolation |
| 7 — AI generation | generateChangelog server action | — |
Seven prompts. A complete collaboration layer. No hand-written code.
Testing Your Collaboration Layer
Before sharing the app, verify these scenarios work:
Test 1 — Unauthenticated block. Open an incognito window and navigate to /dashboard. You should land on the Kinde login page.
Test 2 — Sign up with workspace. Click "Start free" and complete sign-up. You should land on the dashboard with your organization name visible in the sidebar.
Test 3 — Role-based UI. Sign in as your admin user and confirm you see Publish, Delete, and the Team link. Then change your role to viewer in the Kinde dashboard, refresh, and confirm those controls are gone.
Test 4 — AI generation. Paste a few commit messages into the New Entry page and click "Generate with AI." The content editor should populate with a polished changelog entry.
Test 5 — Workspace isolation. Create a second Kinde user and have them sign up with a different organization name. Log in as user one, create a draft entry. Log in as user two and confirm they cannot see user one's entries.
What Is Next
This article built the collaboration shell. The next steps to make it production-ready:
Connect a real database. Add Convex or Supabase and filter all queries by orgCode from getOrganization(). Every entry and every piece of data scoped to the team that created it.
Add team invites. Use the Kinde Management API to let admins invite colleagues by email directly from the Team settings page.
Make the public changelog live. The /changelog route is already public. Connect it to the published entries in your database and it becomes a real public-facing changelog page for your product.
The auth infrastructure is already in place. Everything else builds on top of it.
Create a free Kinde account if you have not already, and try this on whatever app you are building.











Top comments (0)