Skip to main content

Build a Privacy-First App with Lovable + ConsentKeys in 10 Minutes

· 8 min read

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:

ItemWhere to Get It
Lovable Accountlovable.dev
ConsentKeys Client ID & SecretConsentKeys Developer Portal
Supabase AccountAuto-created by Lovable when you enable integration

Step 1: Enable Supabase Integration in Lovable

Important First Step

Before running any prompts, you MUST enable Supabase integration in Lovable. Without this, the database and Edge Functions won't work.

How to Enable:

  1. Open your Lovable project
  2. Click the Supabase icon in the left sidebar (or go to Project Settings)
  3. Click "Enable Supabase" or "Connect to Supabase"
  4. Wait for Lovable to provision your Supabase project
  5. 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:

  1. Go to the ConsentKeys Developer Portal
  2. Create a new application
  3. Set your Redirect URI to:
    https://YOUR_PROJECT_REF.supabase.co/functions/v1/oauth-callback
    (Replace YOUR_PROJECT_REF with your actual Supabase project reference)
  4. Save your Client ID (ck_xxxxx) and Client Secret

Step 3: Configure Supabase Secrets

In your Supabase dashboard (accessible via Lovable), add these secrets:

  1. Go to Settings → Edge Functions → Secrets
  2. Add these secrets:
Secret NameValue
CONSENT_KEYS_CLIENT_SECRETYour ConsentKeys client secret
APP_URLYour 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 This Prompt

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:

StepTestExpected Result
1Click "Sign in with ConsentKeys"Redirects to ConsentKeys login
2Complete authenticationRedirects back to your app
3Check dashboardShows user info and logout button
4Refresh pageStill logged in (session persists)
5Click logoutReturns to login page
6Try accessing /dashboard directlyRedirected 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:

  1. Add more tables - Create tables for your app data with RLS policies
  2. Create protected API endpoints - Use Edge Functions with verify_jwt = true
  3. Add user features - Build on the profile with more user preferences
  4. 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! 🚀