¿Alguna vez has recibido el paquete de tu vecino en la puerta de tu casa? (¿Quizás incluso lo hayas abierto por accidente?) ¿Quizás te han dejado un mensaje de voz confidencial para otra persona? Como desarrolladores de aplicaciones, es tu trabajo asegurarte de que los datos confidenciales almacenados en tu aplicación no se entreguen por accidente a otra parte.
Existen muchas técnicas disponibles para almacenar de forma segura los datos de los clientes, y muchas de ellas son muy complejas y difíciles de implementar. Lo ideal sería proteger todos los datos de los clientes en una única base de datos, manteniendo el diseño de la función simple y segura.
La seguridad a nivel de fila (RLS) es la capacidad de proteger y controlar el acceso a filas específicas de datos dentro de una tabla de base de datos. Es una herramienta poderosa que le permite almacenar todos los datos de sus clientes en una única base de datos sin preocuparse por la fuga de datos entre cuentas. Sin embargo, la correcta implementación de RLS puede ser un proceso complicado que implica combinar los datos de inicio de sesión con los permisos de su base de datos. Neon Authorize agiliza este proceso al integrar automáticamente la autenticación de su proveedor de OAuth con su base de datos PostgreSQL.
Neon Authorize utiliza su capa de autenticación existente para identificar a cada usuario conectado y asocia todos los datos de su base de datos con sus credenciales de inicio de sesión. Esto garantiza que solo los usuarios conectados puedan acceder a los datos almacenados en la base de datos y que solo estos puedan ver sus datos.
Este tutorial le mostrará cómo crear una aplicación Remix utilizando Clerk como capa de autenticación. Clerk es una herramienta de administración y autenticación de usuarios muy popular. Utilizará Neon Postgres como capa de datos y aprovechará Neon Authorize para proteger todos los datos de cada cliente conectado. Cada fila de la tabla designará un ID de usuario, que es proporcionado por Clerk. Solo aquellos autenticados con el ID de usuario pueden interactuar con los datos de la fila.
Nuestra aplicación de muestra es sencilla: registra cada inicio de sesión en la base de datos RLS, utilizando el ID de usuario. Cuando se carga la página, se muestran los últimos 10 inicios de sesión del usuario autenticado y no aparecen los datos de ningún otro usuario (almacenados en la misma tabla de PostgreSQL). ¡Comencemos!
Comience por crear una aplicación Remix e instalar las dependencias utilizando el fragmento de código que aparece a continuación. Para obtener instrucciones más detalladas, consulte la guía de inicio rápido de Remix .
##make a directory and initialise your NPM project mkdir neon-authorize-remix-clerk-app cd neon-authorize-remix-clerk-app npm init -y ## install runtime dependecies for Remix npm i @remix-run/node @remix-run/react @remix-run/serve isbot@4 react react-dom @remix-run/router drizzle-orm npm install @neondatabase/serverless npm install @clerk/remix npm i -D @remix-run/dev vite
Dado que Remix usa Vite, una herramienta de compilación de Javascript, cree vite.config.js
en el directorio raíz:
import { vitePlugin as remix } from "@remix-run/dev"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [remix()], });
Antes de realizar cualquier desarrollo, necesitamos crear cuentas en Clerk y Neon para utilizar sus servicios:
Inicie sesión en el panel de Clerk para crear un nuevo proyecto.
En la navegación de la izquierda, seleccione Claves API .
.env
en tu código.En la navegación de la izquierda, seleccione “ Plantillas JWT”.
Inicie sesión en la consola Neon y cree un nuevo proyecto.
Desde el menú de navegación de la izquierda, seleccione Autorizar .
Cree un nuevo proveedor y pegue la URL de Clerk JWKS que copió de Clerk anteriormente.
Una vez que haya creado la instancia, haga clic en “Comenzar”. Se abrirá un panel lateral con una serie de pasos para completar la integración de Neon Authorize.
La configuración inicial le proporciona los pasos para configurar un proyecto Autorizado básico con Clerk.
1. Set up Neon Extension and Roles Privileges. Run these steps in the Dashboard. 2. Grant privileges to the roles in the neondb database.
El código proporcionado es para una aplicación de tareas pendientes. En lugar de utilizar el código repetitivo proporcionado por Neon para una aplicación de tareas pendientes, crearemos una tabla login_history
y configuraremos RLS en ella. Abra el Editor SQL en el panel de Neon y ejecute el código a continuación. La tabla login_history
se utilizará para almacenar los tiempos de inicio de sesión de cada usuario.
Tenga en cuenta que
login_history
tiene solo tres columnas: id, user_id y login_at. Las dos últimas columnas mostrarán los inicios de sesión más recientes en la aplicación.
CREATE TABLE login_history ( id bigint generated by default as identity primary key, user_id text not null default (auth.user_id()), login_at timestamp not null default now() ); -- 1st enable row level security for your table ALTER TABLE login_history ENABLE ROW LEVEL SECURITY; -- 2nd create policies for your table CREATE POLICY "Individuals can add login." ON login_history FOR INSERT TO authenticated WITH CHECK ((select auth.user_id()) = user_id); CREATE POLICY "Individuals can view their own logins. " ON login_history FOR SELECT TO authenticated USING ((select auth.user_id()) = user_id);
Agregue las variables ambientales proporcionadas a su .env
Una vez completados estos pasos de configuración, su .env
debería tener cuatro variables: dos de Clerk y dos de Neon:
CLERK_PUBLISHABLE_KEY=pk_test_.... CLERK_SECRET_KEY=sk_test_... # Database owner connection string DATABASE_URL='postgresql://neondb_owner:...' # Neon "authenticated" role connection string DATABASE_AUTHENTICATED_URL='postgresql://authenticated@ep-...
La aplicación ya está lista para compilarse. El código completo está disponible en GitHub , pero las características más importantes se destacan aquí. El núcleo de la aplicación se encuentra en app/routes/_index.tsx
:
export const loader: LoaderFunction = async (args) => { const { userId, getToken } = await getAuth(args); if (!userId) { return redirect("/sign-in"); } const authToken = await getToken(); console.log(userId); if (!authToken) { return null; } const DATABASE_AUTHENTICATED_URL= process.env.NEXT_PUBLIC_DATABASE_AUTHENTICATED_URL; try { const sql = neon(DATABASE_AUTHENTICATED_URL ?? '', { authToken, }); const loginResponse = await sql(`INSERT INTO login_history ("user_id") VALUES ($1) RETURNING *`,[userId]); // Retrieve last 10 logins const last10LoginsResponse = await sql(`SELECT * FROM login_history WHERE user_id = $1 ORDER BY login_at DESC LIMIT 10`, [userId]); console.log(`loginResponse: ${JSON.stringify(loginResponse)}`); return last10LoginsResponse as Array<LoginHistory>; } catch (error) { console.error(`Error inserting into login_history table: ${error.message}`); console.error(`Error details: ${JSON.stringify(error)}`); throw error; } }
La LoaderFunction
del archivo _index.tsx
completa las tareas en el servidor antes de mostrar la página para el cliente. En esta aplicación, el cargador realiza gran parte del trabajo pesado de la aplicación.
La función primero verifica si el usuario no ha iniciado sesión y luego redirige al usuario a la página /sign-in
. La página de inicio de sesión se puede configurar en el panel de Clerk para aceptar diferentes tipos de inicio de sesión, como inicios de sesión con Google y correo electrónico:
Para crear la página de inicio de sesión, navegue hasta el panel de Clerk y configure los métodos de inicio de sesión necesarios para el proyecto.
Si el usuario ha iniciado sesión, la función recupera el userId
y authToken
de Clerk. Estos valores son esenciales para garantizar que el usuario haya iniciado sesión y, luego, puede usar el userId
para completar cada fila de su base de datos.
Para realizar cambios en la base de datos protegida por RLS, debe extraer DATABASE_AUTHENTCATED_URL
de las variables de entorno.
La lógica central para implementar la seguridad RLS se encuentra dentro de LoaderFunction
. Se inicializa una instancia de SQL Neon utilizando las variables ambientales y el token de autenticación. La función loginResponse
realiza una llamada SQL e inserta el user_id (y la hora actual) en la base de datos PostgreSQL, después de lo cual la función last10LoginsResponse
consulta la base de datos para conocer los 10 inicios de sesión más recientes.
Finalmente, la función del cargador devuelve la last10LoginsResponse
.
La función Index()
del archivo _index.tsx
representa el diseño de la página como se muestra en el fragmento a continuación:
export default function Index() { const logins = useLoaderData(); return ( <div> <h1>Signed in</h1> <p>You are signed in!</p> <p> <UserButton /></p> <div> <h1>Recent Logins</h1> {logins?.map((logins) => ( <li key={logins.id}> {logins.user_id} login at: {logins.login_at} </li> ))} </div> <p>< SignOutButton > Sign Out</ SignOutButton ></p> </div> ); }
El código anterior recupera la respuesta de LoaderFunction
, que contiene las últimas 10 entradas de inicio de sesión. Esta respuesta crea una página que le indica al usuario que ha iniciado sesión, enumera sus últimos 10 inicios de sesión y muestra un botón Cerrar sesión, como se muestra a continuación:
En este ejemplo, también se muestra el user_id
para indicar claramente que solo son visibles los datos de inicio de sesión del usuario que inició sesión.
Al usar una ventana de incógnito, puedes iniciar sesión con una segunda cuenta de Google y ver datos en paralelo de diferentes usuarios:
Tenga en cuenta que los horarios de inicio de sesión se superponen, pero al utilizar la seguridad a nivel de fila en la base de datos, evitará la fuga de datos entre cuentas. Las filas solo se pueden extraer y mostrar para el usuario autenticado.
Mantener la privacidad de los datos es un caso de uso crítico. Como las aplicaciones suelen almacenar información privada, es necesario protegerla para que los datos estén en las manos adecuadas. Los consumidores cuentan con cada vez más protecciones legales, como el RGPD, y herramientas como Neon Authorize facilitan la implementación de la seguridad a nivel de fila para proteger los datos de sus clientes.
En esta publicación, hemos repasado los pasos necesarios para habilitar la seguridad a nivel de fila en una base de datos de Neon. El uso de RLS con los datos de nuestros clientes garantiza que solo el usuario que haya iniciado sesión tenga las credenciales para extraer sus propios datos.
Agregue seguridad de capa de fila a su aplicación hoy con Neon.