This is sharing of integrating Supabase Auth using Plasmic Codegen in Plasmic. This is based on code in plasmic/examples/supabase-auth-nextjs-pages-codegen at master · plasmicapp/plasmic · GitHub
Supabase Auth accessible from Global Action in Plasmic Studio:
Sign In Action:
Sign Up Action:
Forgot Password Trigger: (call this first as Supabase will sent email with reset link)
Reset Password: (the action to enter new password)
Response and Error Message from Supabase Auth:
[1] npx create-plasmic-app@latest (creating new project!)
[1][0]-[ x] follow the flow. (Make sure to select NEXTJS, CODEGEN, Typescript.)
[2] Codes/ Folder to copy
[2][1] components/PasswordAuth.tsx
import React, { useState, useMemo } from "react";
import { createPagesBrowserClient } from "@supabase/auth-helpers-nextjs";
import { mutate } from "swr";
import { PLASMIC_AUTH_DATA_KEY } from "../utils/cache-keys";
import { GlobalActionsProvider, DataProvider } from "@plasmicapp/host";
import { useRouter } from "next/router";
const PasswordAuth: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const supabaseClient = createPagesBrowserClient();
const [latestResponse, setLatestResponse] = useState<any>(null);
const [authError, setAuthError] = useState<{ error: string } | null>(null);
const router = useRouter();
const actions = useMemo(
() => ({
signIn: async (email: string, password: string, redirectUrl?: string) => {
try {
const { data, error } = await supabaseClient.auth.signInWithPassword({ email, password });
await mutate(PLASMIC_AUTH_DATA_KEY);
if (error) {
setAuthError({
error: error.message || "unknown_error",
});
} else {
setLatestResponse(data);
setAuthError(null);
if (redirectUrl) router.push(redirectUrl);
}
return { data, error };
} catch (err: any) {
console.error("Sign In Error:", err);
setAuthError({
error: err.name || "unknown_error",
});
throw err;
}
},
signUp: async (email: string, password: string, roleId?: string, redirectUrl?: string) => {
try {
const { data, error } = await supabaseClient.auth.signUp({ email, password });
await mutate(PLASMIC_AUTH_DATA_KEY);
if (error) {
setAuthError({
error: error.message || "unknown_error",
});
} else {
const plasmicAuthDataResponse = await fetch("/api/plasmic-auth", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ action: "signUp", roleId }),
});
const plasmicAuthData = await plasmicAuthDataResponse.json();
console.log("Plasmic Auth Data:", plasmicAuthData);
setLatestResponse(data);
setAuthError(null);
if (redirectUrl) router.push(redirectUrl);
}
return { data, error };
} catch (err: any) {
console.error("Sign Up Error:", err);
setAuthError({
error: err.name || "unknown_error",
});
throw err;
}
},
signOut: async (redirectUrl?: string) => {
try {
const { error } = await supabaseClient.auth.signOut();
await mutate(PLASMIC_AUTH_DATA_KEY);
if (error) {
setAuthError({
error: error.message || "unknown_error",
});
} else {
setLatestResponse(null);
setAuthError(null);
if (redirectUrl) router.push(redirectUrl);
}
return { error };
} catch (err: any) {
console.error("Sign Out Error:", err);
setAuthError({
error: err.name || "unknown_error",
});
throw err;
}
},
forgotPassword: async (email: string, redirectUrl?: string) => {
try {
const origin = typeof window !== "undefined" ? window.location.origin : "";
const { data, error } = await supabaseClient.auth.resetPasswordForEmail(email, {
redirectTo: redirectUrl || `${origin}/reset-password`,
});
if (error) {
setAuthError({
error: error.message || "unknown_error",
});
} else {
setLatestResponse(data);
setAuthError(null);
}
return { data, error };
} catch (err: any) {
console.error("Forgot Password Error:", err);
setAuthError({
error: err.name || "unknown_error",
});
throw err;
}
},
updatePassword: async (newPassword: string, redirectUrl: string = "/sign-in") => {
try {
// Now you can safely use the extracted token without the "pkce_" prefix
const { data, error } = await supabaseClient.auth.updateUser( {
password: newPassword, // The new password to update
});
if (error) {
setAuthError({ error: error.message || "unknown_error" });
} else {
setLatestResponse(data);
setAuthError(null);
router.push(redirectUrl); // Redirect to the sign-in page after successful password update
}
return { data, error };
} catch (err: any) {
console.error("Update Password Error:", err);
setAuthError({ error: err.message || "unknown_error" });
throw err;
}
},
}),
[supabaseClient, router]
);
return (
<GlobalActionsProvider contextName="PasswordAuth" actions={actions}>
<DataProvider name="supabaseAuth" data={{ latestResponse, authError }}>
{children}
</DataProvider>
</GlobalActionsProvider>
);
};
export { PasswordAuth };
[2][2] pages/api/plasmic-auth.ts
import { createPagesServerClient } from "@supabase/auth-helpers-nextjs";
import type { NextApiRequest, NextApiResponse } from "next";
import { getPlasmicAuthData } from "../../utils/plasmic-auth";
// This API endpoint is used to provide the Plasmic user in client-side code.
export default async function getPlasmicAuthDataHandler(
req: NextApiRequest,
res: NextApiResponse
): Promise<void> {
try {
const { roleId } = req.body;
const supabaseServerClient = createPagesServerClient({ req, res });
const plasmicAuthData = await getPlasmicAuthData(supabaseServerClient, roleId);
res.status(200).json(plasmicAuthData);
} catch (error) {
console.error("Error in API handler:", error);
res.status(500).json({ error: "Internal Server Error" });
}
}
[2][3] pages/_app.tsx (just copy the relevant portion if you had changes made prior)
import '@/styles/globals.css'
import { PlasmicRootProvider } from "@plasmicapp/react-web";
import type { AppProps } from "next/app";
import Head from "next/head";
import { usePlasmicAuthData } from "../utils/usePlasmicAuth";
function PlasmicRootProviderWithUser(props: { children: React.ReactNode }) {
const { isUserLoading, plasmicUser, plasmicUserToken } = usePlasmicAuthData();
return (
<PlasmicRootProvider
Head={Head}
isUserLoading={isUserLoading}
user={plasmicUser}
userAuthToken={plasmicUserToken}
disableLoadingBoundary={true}
>
{props.children}
</PlasmicRootProvider>
);
}
export default function MyApp({ Component, pageProps }: AppProps) {
if (pageProps.withoutUseAuth) {
return (
<PlasmicRootProvider Head={Head}>
<Component {...pageProps} />
</PlasmicRootProvider>
);
}
return (
<PlasmicRootProviderWithUser>
<Component {...pageProps} />
</PlasmicRootProviderWithUser>
);
}
[2][4] pages/plasmic-host.tsx (copy the “registerGlobalContext(PasswordAuth…” and to import the component also)
import * as React from 'react';
import { PlasmicCanvasHost, registerGlobalContext, registerComponent } from '@plasmicapp/react-web/lib/host';
import {PasswordAuth} from '../components/PasswordAuth'
// You can register any code components that you want to use here; see
// https://docs.plasmic.app/learn/code-components-ref/
// And configure your Plasmic project to use the host url pointing at
// the /plasmic-host page of your nextjs app (for example,
// http://localhost:3000/plasmic-host). See
// https://docs.plasmic.app/learn/app-hosting/#set-a-plasmic-project-to-use-your-app-host
// registerComponent(...)
registerGlobalContext(PasswordAuth, {
name: "PasswordAuth",
props: {},
providesData: true,
globalActions: {
signIn: {
parameters: [
{ name: "email", type: "string" },
{ name: "password", type: "string" },
{ name: "redirectUrl", type: "string" },
],
},
signUp: {
parameters: [
{ name: "email", type: "string" },
{ name: "password", type: "string" },
{ name: "roleId", type: "string" },
{ name: "redirectUrl", type: "string" },
],
},
signOut: {
parameters:[
{ name: "redirectUrl", type: "string" },
],
},
forgotPassword: {
parameters: [
{ name: "email", type: "string" },
{ name: "redirectUrl", type: "string" },
],
},
updatePassword: {
parameters: [
{ name: "newPassword", type: "string" },
{ name: "redirectUrl", type: "string" },
],
},
},
importPath: "./components/PasswordAuth",
});
export default function PlasmicHost() {
return <PlasmicCanvasHost />;
}
[2][5] utils folder (create utils folder in your roots and copy over all 3 files from plasmic/examples/supabase-auth-nextjs-pages-codegen at master · plasmicapp/plasmic · GitHub)
[2][6] .env.local (create this file in your root directory and update the following variables accordingly)
[2][6][1] NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
[2][6][2] NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
[2][6][3] PLASMIC_AUTH_SECRET=your-plasmic-auth-secret (Authentication > Settings > Custom Auth > Token)
[2][7] middleware.ts (create this in your root directory. In this code place those web pages that are limited by access to public to protect it from public access).
import { NextRequest, NextResponse } from "next/server";
import { createMiddlewareClient } from "@supabase/auth-helpers-nextjs";
export default async function middleware(request: NextRequest): Promise<NextResponse> {
// Initialize Supabase client (res is omitted in middleware context)
const supabase = createMiddlewareClient({
req: request,
res: NextResponse.next(), // Provide a placeholder NextResponse
});
try {
// Get the user's session from Supabase
const {
data: { session },
} = await supabase.auth.getSession();
// Check if the user is signed in
if (!session) {
// Redirect to the homepage if unauthenticated
return NextResponse.redirect(new URL("/", request.url));
}
// Allow the request to proceed
return NextResponse.next();
} catch (error) {
console.error("Middleware error:", error);
// Redirect to an error page if something goes wrong
return NextResponse.redirect(new URL("/error", request.url));
}
}
// List of all secured/ private pages
export const config = {
matcher: ["/private-page" , "/secured-page"], // Applies to routes matching "/article"
};
[3] install dependencies
[3][1] npm install @plasmicapp/auth-api
[3][2] npm install @supabase/auth-helpers-nextjs (WARNING! Supabase has deprecated this. Use at your own risks.)
[3][3] npm i swr
[0] Reference
[0][1] New Plasmic codebase (CLI) | Learn Plasmic
[0][2] Auth integration | Learn Plasmic
[0][3] JavaScript API Reference | Supabase Docs
[0][4] Getting Started – SWR
[0][5] Password-based Auth | Supabase Docs
[0][6] plasmic/packages/auth-api at fca35655e7b175e26560ca009cb398060a6ab0d4 · plasmicapp/plasmic · GitHub
[0][7] Sign Up problem
[0][8] plasmic/examples/supabase-auth-nextjs-pages-codegen at master · plasmicapp/plasmic · GitHub
[0][9] Auth integration | Learn Plasmic
[10][0] File Conventions: middleware.js | Next.js