Kinde and edge worker services
Integrations
You can use Kinde as a custom OAuth provider in Supabase so users sign in with Kinde while Supabase manages the session in your app.
This guide walks through connecting Kinde to Supabase Auth, then adding sign-in, sign-up, and logout to a Next.js app using Supabase’s signInWithOAuth method. When you’re done, you can build on this setup with a to-do app.
Sign in to your Supabase dashboard.
If you don’t have a project yet, create one by selecting New Project and completing setup.
Go to Authentication > CONFIGURATION > Sign In / Providers.
Scroll down to the Custom Providers section and select New Provider. A window opens.
Enter the following details:
kindeEnter the Issuer URL: https://<your_business>.kinde.com or your Custom domain if you have one.
Enter the application details you copied from Kinde in the previous step:
Client ID
Client Secret
Scopes: openid, email, profile
To get a refresh token, include the offline scope.
Copy the Callback URL provided by Supabase. You’ll add this to Kinde next.
Select Update provider.
Go to your Kinde application Details page and paste the callback URL into the Allowed Callback URLs field. In the Allowed logout redirect URLs field, add your local and production app origins (for example, http://localhost:3000 and https://your-app.com) so users return to your app after logout. Select Save.
In your Supabase dashboard, go to Authentication > CONFIGURATION > URL Configuration:
http://localhost:3000 for local development or https://your-app.com for production./auth/callback routes, for example http://localhost:3000/auth/callback and your production URL (https://your-app.com/auth/callback).Add Kinde authentication to your Supabase app with the signInWithOAuth method:
const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'custom:kinde',});See the Supabase documentation for more details.
To add Kinde authentication to a Next.js app with Supabase:
To create a new Supabase Next.js project, run:
npx create-next-app -e with-supabaseAdd the following to your .env.local file:
NEXT_PUBLIC_KINDE_DOMAIN=https://<your_business>.kinde.comYou can use either your Kinde domain (https://your_business.kinde.com) or your Custom domain (https://auth.your_business.com). Use the same domain you used for the Issuer URL in the previous step.
Create the required files:
mkdir -p app/auth/callbacktouch components/login-button.tsxtouch components/register-button.tsxtouch app/auth/callback/route.tsOpen components/login-button.tsx, enter the following code, and save the file:
"use client";
import { createClient } from "@/lib/supabase/client";import { Button } from "@/components/ui/button";
export function LoginButton() { const login = async () => { const supabase = createClient(); await supabase.auth.signInWithOAuth({ provider: "custom:kinde", options: { redirectTo: `${window.location.origin}/auth/callback`, }, }); };
return ( <Button size="sm" variant="default" onClick={login}> Sign in </Button> );}Open components/register-button.tsx, enter the following code, and save the file:
"use client";
import { createClient } from "@/lib/supabase/client";import { Button } from "@/components/ui/button";
export function RegisterButton() { const register = async () => { const supabase = createClient(); await supabase.auth.signInWithOAuth({ provider: "custom:kinde", options: { queryParams: { prompt: "create", }, redirectTo: `${window.location.origin}/auth/callback`, }, }); };
return ( <Button size="sm" variant="default" onClick={register}> Sign up </Button> );}Open app/auth/callback/route.ts, enter the following code, and save the file:
import { createClient } from "@/lib/supabase/server";import { NextResponse } from "next/server";
export async function GET(request: Request) { const { searchParams, origin } = new URL(request.url); const code = searchParams.get("code"); const nextParam = searchParams.get("next") ?? "/protected"; // Only allow same-origin relative paths to prevent open redirects. const next = nextParam.startsWith("/") && !nextParam.startsWith("//") ? nextParam : "/protected";
if (code) { const supabase = await createClient(); const { error } = await supabase.auth.exchangeCodeForSession(code); if (!error) { return NextResponse.redirect(`${origin}${next}`); } }
return NextResponse.redirect(`${origin}/auth/error?error=oauth_callback_failed`);}Open components/auth-button.tsx and replace the login/sign-up page links with OAuth buttons:
Remove the Link imports and <Link href="/auth/login"> / <Link href="/auth/sign-up"> buttons.
Add the following imports:
import { LoginButton } from "./login-button";import { RegisterButton } from "./register-button";Update the logged-out state to render:
<div className="flex gap-2"> <LoginButton /> <RegisterButton /></div>Keep the logged-in state the same — show the user email and <LogoutButton />.
Open components/logout-button.tsx and replace the entire file with the following. This signs out of Supabase and redirects to Kinde logout:
"use client";
import { createClient } from "@/lib/supabase/client";import { Button } from "@/components/ui/button";
export function LogoutButton() { const logout = async () => { const supabase = createClient(); await supabase.auth.signOut(); const kindeDomain = process.env.NEXT_PUBLIC_KINDE_DOMAIN; const redirect = encodeURIComponent(window.location.origin); window.location.href = `${kindeDomain}/logout?redirect=${redirect}`; };
return <Button onClick={logout}>Logout</Button>;}Open lib/supabase/proxy.ts and change the unauthenticated redirect from /auth/login to /:
url.pathname = "/";Open app/protected/page.tsx and change the unauthenticated redirect from /auth/login to /:
redirect("/");Run the development server:
npm run devGo to http://localhost:3000 and select the Sign up button. You’re redirected to the Kinde hosted registration page.
Complete registration. You’re redirected to your app’s protected page.
Go to your Kinde dashboard > Users. You’ll see the new user in the list.
Go to your Supabase dashboard > Authentication > MANAGE > Users. You’ll see the same user there.
You’ve connected Kinde as a custom auth provider in Supabase and wired sign-in, sign-up, and logout into your Next.js app. New users appear in both your Kinde and Supabase dashboards.
Next, add user-specific data with PostgreSQL and Row-Level Security by building a to-do list app.