Come creare un'applicazione web SICURA con RLS e Supabase

In questo articolo, con video in allegato, osserveremo come mettere delle restrizioni al modo in cui gli utenti si interfacciano con la nostra app e ai dati su di essa mantenuti.

Autore: Giacomo Verde • 04/10/2026
CategorySupabase Tutorial/Course
Core StackJavaScript, SupabaseJS, Supabase, SQL, RLS policies
Read Time5 min

Di cosa parlo in questo tutorial:

In questo nuovo video della serie/corso di Supabase in Italiano, parleremo di RLS e di come grazie ad esse è possibile costruire un'applicazione che sia effettivamente sicura. Nel video ribadiamo concetti come le operazioni CRUD (Creazione, Lettura, Modifica ed Eliminazione dei dati) e ne affrontiamo di nuovi come: le Policy di sicurezza RLS, nonchè il concetto di Server web locale e di origine.

Codice di implementazione

Copia e incolla questo codice nel tuo editor preferito e inserisci le tue credenziali pubbliche per la connessione a Supabase. Ricorda di aggiungere le tabelle e le policy di sicurezza RLS come nel video per evitare errori, come colonne non trovate o di tipo dato sbagliato:

Puoi scaricare il codice in una cartella .zip direttamente da qui: Download del codice sorgente tramite file ZIP.

Oppure incollare a mano il codice:

01-rls-policies-examples.sql

-- Abilitazione di RLS sulla tabella desiderata
ALTER TABLE products ENABLE ROW LEVEL SECURITY;

-- 1. Permetti la lettura (SELECT) a CHIUNQUE (anonimi e autenticati)
CREATE POLICY "Nome della regola di sicurezza"
ON products
FOR SELECT
USING (true);

-- 2. Permetti l'inserimento (INSERT) SOLO agli utenti autenticati
CREATE POLICY "Solo utenti autenticati possono creare nuove righe nella tabella desiderata"
ON products
FOR INSERT
TO authenticated
WITH CHECK (true);

supabase_crud_no_css.html

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Supabase CRUD Operations</title>
    <!-- Libreria Supabase JS via CDN -->
    <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
