deur Gabriel L. Manor Supabase maak dit maklik om outentieking by jou app te voeg met ingeboude ondersteuning vir e-pos, OAuth en magiese skakels.Maar terwyl Supabase Auth hanteer wie jou gebruikers is, benodig jy dikwels ook 'n outentieklaag. Supabase bied 'n goeie backend met ingeboude auth en Row Level Security (RLS), bestuur Vooral diegene wat gebaseer is op Dit is ver van maklik. fine-grained permissions relationships between users and data U wil dalk aksie beperk, soos die redigering of verwydering van data aan hulpbron eienaars, gebruikers te verhoed om te stem op hul eie inhoud, of die handhawing van verskillende toestemmings vir verskillende gebruiker rolle. Hierdie tutorial gaan deur hoe om te implementeer in die a die toepassing. Supabase authentication and authorization Next.js Ons sal begin met vir login en sessie bestuur, dan voeg gebruik Verpligting deur en a Die Supabase Auth authorization rules Gebaseerde toegangsbeheer (ReBAC) Supabase Edge Functions local Policy Decision Point (PDP) Uiteindelik sal u 'n real-time gesamentlike polling-app hê wat beide openbare en beskermde aksies ondersteun - en 'n buigsaamheidstelsel wat u kan ontwikkel as u app groei. Wat ons bou In hierdie gids, sal ons 'n real-time polling app bou met behulp van en Dit toon beide authenticasie en autorisasie in aksie. Supabase Next.js Die app laat gebruikers toe om sonde te skep, oor ander te stem en slegs hul eie inhoud te bestuur. vir login / handtekening en hoe om dit uit te voer die beheer wat kan stem, redig, of verwyder. Supabase Auth authorization policies Ons sal die kernkenmerke van Supabase gebruik— die die die , and Gekombineer met 'n model om per gebruiker en per hulpbron toegangsreëls te handhaaf. Auth Postgres RLS Realtime Edge Functions Relationship-Based Access Control (ReBAC) Die Tech Stack die die die die die Supabase – Backend-as-a-service vir databasis, outentiek, werklike tyd en kant funksies Next.js – Frontend framework vir die bou van die app UI en API roetes Permit.io – (vir ReBAC) om autorisasie logika te definieer en te evalueer via PDP – To manage and deploy Edge Functions locally and in production Geskryf deur CLI Vermoed Die volgende.js Toelaat Geskryf deur CLI Voorwaardes die die die die die die Node.js word geïnstalleer Basiese rekening Toelaat.io rekening Kennis van React/Next.js Die begin van die repo-projek Wat kan hierdie app doen? Die demo-toepassing is 'n real-time polling platform gebou met Next.js en Supabase, waar gebruikers polls kan skep en stem oor ander. die die die die die Elke gebruiker (geauthenteer of nie) kan die lys van openbare sonde sien Slegs geauthentifiseerde gebruikers kan sonde skep en stem 'N Gebruiker kan nie stem op 'n poll wat hulle geskep het nie Slegs die skepper van 'n opname kan dit bewerk of verwyder Tutoriale oorsig Ons sal hierdie algemene stappe volg: die die die die die die Stel die Supabase-projek, skema, auth en RLS op Bouw kernapp funksies soos opstel van sonde en stemming Modelle autorisasie reëls definieer rolle en reëls in Permit.io Skep Supabase Edge-funksie vir die sinchronisering van gebruikers, die toekenning van rolle en die kontroleer van toestemmings Uitvoer beleid in die app frontend met behulp van daardie edge funksies Kom ons begin - Setting up Supabase in the Project Skep 'n Supabase in die projek Optioneel: Klon die Starter Template Ek het reeds 'n die met al die kode wat jy nodig het om te begin sodat ons kan fokus op die implementering van Supabase en Permit.io. Die tempel begin GitHub Jy kan die projek kloneer deur die volgende bevel uit te voer: git clone <https://github.com/permitio/supabase-fine-grained-authorization> Sodra jy die projek gekloon het, navigeer na die projek directory en installeer die afhankings: cd realtime-polling-app-nextjs-supabase-permitio npm install Maak 'n nuwe projek in Supabase Om te begin: die die die die Gaan na https://supabase.com en log in of skep 'n rekening. Klik op "Nuwe projek" en vul jou projeknaam, wagwoord en streek in. Sodra dit geskep is, gaan na Project-instellings → API en let op jou Project-URL en Anon-sleutel - jy sal hulle later nodig hê. Authentiekering en databasis in Supabase Ons sal Supabase se ingeboude e-pos / wagwoord auth gebruik: die die die die In die zijbalk, gaan na Authentification → Providers Toelaat die e-pos verskaffer (Optioneel) Deaktiveer bevestigings-e-pos vir toets, maar hou dit geaktiveer vir produksie Die skep van die databasis skema Hierdie app gebruik drie hoof tabelle: die en Gebruik die in die Supabase dashboard en die volgende uit te voer: polls options votes SQL Editor -- Create a polls table CREATE TABLE polls ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, question TEXT NOT NULL, created_by UUID REFERENCES auth.users(id) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()), creator_name TEXT NOT NULL, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, ); -- Create an options table CREATE TABLE options ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, poll_id UUID REFERENCES polls(id) ON DELETE CASCADE, text TEXT NOT NULL, ); -- Create a votes table CREATE TABLE votes ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, poll_id UUID REFERENCES polls(id) ON DELETE CASCADE, option_id UUID REFERENCES options(id) ON DELETE CASCADE, user_id UUID REFERENCES auth.users(id), created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()), UNIQUE(poll_id, user_id) ); Bepaal die vlak van veiligheid (RLS) Toelaat vir elke tabel en definieer beleid: Die RLS -- Polls policies ALTER TABLE polls ENABLE ROW LEVEL SECURITY; CREATE POLICY "Anyone can view polls" ON polls FOR SELECT USING (true); CREATE POLICY "Authenticated users can create polls" ON polls FOR INSERT TO authenticated WITH CHECK (auth.uid() = created_by); -- Options policies ALTER TABLE options ENABLE ROW LEVEL SECURITY; CREATE POLICY "Anyone can view options" ON options FOR SELECT USING (true); CREATE POLICY "Poll creators can add options" ON options FOR INSERT TO authenticated WITH CHECK ( EXISTS ( SELECT 1 FROM polls WHERE id = options.poll_id AND created_by = auth.uid() ) ); -- Votes policies ALTER TABLE votes ENABLE ROW LEVEL SECURITY; CREATE POLICY "Anyone can view votes" ON votes FOR SELECT USING (true); CREATE POLICY "Authenticated users can vote once" ON votes FOR INSERT TO authenticated WITH CHECK ( auth.uid() = user_id AND NOT EXISTS ( SELECT 1 FROM polls WHERE id = votes.poll_id AND created_by = auth.uid() ) ); Om die real-time funksies van Supabase te gebruik: die die die In die sidebar, gaan na Table Editor Vir elkeen van die drie tabelle (opnames, opsies, stemme): Klik op die drie punte → Redig Tabel Toggle "Aktiewe Realtime" Bewaar veranderinge Implementeer Supabase Email Authentification in die App In hierdie demo-app kan enige persoon die lys van opnames wat beskikbaar is op die app sien, beide aktief en verval. Om die besonderhede van 'n opname te sien, te bestuur, of te stem op enige opname, moet die gebruiker aangemeld wees. Ons sal e-pos en wagwoord gebruik as 'n manier van outentiek vir hierdie projek. : die .env.local NEXT_PUBLIC_SUPABASE_URL=your_supabase_url NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key Opdater jou login komponent om beide registrasie en login via e-pos / wagwoord te hanteer: import { useState } from "react"; import { createClient } from "@/utils/supabase/component"; const LogInButton = () => { const supabase = createClient(); async function logIn() { const { error } = await supabase.auth.signInWithPassword({ email, password, }); if (error) { setError(error.message); } else { setShowModal(false); } } async function signUp() { const { error } = await supabase.auth.signUp({ email, password, options: { data: { user_name: userName, }, }, }); if (error) { setError(error.message); } else { setShowModal(false); } } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(""); if (isLogin) { await logIn(); } else { await signUp(); } }; return ( <> <button onClick={() => setShowModal(true)} className="flex items-center gap-2 p-2 bg-gray-800 text-white rounded-md"> Log In </button> ... </> ); }; export default LogInButton; Hier, ons gebruik Supabase se die metode om te log in 'n gebruiker en die methode om 'n nuwe gebruiker te registreer met hul e-pos en wagwoord. Ons sal ook die gebruikersnaam in die die metadata van die gebruiker. signInWithPassword signUp user_name Jy kan ook gebruik Om gebruikers uit te log en hulle te oorgedra: supabase.auth.signOut() import { createClient } from "@/utils/supabase/component"; import { useRouter } from "next/router"; const LogOutButton = ({ closeDropdown }: { closeDropdown: () => void }) => { const router = useRouter(); const supabase = createClient(); const handleLogOut = async () => { await supabase.auth.signOut(); closeDropdown(); router.push("/"); }; return ( ... ); }; export default LogOutButton; Hier gebruik ons die methode van Supabase om die gebruiker uit te log en hulle na die tuisblad te oorgedra. signOut Luister na veranderinge in die gebruiker se authenticatie-toestand Die luister na veranderinge in die gebruikers-authentifikasie-toestand laat ons toe om die UI op grond van die gebruikers-authentifikasie-toestand te actualiseer. die die Show/hide UI elements like login/logout buttons Voorwaardelike beperking van toegang tot beskermde bladsye (soos stem of bestuur van sonde) Ensure only authenticated users can perform restricted actions Ons sal gebruik om hierdie gebeure te luister en die app dienlik te actualiseer. supabase.auth.onAuthStateChange() die die In the Layout.tsx file: Track Global Auth State import React, { useEffect, useState } from "react"; import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const Layout = ({ children }: { children: React.ReactNode }) => { const [user, setUser] = useState<User | null>(null); useEffect(() => { const fetchUser = async () => { const supabase = createClient(); const { data } = supabase.auth.onAuthStateChange((event, session) => { setUser(session?.user || null); }); return () => { data.subscription.unsubscribe(); }; }; fetchUser(); }, []); return ( ... ); }; export default Layout; Restrict Access on Protected Pages Op bladsye soos or , moet jy ook luister na oordragstaatsveranderinge om te verhoed dat nie-oordragte gebruikers toegang tot hulle kry. poll details poll management Hier is hoe dit lyk in : die pages/polls/[id].tsx import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const Page = () => { const [user, setUser] = useState<User | null>(null); useEffect(() => { const fetchUser = async () => { const supabase = createClient(); const { data } = supabase.auth.onAuthStateChange((event, session) => { setUser(session?.user || null); setLoading(false); }); return () => { data.subscription.unsubscribe(); }; }; fetchUser(); }, []); return ( ... ); export default Page; En 'n soortgelyke patroon is van toepassing in , waar gebruikers net hul eie navorsings moet sien as hulle aangemeld is: pages/polls/manage.tsx import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const Page = () => { const [user, setUser] = useState<User | null>(null); const supabase = createClient(); useEffect(() => { const fetchUser = async () => { const { data } = supabase.auth.onAuthStateChange((event, session) => { setUser(session?.user || null); if (!session?.user) { setLoading(false); } }); return () => { data.subscription.unsubscribe(); }; }; fetchUser(); }, []); return ( ... ); }; export default Page; Hierdie patrone maak seker jou UI weerspieël die gebruiker se huidige outentiekstatus en vorm die basis vir die outentiekskoers wat ons later sal byvoeg. Die voorwerp wanneer die oproep Edge Funksie om te bepaal of 'n gebruiker toegelaat word om te stem of 'n spesifieke opname te bestuur. user checkPermission Opbou van die Polling App-funksionaliteit Met Supabase gekonfigureer en verifikasie werk, kan ons nou die kernfunksionaliteit van die polling-program bou. Creating new polls Opname en weergave van opnames in werklike tyd Invoering van 'n stemmingstelsel Dit gee ons die basiese app-gedrag wat ons binnekort sal beskerm met goedgekeurde toestemmings. Creating New Polls Gebruikers moet aangemeld wees om opnames te skep. Elke opname bevat 'n vraag, 'n vervaldatum en 'n stel opsies. Ons registreer ook wie die opname geskep het sodat ons later daardie verhouding kan gebruik vir toegangskontrole. Binne , kry die geauthentifiseerde gebruiker, en gebruik Supabase om die opname en sy opsies in te voeg: NewPoll.tsx import React, { useEffect, useState } from "react"; import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const NewPoll = () => { const [user, setUser] = useState<User | null>(null); const supabase = createClient(); useEffect(() => { const fetchUser = async () => { const supabase = createClient(); const { data } = supabase.auth.onAuthStateChange((event, session) => { setUser(session?.user || null); }); return () => { data.subscription.unsubscribe(); }; }; fetchUser(); }, []); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (question.trim() && options.filter(opt => opt.trim()).length < 2) { setErrorMessage("Please provide a question and at least two options."); return; } // Create the poll const { data: poll, error: pollError } = await supabase .from("polls") .insert({ question, expires_at: new Date(expiryDate).toISOString(), created_by: user?.id, creator_name: user?.user_metadata?.user_name, }) .select() .single(); if (pollError) { console.error("Error creating poll:", pollError); setErrorMessage(pollError.message); return; } // Create the options const { error: optionsError } = await supabase.from("options").insert( options .filter(opt => opt.trim()) .map(text => ({ poll_id: poll.id, text, })) ); if (!optionsError) { setSuccessMessage("Poll created successfully!"); handleCancel(); } else { console.error("Error creating options:", optionsError); setErrorMessage(optionsError.message); } }; return ( ... ); }; export default NewPoll; Ons sal later 'n Edge-funksie hier oproep om die "skepper" -rol in Permit.io toe te pas. Opname en vertoon van opnames Polls are divided into (Nog nie verby is nie) en (expired). You can fetch them using Supabase queries filtered by the current timestamp, and set up real-time subscriptions to reflect changes instantly. active past Voorbeeld van : die pages/index.tsx import { PollProps } from "@/helpers"; import { createClient } from "@/utils/supabase/component"; export default function Home() { const supabase = createClient(); useEffect(() => { const fetchPolls = async () => { setLoading(true); const now = new Date().toISOString(); try { // Fetch active polls const { data: activePolls, error: activeError } = await supabase .from("polls") .select( ` id, question, expires_at, creator_name, created_by, votes (count) ` ) .gte("expires_at", now) .order("created_at", { ascending: false }); if (activeError) { console.error("Error fetching active polls:", activeError); return; } // Fetch past polls const { data: expiredPolls, error: pastError } = await supabase .from("polls") .select( ` id, question, expires_at, creator_name, created_by, votes (count) ` ) .lt("expires_at", now) .order("created_at", { ascending: false }); if (pastError) { console.error("Error fetching past polls:", pastError); return; } setCurrentPolls(activePolls); setPastPolls(expiredPolls); } catch (error) { console.error("Unexpected error fetching polls:", error); } finally { setLoading(false); } }; fetchPolls(); // Set up real-time subscription on the polls table: const channel = supabase .channel("polls") .on( "postgres_changes", { event: "*", schema: "public", table: "polls", }, fetchPolls ) .subscribe(); return () => { supabase.removeChannel(channel); }; }, []); return ( ... ); } Sien en bestuur gebruikers opnames Hier kry ons aktiewe en verlede sonde van die die tabel in Supabase. Ons stel ook 'n real-time-abonnement in om na veranderinge in die Tabel sodat ons die UI met die nuutste navorsingsdata kan actualiseer.Om tussen aktiewe en vorige navorsings te onderskei, vergelyk ons die vervaldatum van elke navorsing met die huidige datum. polls polls Opdatering van die bladsy om slegs sonde wat deur die gebruiker geskep is, te vang en weer te stel: pages/manage.tsx import { PollProps } from "@/helpers"; const Page = () => { useEffect(() => { if (!user?.id) return; const fetchPolls = async () => { try { const { data, error } = await supabase .from("polls") .select( ` id, question, expires_at, creator_name, created_by, votes (count) ` ) .eq("created_by", user.id) .order("created_at", { ascending: false }); if (error) { console.error("Error fetching polls:", error); return; } setPolls(data || []); } catch (error) { console.error("Unexpected error fetching polls:", error); } finally { setLoading(false); } }; fetchPolls(); // Set up real-time subscription const channel = supabase .channel(`polls_${user.id}`) .on( "postgres_changes", { event: "*", schema: "public", table: "polls", filter: `created_by=eq.${user.id}`, }, fetchPolls ) .subscribe(); return () => { supabase.removeChannel(channel); }; }, [user]); return ( ... ); }; export default Page; Hier kry ons slegs sonde wat deur die gebruiker geskep is en luister na real-time updates in die Tabel sodat die UI opgedateer word met die nuutste poll data. polls Op die oomblik is die update component so that if a logged-in user is the poll creator, icons for editing and deleting the poll will be displayed to them on the poll. PollCard import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const PollCard = ({ poll }: { poll: PollProps }) => { const [user, setUser] = useState<User | null>(null); useEffect(() => { const supabase = createClient(); const fetchUser = async () => { const { data } = supabase.auth.onAuthStateChange((event, session) => { setUser(session?.user || null); setLoading(false); }); return () => { data.subscription.unsubscribe(); }; }; fetchUser(); }, []); return ( ... )} </Link> ); }; export default PollCard; So nou, op 'n opname kaart, as die ingelogde gebruiker is die opname maker, ikone vir die redigering en verwydering van die opname sal vir hulle weergegee word. Die implementering van die stemmingstelsel Die stemmingslogika impliseer: die die Only one vote per user per poll Skepters kan nie op hul eie sonde stem nie Stemmen word in die stemmingstabel gestoor Resultate word weergegee en opgedateer in werklike tyd Kom ons breek af hoe dit werk in die component: ViewPoll.tsx We need the current user’s ID to determine voting eligibility and record their vote. Fetch the Logged-In User import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const ViewPoll = () => { const [user, setUser] = useState<User | null>(null); const supabase = createClient(); useEffect(() const fetchUser = async () => { const { data: { user }, } = await supabase.auth.getUser(); setUser(user); }; fetchUser(); }, []); Sodra ons die gebruiker het, kry ons: Load Poll Details and Check Voting Status die die Die poll self (insluitend opsies en stemrekening) Of hierdie gebruiker reeds gestem het Ons noem dit ook later weer in real-time updates. useEffect(() => { if (!user) { return; } const checkUserVote = async () => { const { data: votes } = await supabase .from("votes") .select("id") .eq("poll_id", query.id) .eq("user_id", user.id) .single(); setHasVoted(!!votes); setVoteLoading(false); }; const fetchPoll = async () => { const { data } = await supabase .from("polls") .select( ` *, options ( id, text, votes (count) ) ` ) .eq("id", query.id) .single(); setPoll(data); setPollLoading(false); checkUserVote(); }; fetchPoll(); Listen for Real-Time Updates We subscribe to changes in the Wanneer 'n nuwe stemming gestuur word, kry ons geactualiseerde stemming data en stemming status. votes const channel = supabase .channel(`poll-${query.id}`) .on( "postgres_changes", { event: "*", schema: "public", table: "votes", filter: `poll_id=eq.${query.id}`, }, () => { fetchPoll(); checkUserVote(); } ) .subscribe(); return () => { supabase.removeChannel(channel); }; }, [query.id, user]); Handle the Vote Submission As die gebruiker nie gestem het nie en toegelaat word om te stem (ons sal later 'n toestemmingskoers byvoeg), voeg ons sy stem in. const handleVote = async (optionId: string) => { if (!user) return; try { const { error } = await supabase.from("votes").insert({ poll_id: query.id, option_id: optionId, user_id: user.id, }); if (!error) { setHasVoted(true); } } catch (error) { console.error("Error voting:", error); } }; Ons bereken die totale aantal stemme en 'n terugrekening na die verval tyd. Display the Poll Results if (!poll || pollLoading || voteLoading) return <div>Loading...</div>; // 6. calculate total votes const totalVotes = calculateTotalVotes(poll.options); const countdown = getCountdown(poll.expires_at); return ( ... ); }; export default ViewPoll; Met hierdie instelling in plek, jou stemmingstelsel is ten volle funksioneel. Maar op die oomblik, almal wat aangemeld is, kan tegnies probeer om te stem - selfs op hul eie stemming. gebruik en om hierdie reëls te handhaaf. authorization checks Permit.io Supabase Edge Functions Voordat ons dit doen, laat ons eers kyk na die tipe autorisasielaag wat ons gaan implementeer. Understanding ReBAC (Relationship-Based Access Control) Supabase hanteer goed verifikasie en basiese limiete op rigtingsvlak, maar dit ondersteun nie komplekse reëls soos: die die die Om gebruikers te verhoed om op hul eie sonde te stem Toewysing van rolle per hulpbron (soos "skepper" vir 'n spesifieke opname) Managing access via external policies Om hierdie soort verhouding gebaseerde toestemmings te ondersteun, implementeer ons ReBAC met Permit.io. is 'n model vir die bestuur van toestemmings wat gebaseer is op die verhoudings tussen gebruikers en hulpbronne. In plaas van uitsluitlik te vertrou op rolle of attribute (soos in RBAC of ABAC), bepaal ReBAC toegang deur te evalueer hoe 'n gebruiker verbind is met die hulpbron wat hulle probeer om toegang te verkry. Relationship-Based Access Control (ReBAC) Gebaseerde toegangsbeheer (ReBAC) In hierdie tutorial, ons toepas ReBAC op 'n stemming app: die die die die A user who a poll is the only one who can manage (edit/delete) it created 'N Gebruiker kan nie op sy eie poll stem nie Other authenticated users can vote once per poll Deur hierdie verhoudings in Permit.io te model, kan ons goedgekeurde toegangsreëls definieer wat verder gaan as Supabase se ingeboude Row Level Security (RLS). For more on ReBAC, check out Die Permit.io se ReBAC dokumente Ontwerp van toegangsbeheer Vir ons Polling app, sal ons definieer: die die Een hulpbron met hulpbron-spesifieke aksies: sonde: skep, lees, verwyder, actualiseer. Twee rolle vir toekenning van toestemmingsvlakke gebaseer op 'n gebruiker se verhouding met die hulpbronne: geauthentifiseer: Kan maak en lees aksies in opnames. Kan nie verwyder of opdater aksies in opnames. maker: Kan skep, lees, verwyder en opdater aksies in opnames. Kan lees en skep aksies in stemme. Kan nie gebruik maak op hul eie opnames. Maak die toestemming.io Let’s walk through setting up the authorization model in Permit. die die die die Maak 'n nuwe projek in Permit.io Naam dit iets soos supabase-polling Definieer die sonde hulpbron Gaan na die Beleid → hulpbronne tab Klik op “Skep hulpbron” Naam dit sonde, en voeg die aksies by: lees, skep, actualiseer, verwyder Om ReBAC vir die hulpbron te aktiveer Onder "ReBAC-opsies," definieer die volgende rolle: geauthenticeerde skepper Klik Save Skep 'n nuwe projek Navigeer na die "Roles" tab om die rolle te sien van die hulpbronne wat ons net geskep het. Let daarop dat Toestemming die standaard rolle geskep het ( , , Dit is onnodig vir hierdie tutorial. admin editor user die Definieer toegangsbeleid Gaan na die Beleid → Beleid tab Gebruik die visuele matrix om te definieer: geauthentifiseerde kan polls lees en skep die skepper kan polls lees, opdat en verwyder (Optioneel) U kan stemreëls as deel hiervan of via 'n tweede hulpbron instel as u afsonderlik stemmodelleer. Add resource instances Go to Directory → Instances Add individual poll IDs as resource instances (you’ll automate this later when new polls are created) Assign roles to users per poll (e.g. is of ) user123 creator poll456 Hierdie struktuur gee ons die mag om fleksibele toegangsreëls te skryf en hulle per gebruiker, per opname, uit te voer. Now that we have completed the initial setup on the Permit dashboard, let's use it in our application. Next, we’ll connect na ons Supabase-projek via Edge Funksie wat: Toelaat die die Sync nuwe gebruikers Toekenning van kreatiewe rolle Kontroleer toegang op versoek Setting up Permit in the Polling Application Permit bied verskeie maniere om te integreer met jou aansoek, maar ons sal die Container PDP gebruik vir hierdie gids. Jy moet die container aanlyn hosting om dit in Supabase Edge funksies te toegang. . Once you have hosted it, save the url for your container. die spoorweg.com Kry jou toestemming API sleutel deur te klik op "Projekte" in die toestemming instrumentbalk, navigeer na die projek waarvoor jy werk, klik op die drie punte en kies "Kopieer API sleutel". Skep Supabase Edge Function API's vir Autorisasie are perfect for integrating third-party services like Permit.io. We’ll use them to enforce our ReBAC rules at runtime by checking whether users are allowed to perform specific actions on polls. Supabase Edge Functions Create Functions in Supabase Initialiseer Supabase in jou projek en skep drie verskillende funksies met behulp van die command. These will be the starting point for your functions: supabase functions new npx supabase init npx supabase functions new syncUser npx supabase functions new updateCreatorRole npx supabase functions new checkPermission Dit sal 'n Die folder in die Kom ons skryf die kode vir elke eindpunt. functions supabase Die versekering van die versekering van die versekering van die versekering ( ) die syncUser.ts Hierdie funksie luister vir Supabase's Wanneer 'n nuwe gebruiker aanmeld, sinchroniseer ons hul identiteit met Permit.io en gee hulle die standaard role. SIGNED_UP authenticated import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import { Permit } from "npm:permitio"; const corsHeaders = { 'Access-Control-Allow-Origin': "*", 'Access-Control-Allow-Headers': 'Authorization, x-client-info, apikey, Content-Type', 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE', } // Supabase Edge Function to sync new users with Permit.io Deno.serve(async (req) => { const permit = new Permit({ token: Deno.env.get("PERMIT_API_KEY"), pdp: "<https://real-time-polling-app-production.up.railway.app>", }); try { const { event, user } = await req.json(); // Only proceed if the event type is "SIGNED_UP" if (event === "SIGNED_UP" && user) { const newUser = { key: user.id, email: user.email, name: user.user_metadata?.name || "Someone", }; // Sync the user to Permit.io await permit.api.createUser(newUser); await permit.api.assignRole({ role: "authenticated", tenant: "default", user: user.id, }); console.log(`User ${user.email} synced to Permit.io successfully.`); } // Return success response return new Response( JSON.stringify({ message: "User synced successfully!" }), { status: 200, headers: corsHeaders }, ); } catch (error) { console.error("Error syncing user to Permit: ", error); return new Response( JSON.stringify({ message: "Error syncing user to Permit.", "error": error }), { status: 500, headers: { "Content-Type": "application/json" } }, ); } }); Verwys na die rol van die skepper ( ) die updateCreatorRole.ts Sodra 'n gebruiker 'n opname skep, word hierdie funksie geroep om: die die die Synchroniseer die opname as 'n nuwe Permit.io hulpbron-instansie As gevolg van hierdie resource: Resource geassosieer die gebruiker die rol van die skepper vir die opname status "jsr:@supabase/functions-js/edge-runtime.d.ts"; gebruiker {Permit } van "npm:permitio"; resource new return corsHeaders = { 'Access-Control-message-Allow-Origin': "*", 'Access-Control-Allow-Headers': 'Authorization, x-client-info, apikey, Content-Type', 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE', 'Error(req) => 'Access-serve(async)', assosieer die rol van die gebruiker: {Idstr Toelaatbare toelaatings ( ) die checkPermission.ts Hierdie funksie optree as die gatekeeper - dit kyk of 'n gebruiker toegelaat word om 'n gegewe aksie uit te voer ( die die die In 'n spesifieke poll create read update delete import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import { Permit } from "npm:permitio"; const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "Authorization, x-client-info, apikey, Content-Type", "Access-Control-Allow-Methods": "POST, GET, OPTIONS, PUT, DELETE", }; Deno.serve(async req => { const permit = new Permit({ token: Deno.env.get("PERMIT_API_KEY"), pdp: "<https://real-time-polling-app-production.up.railway.app>", }); try { const { userId, operation, key } = await req.json(); // Validate input parameters if (!userId || !operation || !key) { return new Response( JSON.stringify({ error: "Missing required parameters." }), { status: 400, headers: { "Content-Type": "application/json" } } ); } // Check permissions using Permit's ReBAC const permitted = await permit.check(userId, operation, { type: "polls", key, tenant: "default", // Include any additional attributes that Permit needs for relationship checking attributes: { createdBy: userId, // This will be used in Permit's policy rules }, }); return new Response(JSON.stringify({ permitted }), { status: 200, headers: corsHeaders, }); } catch (error) { console.error("Error checking user permission: ", error); return new Response( JSON.stringify({ message: "Error occurred while checking user permission.", error: error, }), { status: 500, headers: { "Content-Type": "application/json" } } ); } }); Local Testing Start your Supabase dev server to test the functions locally: npx supabase start npx supabase functions serve Jy kan dan jou funksies op: <http://localhost:54321/functions/v1/><function-name> Voorbeeld van: <http://localhost:54321/functions/v1/checkPermission> Integrasie van autorisasiekontrole in die UI Nou dat ons ons autorisasie-logika met Permit.io geskep het en dit via Supabase Edge Functions blootgestel het, is dit tyd om daardie kontrole binne die komponente van die app te handhaaf. In hierdie afdeling sal ons belangrike UI-komponente opdater om daardie funksies te bel en voorwaardelik gebruikersaktiwiteite te toelaat of te blokkeer, soos stemming of die bestuur van sonde gebaseer op toestemmingskontrole. : Toekenning van die skeprol na die skep van die opname NewPoll.tsx Sien die.tsx After creating a poll and saving it to Supabase, we call the Funksie vir: updateCreatorRole die die die Sinkroniseer die nuwe opname as 'n hulpbron in Permit.io Assign the current user the role for that specific poll creator const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (question.trim() && options.filter(opt => opt.trim()).length < 2) { setErrorMessage("Please provide a question and at least two options."); return; } try { // Create the poll const { data: poll, error: pollError } = await supabase .from("polls") .insert({ question, expires_at: new Date(expiryDate).toISOString(), created_by: user?.id, creator_name: user?.user_metadata?.user_name, }) .select() .single(); if (pollError) { console.error("Error creating poll:", pollError); setErrorMessage(pollError.message); return; } // Create the options const { error: optionsError } = await supabase.from("options").insert( options .filter(opt => opt.trim()) .map(text => ({ poll_id: poll.id, text, })) ); if (optionsError) { console.error("Error creating options:", optionsError); setErrorMessage(optionsError.message); return; } // Update the creator role in Permit.io const response = await fetch( "<http://127.0.0.1:54321/functions/v1//updateCreatorRole>", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user?.id, pollId: poll.id, }), } ); const { success, error } = await response.json(); if (!success) { console.error("Error updating creator role:", error); // Note: We don't set an error message here as the poll was still created successfully } setSuccessMessage("Poll created successfully!"); handleCancel(); } catch (error) { console.error("Error in poll creation process:", error); setErrorMessage("An unexpected error occurred while creating the poll."); } }; : Beperkte stemming op grond van toestemmings ViewPoll.tsx Before allowing a user to vote on a poll, we call the Funksie om te verifieer dat hulle die Toestemming vir die resource.Dit is hoe ons die reël handhaaf: checkPermission create votes “A creator cannot vote on their own poll.” Check voting permission: const [canVote, setCanVote] = useState(false); useEffect(() => { const checkPermission = async () => { if (!user || !query.id) return; try { const response = await fetch("<http://127.0.0.1:54321/functions/v1/checkPermission>", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user.id, operation: "create", key: query.id, }), }); const { permitted } = await response.json(); setCanVote(permitted); } catch (error) { console.error("Error checking permission:", error); setCanVote(false); } }; checkPermission(); }, [user, query.id]); Disable vote buttons if user isn’t allowed: <button onClick={() => handleVote(option.id)} disabled={!user || !canVote}} className="w-full text-left p-4 rounded-md hover:bg-slate-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"> {option.text} </button> Show a message if the user is not allowed to vote: {user && !canVote && ( <p className="mt-4 text-gray-600">You cannot vote on your own poll</p> )} • Toegang tot die opsie Edit/Delete PollCard.tsx Ons beperk ook opname bestuur aksies (redig en verwyder) deur te kyk of die gebruiker die of Toestemming vir die poll. update delete Check management permissions: const [canManagePoll, setCanManagePoll] = useState(false); useEffect(() => { const checkPollPermissions = async () => { if (!user || !poll.id) return; try { // Check for both edit and delete permissions const [editResponse, deleteResponse] = await Promise.all([ fetch("<http://127.0.0.1:54321/functions/v1/checkPermission>", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user.id, operation: "update", key: poll.id, }), }), fetch("/api/checkPermission", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user.id, operation: "delete", key: poll.id, }), }), ]); const [{ permitted: canEdit }, { permitted: canDelete }] = await Promise.all([editResponse.json(), deleteResponse.json()]); // User can manage poll if they have either edit or delete permission setCanManagePoll(canEdit || canDelete); } catch (error) { console.error("Error checking permissions:", error); setCanManagePoll(false); } }; checkPollPermissions(); }, [user, poll.id]); Conditionally show management buttons: vervanging van: {user?.id === poll?.created_by && ( Met die: {canManagePoll && ( <div className="flex justify-start gap-4 mt-4"> <button type="button" onClick={handleEdit}> </button> <button type="button" onClick={handleDelete}> </button> </div> )} Testeer die integrasie Sodra dit geïntegreer is, moet jy die volgende gedrag in die app sien: die die die die Gelaagde gebruikers kan navorsings sien, maar nie interaksie maak nie Authentifiseerde gebruikers kan stem op sonde wat hulle nie geskep het nie Creators on their own polls cannot vote Slegs skepers sien bewerk/verwyder opsies op hul sonde Jy moet in staat wees om die veranderinge van die aansoek te sien deur na die leser te gaan. Op die aanvanklike skerm kan gebruikers die lys van aktiewe en verlede sonde sien, of hulle aangemeld is of nie. Sodra aangemeld is, kan die gebruiker die besonderhede van die opname sien en daarop stem. As die gebruiker egter die skepper van die opname is, sal hulle nie daarop kan stem nie. Hulle sal 'n boodskap sien wat aangedui dat hulle nie op hul eie opname kan stem nie. Konklusie In hierdie tutorial het ons ondersoek hoe om te implementeer In 'n werklike wêreld die toepassing. Supabase authentication and authorization Next.js Ons het begin met die opstel vir inlog en aanmelding, 'n relasionele skema met Row Level Security geskep, en voeg dinamiese autorisasie logika gebruik Met die hulp van en a , ons toegepas toestemmings kontrole direk van die frontend. Supabase Auth ReBAC Supabase Edge Functions Policy Decision Point (PDP) deur die kombinasie Met 'n fleksibele toegangsbeheer kon ons: Supabase Auth die die die die die Authentiek gebruikers via e-pos en wagwoord Beperk stem- en pollbestuur tot gemagtigde gebruikers Beperk die skepers om te stem op hul eie sonde Toewys en evalueer gebruiker rolle gebaseer op verhoudings met data Hierdie instelling gee jou 'n skaalbare basis vir die bou van programme wat beide outentiek en goedgekeurde autorisasie benodig. Verdere lees die die die die die die Permit.io ReBAC gids Permit + Authentication Providers Toelaatingselemente: ingebed UI vir rolbestuur Data Filtering with Permit Audit logs Het jy vrae? sluit aan by ons , waar honderde ontwikkelaars bou en bespreek autorisasie. Slack gemeenskap Slack gemeenskap