Build a Privacy-First App with Lovable + ConsentKeys in 10 Minutes
Learn how to add ConsentKeys authentication to your Lovable app with copy-paste prompts. This guide walks you through the entire process, from enabling Supabase to deploying your authenticated app.
What You'll Build
By the end of this tutorial, you'll have a Lovable app with:
- ✅ Privacy-focused authentication via ConsentKeys
- ✅ Supabase backend with Row Level Security
- ✅ User profiles that auto-create on signup
- ✅ Protected routes and authenticated API calls
Prerequisites
Before starting, you'll need:
| Item | Where to Get It |
|---|---|
| Lovable Account | lovable.dev |
| ConsentKeys Client ID & Secret | ConsentKeys Developer Portal |
| Supabase Account | Auto-created by Lovable when you enable integration |
Step 1: Enable Supabase Integration in Lovable
Before running any prompts, you MUST enable Supabase integration in Lovable. Without this, the database and Edge Functions won't work.
How to Enable:
- Open your Lovable project
- Click the Supabase icon in the left sidebar (or go to Project Settings)
- Click "Enable Supabase" or "Connect to Supabase"
- Wait for Lovable to provision your Supabase project
- Note down your Project URL and Project Reference (you'll need these!)
┌─────────────────────────────────────────────────────────────────────┐
│ 📍 Where to Find Your Supabase Details in Lovable: │
│ │
│ Project URL: https://xxxxxxxxxxxx.supabase.co │
│ Project Ref: xxxxxxxxxxxx (the part before .supabase.co) │
│ │
│ 💡 Keep these handy - you'll use them in the prompts below! │
└─────────────────────────────────────────────────────────────────────┘
Step 2: Register Your App with ConsentKeys
Before we start prompting Lovable, register your OAuth client:
- Go to the ConsentKeys Developer Portal
- Create a new application
- Set your Redirect URI to:
(Replace
https://YOUR_PROJECT_REF.supabase.co/functions/v1/oauth-callbackYOUR_PROJECT_REFwith your actual Supabase project reference) - Save your Client ID (
ck_xxxxx) and Client Secret
Step 3: Configure Supabase Secrets
In your Supabase dashboard (accessible via Lovable), add these secrets:
- Go to Settings → Edge Functions → Secrets
- Add these secrets:
| Secret Name | Value |
|---|---|
CONSENT_KEYS_CLIENT_SECRET | Your ConsentKeys client secret |
APP_URL | Your Lovable app URL (e.g., https://your-app.lovable.app) |
Lovable Prompts
Now for the fun part! Copy and paste these prompts into Lovable one at a time. Wait for each to complete before moving to the next.
Prompt 1: Create the OAuth Callback Edge Function
Copy the entire prompt below and paste it into Lovable's chat.
Create a Supabase Edge Function called "oauth-callback" that handles ConsentKeys OAuth authentication.
The function should:
1. Accept GET requests with an authorization code parameter
2. Exchange the code for an access token by calling https://api.consentkeys.com/token
3. Fetch user info from https://api.consentkeys.com/userinfo using the access token
4. Create or find the user in Supabase Auth using supabaseAdmin.auth.admin.createUser()
5. Generate a magic link using supabaseAdmin.auth.admin.generateLink()
6. Redirect to the magic link's action_link
Use these environment variables:
- CONSENT_KEYS_CLIENT_SECRET for the client secret
- APP_URL for where to redirect after login (use this in the magic link redirectTo option)
The ConsentKeys client ID is: ck_YOUR_CLIENT_ID_HERE
Important configuration:
- Set verify_jwt = false in the function config since this is a public OAuth callback
- Handle the case where the user already exists (email_exists error is OK, just continue)
- Include proper CORS headers for OPTIONS requests
The token endpoint expects:
- grant_type: authorization_code
- code: the auth code from the URL
- client_id: the ConsentKeys client ID
- client_secret: from CONSENT_KEYS_CLIENT_SECRET env var
- redirect_uri: the Edge Function URL
Store provider metadata in user_metadata:
- provider: "consent_keys"
- provider_id: the user's sub claim
- display_name: the user's name or email prefix
🔄 After running: Replace ck_YOUR_CLIENT_ID_HERE with your actual ConsentKeys Client ID.
Prompt 2: Create Database Schema with RLS
Create a database schema for user profiles with Row Level Security.
Create a table called "profiles" with these columns:
- id: UUID primary key with default gen_random_uuid()
- user_id: UUID, unique, references auth.users(id) with ON DELETE CASCADE
- display_name: text, nullable
- created_at: timestamptz with default now()
- updated_at: timestamptz with default now()
Enable Row Level Security (RLS) on the profiles table.
Create these RLS policies:
1. "Users can view their own profile" - SELECT where auth.uid() = user_id
2. "Users can update their own profile" - UPDATE where auth.uid() = user_id
3. "Users can insert their own profile" - INSERT with check auth.uid() = user_id
Create a database trigger that automatically creates a profile when a new user signs up:
- Function name: handle_new_user
- Trigger name: on_auth_user_created
- Fire AFTER INSERT on auth.users
- The function should insert into profiles with:
- user_id from NEW.id
- display_name from NEW.raw_user_meta_data->>'display_name' or fallback to NEW.email
- Use SECURITY DEFINER and SET search_path = public
Prompt 3: Create Authentication Hook
Create a React authentication hook and context for managing user state with Supabase.
Create a custom hook called useAuth in src/hooks/useAuth.tsx that:
1. Creates an AuthContext with user, session, loading, userProfile, and signOut
2. Uses supabase.auth.onAuthStateChange to listen for auth events
3. When a user is authenticated, fetches their profile from the profiles table
4. Provides a signOut function that calls supabase.auth.signOut()
5. Checks for existing session on mount with supabase.auth.getSession()
Export both AuthProvider component and useAuth hook.
The context should have these types:
- user: User | null (from @supabase/supabase-js)
- session: Session | null
- loading: boolean
- userProfile: any | null
- signOut: () => Promise<void>
Make sure to:
- Use a mounted flag to prevent state updates after unmount
- Clean up the subscription on unmount
- Handle the profile fetch with maybeSingle() to handle empty results
Prompt 4: Create Login Page with ConsentKeys Button
Create a beautiful login page that uses ConsentKeys for authentication.
The page should:
1. Have a hero section with a catchy headline about the app
2. Include a prominent "Sign in with ConsentKeys" button
3. Show loading state while checking auth
4. Redirect to /dashboard if user is already authenticated
5. Use the useAuth hook to check authentication state
The login button should redirect to ConsentKeys OAuth:
- URL: https://api.consentkeys.com/auth
- Query parameters:
- client_id: ck_YOUR_CLIENT_ID_HERE
- redirect_uri: https://YOUR_PROJECT_REF.supabase.co/functions/v1/oauth-callback
- response_type: code
- scope: openid profile email
Style the page with a modern, clean design using shadcn/ui components.
Include some feature highlights or benefits below the login card.
🔄 After running: Replace ck_YOUR_CLIENT_ID_HERE and YOUR_PROJECT_REF with your actual values.
Prompt 5: Create Protected Dashboard
Create a protected dashboard page that shows user information.
The page should:
1. Use the useAuth hook to get user and userProfile
2. Redirect to login page if not authenticated
3. Show loading state while auth is being checked
4. Display the user's display name or email
5. Show a logout button that calls signOut from useAuth
6. Include a welcome message with the user's name
Also create a ProtectedRoute component that:
- Wraps children components
- Checks if user is authenticated via useAuth
- Shows loading spinner while loading is true
- Redirects to "/" if user is null after loading completes
- Renders children if authenticated
Use shadcn/ui components for styling.
Add some placeholder dashboard content like cards or stats.
Prompt 6: Update App Router with Auth Provider
Update the App.tsx or main router to include the AuthProvider and all routes.
The app should:
1. Wrap all routes with the AuthProvider from useAuth
2. Have these routes:
- "/" - Login page (public)
- "/dashboard" - Protected dashboard
3. Use React Router for navigation
Make sure the AuthProvider wraps the BrowserRouter or is inside it depending on how routes need access to navigation.
Also ensure the Supabase client is properly initialized with:
- persistSession: true
- autoRefreshToken: true
- storage: localStorage
Prompt 7: Add User Profile Update Feature
Add a profile settings component to the dashboard where users can update their display name.
Create a ProfileSettings component that:
1. Shows a form with the current display name
2. Uses the userProfile from useAuth context
3. Allows updating the display name via Supabase
4. Shows success/error toast notifications
5. Updates the local state after successful update
Use shadcn/ui form components, Input, Button, and toast.
Include proper loading states during the update.
The update should:
- Use supabase.from('profiles').update()
- Filter by user_id equals auth.uid()
- Only allow updating their own profile (RLS handles this)
Verification Checklist
After running all prompts, verify your integration:
| Step | Test | Expected Result |
|---|---|---|
| 1 | Click "Sign in with ConsentKeys" | Redirects to ConsentKeys login |
| 2 | Complete authentication | Redirects back to your app |
| 3 | Check dashboard | Shows user info and logout button |
| 4 | Refresh page | Still logged in (session persists) |
| 5 | Click logout | Returns to login page |
| 6 | Try accessing /dashboard directly | Redirected to login if not authenticated |
Troubleshooting
"Edge Function not found"
Make sure you enabled Supabase integration in Lovable first. The Edge Function needs Supabase to be connected.
"Authorization code not found"
Check that your redirect URI in ConsentKeys Developer Portal matches exactly:
https://YOUR_PROJECT_REF.supabase.co/functions/v1/oauth-callback
"Failed to get token"
Verify that CONSENT_KEYS_CLIENT_SECRET is set in Supabase Edge Function secrets.
Session not persisting
Make sure your Supabase client has:
{
auth: {
persistSession: true,
autoRefreshToken: true,
storage: localStorage,
}
}
Profile not created
Check that the database trigger was created. You can verify in Supabase SQL Editor:
SELECT * FROM pg_trigger WHERE tgname = 'on_auth_user_created';
What's Next?
Now that you have authentication working, you can:
- Add more tables - Create tables for your app data with RLS policies
- Create protected API endpoints - Use Edge Functions with
verify_jwt = true - Add user features - Build on the profile with more user preferences
- Deploy to production - Lovable handles deployment automatically
Full Code Reference
Need to see the complete implementation? Check out the Supabase Integration Guide for detailed code examples and explanations.
Questions?
Happy building! 🚀