</head>
<body>
    <h1>Supabase CRUD Example</h1>

    <!-- Operazione CREATE (Insert) -->
    <section>
        <h2>2. Create (Insert)</h2>
        <form id="createForm" onsubmit="createRecord(event)">
            <label for="createTitle">Titolo / Nome:</label>
            <input type="text" id="createTitle" required><br><br>

            <label for="createDesc">Descrizione:</label>
            <input type="text" id="createDesc"><br><br>

            <button type="submit">Inserisci Record</button>
        </form>
    </section>

    <hr>

    <!-- Operazione READ (Select) -->
    <section>
        <h2>3. Read (Select)</h2>
        <button onclick="readRecords()">Carica / Ricarica Tutti i Record</button>
        <h3>Risultati:</h3>
        <pre id="outputRead">Nessun dato caricato</pre>
    </section>

    <hr>

    <!-- Operazione UPDATE -->
    <section>
        <h2>4. Update</h2>
        <form id="updateForm" onsubmit="updateRecord(event)">
            <label for="updateId">ID Record da aggiornare:</label>
            <input type="text" id="updateId" required><br><br>

            <label for="updateTitle">Nuovo Titolo:</label>
            <input type="text" id="updateTitle" required><br><br>

            <label for="updateDesc">Nuova Descrizione:</label>
            <input type="text" id="updateDesc"><br><br>

            <button type="submit">Aggiorna Record</button>
        </form>
    </section>

    <hr>

    <!-- Operazione DELETE -->
    <section>
        <h2>5. Delete</h2>
        <form id="deleteForm" onsubmit="deleteRecord(event)">
            <label for="deleteId">ID Record da eliminare:</label>
            <input type="text" id="deleteId" required><br><br>

            <button type="submit">Elimina Record</button>
        </form>
    </section>

    <script>
        let supabaseClient = null;

        // 1. Inizializzazione Client
        function initSupabase() {
            // INSERISCI QUI I TUOI DATI PRESI DALLA DASHBOARD SUPABASE
            const SUPABASE_URL = 'IL_TUO_SUPABASE_URL';
            const SUPABASE_KEY = 'LA_TUA_SUPABASE_PUBLIC_KEY';

            if (!SUPABASE_URL || !SUPABASE_KEY || SUPABASE_URL.includes('TUO-PROGETTO')) {
                console.log('Inserisci sia URL che Anon Key di Supabase nel codice JS!');
                return;
            }

            try {
                supabaseClient = supabase.createClient(SUPABASE_URL, SUPABASE_KEY);
                console.log('Client Supabase inizializzato correttamente.');
            } catch (err) {
                console.error('Errore durante l\'inizializzazione del client:', err);
            }
        }

        function checkClient() {
            if (!supabaseClient) {
                console.log('Inizializza prima il client Supabase con URL e Anon Key!');
                return false;
            }
            return true;
        }

        // 2. CREATE (Insert)
        async function createRecord(event) {
            event.preventDefault();
            if (!checkClient()) return;

            const title = document.getElementById('createTitle').value;
            const description = document.getElementById('createDesc').value;

            const { data, error } = await supabaseClient
                .from('products')
                .insert([{ name: title, description: description }])
                .select();

            if (error) {
                console.error('Errore durante la CREATE:', error);
                console.log('Errore: ' + error.message);
            } else {
                console.log('CREATE completata con successo:', data);
                document.getElementById('createForm').reset();
                readRecords();
            }
        }

        // 3. READ (Select)
        async function readRecords() {
            if (!checkClient()) return;

            const { data, error } = await supabaseClient
                .from('products')
                .select('*');

            if (error) {
                console.error('Errore durante la READ:', error);
                console.log('Errore: ' + error.message);
            } else {
                console.log('READ completata:', data);
                document.getElementById('outputRead').textContent = JSON.stringify(data, null, 2);
            }
        }

        // 4. UPDATE
        async function updateRecord(event) {
            event.preventDefault();
            if (!checkClient()) return;

            const id = document.getElementById('updateId').value;
            const title = document.getElementById('updateTitle').value;
            const description = document.getElementById('updateDesc').value;

            const { data, error } = await supabaseClient
                .from('products')
                .update({ name: title, description: description })
                .eq('id', id)
                .select();

            if (error) {
                console.error('Errore durante l\'UPDATE:', error);
                console.log('Errore: ' + error.message);
            } else {
                console.log('UPDATE completata con successo:', data);
                document.getElementById('updateForm').reset();
                readRecords();
            }
        }

        // 5. DELETE
        async function deleteRecord(event) {
            event.preventDefault();
            if (!checkClient()) return;

            const id = document.getElementById('deleteId').value;

            const { data, error } = await supabaseClient
                .from('products')
                .delete()
                .eq('id', id)
                .select();

            if (error) {
                console.error('Errore durante la DELETE:', error);
                console.log('Errore: ' + error.message);
            } else {
                console.log('DELETE completata con successo:', data);
                document.getElementById('deleteForm').reset();
                readRecords();
            }
        }

        // Avvio inizializzazione
        initSupabase();
    </script>
</body>
</html>

auth.html

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Supabase Email & Password Auth</title>
    <!-- Libreria Supabase JS via CDN -->
    <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
</head>
<body>
    <h1>Supabase Authentication Example</h1>

    <!-- STATO UTENTE CORRENTE -->
    <section>
        <h2>1. Stato Sessione</h2>
        <div id="userInfo">Verifica stato in corso...</div>
        <br>
        <button onclick="checkUser()">Aggiorna Stato Utente</button>
        <button onclick="handleSignOut()">Logout</button>
    </section>

    <hr>

    <!-- REGISTRAZIONE -->
    <section>
        <h2>2. Registrazione (Sign Up)</h2>
        <form id="signUpForm" onsubmit="handleSignUp(event)">
            <label for="signUpEmail">Email:</label>
            <input type="email" id="signUpEmail" required><br><br>

            <label for="signUpPassword">Password:</label>
            <input type="password" id="signUpPassword" required minlength="6"><br><br>

            <button type="submit">Crea Account</button>
        </form>
    </section>

    <hr>

    <!-- LOGIN -->
    <section>
        <h2>3. Accesso (Sign In)</h2>
        <form id="signInForm" onsubmit="handleSignIn(event)">
            <label for="signInEmail">Email:</label>
            <input type="email" id="signInEmail" required><br><br>

            <label for="signInPassword">Password:</label>
            <input type="password" id="signInPassword" required><br><br>

            <button type="submit">Accedi</button>
        </form>
    </section>

    <hr>

    <!-- OUTPUT DEBUG -->
    <section>
        <h2>4. Risposta Supabase (Debug)</h2>
        <pre id="outputLog">Nessuna operazione eseguita</pre>
    </section>

    <script>
        let supabaseClient = null;

        // 1. Inizializzazione Client
        function initSupabase() {
            const SUPABASE_URL = 'IL_TUO_SUPABASE_URL';
            const SUPABASE_KEY = 'LA_TUA_SUPABASE_PUBLIC_KEY';

            try {
                supabaseClient = supabase.createClient(SUPABASE_URL, SUPABASE_KEY);
                console.log('Client Supabase inizializzato.');

                // Ascolta i cambiamenti di stato dell'autenticazione in tempo reale
                supabaseClient.auth.onAuthStateChange((event, session) => {
                    console.log('Evento Auth:', event);
                    updateUI(session?.user || null);
                });

                checkUser();
            } catch (err) {
                console.error('Errore inizializzazione client:', err);
            }
        }

        function checkClient() {
            if (!supabaseClient) {
                alert('Client Supabase non inizializzato!');
                return false;
            }
            return true;
        }

        // 2. Controllo Stato Sessione
        async function checkUser() {
            if (!checkClient()) return;

            const { data: { user }, error } = await supabaseClient.auth.getUser();
            if (error && error.name !== 'AuthSessionMissingError') {
                console.error('Errore recupero utente:', error);
            }
            updateUI(user);
        }

        function updateUI(user) {
            const userInfoEl = document.getElementById('userInfo');
            if (user) {
                userInfoEl.innerHTML = `<strong>Autenticato come:</strong> ${user.email} <br><strong>ID Utente:</strong> <code>${user.id}</code>`;
            } else {
                userInfoEl.innerHTML = '<em>Nessun utente autenticato.</em>';
            }
        }

        function logOutput(data) {
            document.getElementById('outputLog').textContent = JSON.stringify(data, null, 2);
        }

        // 3. Registrazione (signUp)
        async function handleSignUp(event) {
            event.preventDefault();
            if (!checkClient()) return;

            const email = document.getElementById('signUpEmail').value;
            const password = document.getElementById('signUpPassword').value;

            const { data, error } = await supabaseClient.auth.signUp({
                email: email,
                password: password
            });

            if (error) {
                alert('Errore Sign Up: ' + error.message);
                logOutput(error);
            } else {
                alert('Registrazione effettuata! Se la conferma email è attiva sul tuo progetto Supabase, controlla la tua casella di posta.');
                logOutput(data);
                document.getElementById('signUpForm').reset();
            }
        }

        // 4. Login (signInWithPassword)
        async function handleSignIn(event) {
            event.preventDefault();
            if (!checkClient()) return;

            const email = document.getElementById('signInEmail').value;
            const password = document.getElementById('signInPassword').value;

            const { data, error } = await supabaseClient.auth.signInWithPassword({
                email: email,
                password: password
            });

            if (error) {
                alert('Errore Sign In: ' + error.message);
                logOutput(error);
            } else {
                alert('Login effettuato con successo!');
                logOutput(data);
                document.getElementById('signInForm').reset();
            }
        }

        // 5. Logout (signOut)
        async function handleSignOut() {
            if (!checkClient()) return;

            const { error } = await supabaseClient.auth.signOut();
            if (error) {
                alert('Errore Logout: ' + error.message);
                logOutput(error);
            } else {
                alert('Logout completato!');
                logOutput({ status: 'Logged out successfully' });
            }
        }

        // Avvio automatizzato
        initSupabase();
    </script>
</body>
</html>

Una volta fatto ciò puoi eseguire il codice con un server web locale, di cui mostro i passaggi per l'installazione in un precedente video, per provarlo e vedere se funziona correttamente.

Se non funziona

Controlla questi punti:

Puoi utilizzare LLM e coding agent per trovare errori. Oppure contattami per richiedere supporto allo sviluppo.