From 9c3411c21bf00def78b6190a5055405fb9e0100c Mon Sep 17 00:00:00 2001 From: bgrenon Date: Thu, 28 Nov 2024 09:21:05 -0400 Subject: [PATCH 01/62] docs: stashing draft --- content/docs/guides/neon-authorize-drizzle.md | 31 ++++++++ content/docs/guides/neon-authorize.md | 78 ++++++++++--------- 2 files changed, 74 insertions(+), 35 deletions(-) create mode 100644 content/docs/guides/neon-authorize-drizzle.md diff --git a/content/docs/guides/neon-authorize-drizzle.md b/content/docs/guides/neon-authorize-drizzle.md new file mode 100644 index 0000000000..9e84d56343 --- /dev/null +++ b/content/docs/guides/neon-authorize-drizzle.md @@ -0,0 +1,31 @@ +--- +title: Using Drizzle with Neon Authorize +subtitle: Best practices for authentication and Row-Level Security with Drizzle +enableTableOfContents: true +--- + + + + Social wall example + + + + About Neon Authorize + Connect from Drizzle to Neon +RLS in Drizzle + + + +Drizzle works very well with Neon Authorize. This guide shows you some best practices we recommend when using Drizzle as ORM for your authentication and Row-level Security policies. + +## Example Application + +Check out our [social wall sample application](https://github.com/neondatabase-labs/social-wall-drizzle-neon-authorize) to see RLS patterns in action. It demonstrates how to use `crudPolicy` to implement common access patterns: + +- Public read access for posts +- User-specific write permissions +- Admin-only tables + +This example shows how to implement these security patterns in a real application. + + \ No newline at end of file diff --git a/content/docs/guides/neon-authorize.md b/content/docs/guides/neon-authorize.md index 0076e1b49c..1f699964b9 100644 --- a/content/docs/guides/neon-authorize.md +++ b/content/docs/guides/neon-authorize.md @@ -61,16 +61,19 @@ export async function insertTodo(newTodo: { newTodo: string; userId: string }) { const { userId } = auth(); // Gets the user's ID from the JWT or session if (!userId) throw new Error('No user logged in'); // No user authenticated - if (newTodo.userId !== userId) throw new Error('Unauthorized'); // User mismatch // Inserts the new todo, linking it to the authenticated user - await fetchWithDrizzle(async (db) => { - return db.insert(schema.todos).values({ - task: newTodo.newTodo, - isComplete: false, - userId, // Explicitly ties todo to the user - }); + const db = drizzle(neon(process.env.DATABASE_AUTHENTICATED_URL!, { + authToken: authToken, + }), { + schema, + }); + + await db.insert(schema.todos).values({ + task: newTodo.newTodo, + isComplete: false, + userId, // Explicitly ties todo to the user }); revalidatePath('/'); @@ -86,17 +89,7 @@ In this case, you have to: With Neon Authorize, you can let the database handle the authorization through **Row-Level Security** (RLS) policies. Here's an example of applying authorization for creating new todo items, where only authenticated users can insert data: - - - -```sql -CREATE POLICY "create todos" ON "todos" - AS PERMISSIVE FOR INSERT - TO authenticated - WITH CHECK (auth.user_id() = user_id); -``` - - + @@ -108,6 +101,17 @@ pgPolicy('create todos', { }); ``` + + + + +```sql +CREATE POLICY "create todos" ON "todos" + AS PERMISSIVE FOR INSERT + TO authenticated + WITH CHECK (auth.user_id() = user_id); +``` + @@ -115,34 +119,28 @@ Now, in your backend, you can simplify the logic, removing the user authenticati ```typescript shouldWrap export async function insertTodo(newTodo: { newTodo: string }) { - await fetchWithDrizzle(async (db) => { - return db.insert(schema.todos).values({ - task: newTodo.newTodo, - isComplete: false, - }); + const authToken = getToken(); + const db = drizzle(process.env.DATABASE_AUTHENTICATED_URL!, { + schema, + }); + + return db.$withAuth(authToken).insert(schema.todos).values({ + task: newTodo.newTodo, + isComplete: false, + userId, // Explicitly ties todo to the user }); revalidatePath('/'); } ``` -This approach is flexible: you can manage RLS policies directly in SQL, or use an ORM to centralize them within your schema. Keeping both schema and authorization in one place can make it easier to maintain security. Some ORMs like [Drizzle](https://orm.drizzle.team/docs/rls#using-with-neon) are adding support for declaritive RLS, which makes the logic easier to scan and scale. +This approach is flexible: you can manage RLS policies directly in SQL, or use an ORM like Drizzleto centralize them within your schema. Keeping both schema and authorization in one place can make it easier to maintain security. Some ORMs like [Drizzle](https://orm.drizzle.team/docs/rls#using-with-neon) are adding support for declaritive RLS, which makes the logic easier to scan and scale. ## How Neon Authorize gets `auth.user_id()` from the JWT Let's break down the RLS policy controlling who can **view todos** to see what Neon Authorize is actually doing: - - - - -```sql -CREATE POLICY "view todos" ON "todos" AS PERMISSIVE - FOR SELECT TO authenticated - USING ((select auth.user_id() = user_id)); -``` - - + @@ -156,6 +154,16 @@ pgPolicy('view todos', { + + +```sql +CREATE POLICY "view todos" ON "todos" AS PERMISSIVE + FOR SELECT TO authenticated + USING ((select auth.user_id() = user_id)); +``` + + + This policy enforces that an authenticated user can only view their own `todos`. Here's how each component works together. From cf8f4ea1f9f8953caa60cb764ebd7c02870161b7 Mon Sep 17 00:00:00 2001 From: bgrenon Date: Thu, 28 Nov 2024 16:35:04 -0400 Subject: [PATCH 02/62] docs: drizzle crudpolicy explainer modeled on blog post --- content/docs/guides/neon-authorize-drizzle.md | 160 ++++++++++++++++-- content/docs/sidebar.yaml | 2 + 2 files changed, 147 insertions(+), 15 deletions(-) diff --git a/content/docs/guides/neon-authorize-drizzle.md b/content/docs/guides/neon-authorize-drizzle.md index 9e84d56343..53b2a4896b 100644 --- a/content/docs/guides/neon-authorize-drizzle.md +++ b/content/docs/guides/neon-authorize-drizzle.md @@ -1,31 +1,161 @@ --- -title: Using Drizzle with Neon Authorize -subtitle: Best practices for authentication and Row-Level Security with Drizzle +title: Simplify RLS with Drizzle +subtitle: Use Drizzle crudPolicy to manage Row-Level Security with Neon Authorize enableTableOfContents: true --- - + - - Social wall example + +

How to simplify Row-Level Security using crudPolicy

+

Common RLS patterns with Drizzle

About Neon Authorize - Connect from Drizzle to Neon -RLS in Drizzle + RLS in Drizzle +
-Drizzle works very well with Neon Authorize. This guide shows you some best practices we recommend when using Drizzle as ORM for your authentication and Row-level Security policies. +## Why simplify RLS policies? + +Using Row-Level Security (RLS) policies in your database schema is a best practice in application development. While it may not be your only security measure, RLS acts as a reliable last line of defense, protecting your data from unauthorized access where it lives: at the database level. + +However, implementing RLS comes with a few challenges. Writing and maintaining SQL for each CRUD operation (Create, Read, Update, Delete) can lead to repetitive code and increased complexity: both tedious and prone to errors. + +### For example + +To illustrate, let's consider a simple **Todo** list app with RLS policies applied to a `todos` table. Postgres RLS policies use two types of conditions: + +- `USING` clause — controls which existing rows can be accessed +- `WITH CHECK` clause — controls what new or modified data can be written + +Here's how these clauses apply to each operation: + +| Operation | USING clause | WITH CHECK clause | +|-----------|----------------------------|----------------------------| +| Select | `auth.user_id() = user_id` | | +| Insert | | `auth.user_id() = user_id` | +| Update | `auth.user_id() = user_id` | `auth.user_id() = user_id` | +| Delete | `auth.user_id() = user_id` | | + +And the SQL code would look like this: + +```sql shouldWrap +CREATE TABLE IF NOT EXISTS "todos" ( + "id" bigint PRIMARY KEY, + "user_id" text DEFAULT (auth.user_id()) NOT NULL, + "task" text NOT NULL, + "is_complete" boolean DEFAULT false NOT NULL, + "inserted_at" timestamp with time zone DEFAULT now() NOT NULL +); + +ALTER TABLE "todos" ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "create todos" ON "todos" AS PERMISSIVE FOR INSERT TO "authenticated" WITH CHECK ((select auth.user_id() = user_id)); + +CREATE POLICY "view todos" ON "todos" AS PERMISSIVE FOR SELECT TO "authenticated" USING ((select auth.user_id() = user_id)); + +CREATE POLICY "crud-authenticated-policy-update" ON "todos" AS PERMISSIVE FOR UPDATE TO "authenticated" USING ((select auth.user_id() = "todos"."user_id")) WITH CHECK ((select auth.user_id() = "todos"."user_id")); + +CREATE POLICY "delete todos" ON "todos" AS PERMISSIVE FOR DELETE TO "authenticated" USING ((select auth.user_id() = user_id)); +``` + +With each new feature or role, the number of policies grows. This complexity can lead to subtle bugs, like accidentally allowing a user to update todos they can't read, or forgetting to apply a policy to a new table. These issues can be hard to spot in a large schema file filled with SQL statements. + +## How crudPolicy simplifies RLS + +Drizzle's `crudPolicy` simplifies RLS by consolidating rules into a single configuration: + +- `role`: The Postgres role(s) to apply the policy to. Can be a single role or an array of roles +- `read`: Controls SELECT operations: + - `true` to allow all reads + - `false` to deny all reads + - A custom SQL expression + - `null` to prevent policy generation +- `modify`: Controls INSERT, UPDATE, and DELETE operations: + - `true` to allow all modifications + - `false` to deny all modifications + - A custom SQL expression + - `null` to prevent policy generation + +It returns an array of Postgres RLSpolicy definitions, one for each operation (select, insert, update, delete). + +## Common patterns + +Before looking at patterns, let's understand the `authUid` function. It provides a simple way to connect `auth.user_id()` to a column in your table: + +```typescript +export const authUid = (userIdColumn: AnyPgColumn) => sql`(select auth.user_id() = ${userIdColumn})`; +``` + +### Basic access control + +The most common pattern is restricting users to their own data: + +```typescript shouldWrap +import { crudPolicy, authenticatedRole, authUid } from "drizzle-orm/neon"; + +export const todos = pgTable( + "todos", + { + id: bigint().primaryKey(), + userId: text() + .notNull() + .default(sql`(auth.user_id())`), + task: text().notNull(), + isComplete: boolean().notNull().default(false), + insertedAt: timestamp({ withTimezone: true }) + .defaultNow() + .notNull(), + }, + (table) => [ + crudPolicy({ + role: authenticatedRole, + read: authUid(table.userId), // users can only read their own todos + modify: authUid(table.userId), // users can only modify their own todos + }), + ], +); +``` + +### Role-based access control + +For more complex scenarios, you might want different permissions for different roles: + +```typescript shouldWrap +import { crudPolicy, authenticatedRole, anonymousRole } from "drizzle-orm/neon"; -## Example Application +export const posts = pgTable( + "posts", + { + id: bigint().primaryKey(), + userId: text().notNull().default(sql`(auth.user_id())`), + content: text().notNull(), + published: boolean().notNull().default(false), + }, + (table) => [ + // Public read access + crudPolicy({ + role: anonymous, + read: true, // anyone can read posts + modify: false // no modifications allowed + }), + // Authenticated user access + crudPolicy({ + role: authenticatedRole, + read: true, // can read all posts + modify: authUid(table.userId) // can only modify own posts + }) + ] +); +``` -Check out our [social wall sample application](https://github.com/neondatabase-labs/social-wall-drizzle-neon-authorize) to see RLS patterns in action. It demonstrates how to use `crudPolicy` to implement common access patterns: +## Example application -- Public read access for posts -- User-specific write permissions -- Admin-only tables +Check out our [social wall sample application](https://github.com/neondatabase-labs/social-wall-drizzle-neon-authorize), a simple schema that demonstrates RLS policies with `crudPolicy`. It implements a social wall where: -This example shows how to implement these security patterns in a real application. +- Anyone can view the wall +- Authenticated users can modify their own posts - \ No newline at end of file + diff --git a/content/docs/sidebar.yaml b/content/docs/sidebar.yaml index 4bec5f0f00..83ceb40222 100644 --- a/content/docs/sidebar.yaml +++ b/content/docs/sidebar.yaml @@ -444,6 +444,8 @@ slug: guides/neon-authorize - title: Tutorial slug: guides/neon-authorize-tutorial + - title: Simplified RLS with Drizzle + slug: guides/neon-authorize-drizzle - title: Troubleshooting slug: guides/neon-authorize-troubleshooting - title: What's next From 515ad4165f261ae28c40acf553629167182d9bea Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 28 Nov 2024 20:36:28 +0000 Subject: [PATCH 03/62] chore: format content markdown files with Prettier --- content/docs/guides/neon-authorize-drizzle.md | 43 ++++++++++--------- content/docs/guides/neon-authorize.md | 13 +++--- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/content/docs/guides/neon-authorize-drizzle.md b/content/docs/guides/neon-authorize-drizzle.md index 53b2a4896b..6eb0cf13fa 100644 --- a/content/docs/guides/neon-authorize-drizzle.md +++ b/content/docs/guides/neon-authorize-drizzle.md @@ -3,7 +3,7 @@ title: Simplify RLS with Drizzle subtitle: Use Drizzle crudPolicy to manage Row-Level Security with Neon Authorize enableTableOfContents: true --- - +

How to simplify Row-Level Security using crudPolicy

@@ -33,7 +33,7 @@ To illustrate, let's consider a simple **Todo** list app with RLS policies appli Here's how these clauses apply to each operation: | Operation | USING clause | WITH CHECK clause | -|-----------|----------------------------|----------------------------| +| --------- | -------------------------- | -------------------------- | | Select | `auth.user_id() = user_id` | | | Insert | | `auth.user_id() = user_id` | | Update | `auth.user_id() = user_id` | `auth.user_id() = user_id` | @@ -86,7 +86,8 @@ It returns an array of Postgres RLSpolicy definitions, one for each operation (s Before looking at patterns, let's understand the `authUid` function. It provides a simple way to connect `auth.user_id()` to a column in your table: ```typescript -export const authUid = (userIdColumn: AnyPgColumn) => sql`(select auth.user_id() = ${userIdColumn})`; +export const authUid = (userIdColumn: AnyPgColumn) => + sql`(select auth.user_id() = ${userIdColumn})`; ``` ### Basic access control @@ -94,10 +95,10 @@ export const authUid = (userIdColumn: AnyPgColumn) => sql`(select auth.user_id() The most common pattern is restricting users to their own data: ```typescript shouldWrap -import { crudPolicy, authenticatedRole, authUid } from "drizzle-orm/neon"; +import { crudPolicy, authenticatedRole, authUid } from 'drizzle-orm/neon'; export const todos = pgTable( - "todos", + 'todos', { id: bigint().primaryKey(), userId: text() @@ -105,17 +106,15 @@ export const todos = pgTable( .default(sql`(auth.user_id())`), task: text().notNull(), isComplete: boolean().notNull().default(false), - insertedAt: timestamp({ withTimezone: true }) - .defaultNow() - .notNull(), + insertedAt: timestamp({ withTimezone: true }).defaultNow().notNull(), }, (table) => [ crudPolicy({ role: authenticatedRole, - read: authUid(table.userId), // users can only read their own todos - modify: authUid(table.userId), // users can only modify their own todos + read: authUid(table.userId), // users can only read their own todos + modify: authUid(table.userId), // users can only modify their own todos }), - ], + ] ); ``` @@ -124,29 +123,31 @@ export const todos = pgTable( For more complex scenarios, you might want different permissions for different roles: ```typescript shouldWrap -import { crudPolicy, authenticatedRole, anonymousRole } from "drizzle-orm/neon"; +import { crudPolicy, authenticatedRole, anonymousRole } from 'drizzle-orm/neon'; export const posts = pgTable( - "posts", + 'posts', { id: bigint().primaryKey(), - userId: text().notNull().default(sql`(auth.user_id())`), + userId: text() + .notNull() + .default(sql`(auth.user_id())`), content: text().notNull(), published: boolean().notNull().default(false), }, (table) => [ // Public read access - crudPolicy({ + crudPolicy({ role: anonymous, - read: true, // anyone can read posts - modify: false // no modifications allowed + read: true, // anyone can read posts + modify: false, // no modifications allowed }), // Authenticated user access - crudPolicy({ + crudPolicy({ role: authenticatedRole, - read: true, // can read all posts - modify: authUid(table.userId) // can only modify own posts - }) + read: true, // can read all posts + modify: authUid(table.userId), // can only modify own posts + }), ] ); ``` diff --git a/content/docs/guides/neon-authorize.md b/content/docs/guides/neon-authorize.md index 1f699964b9..5d20453498 100644 --- a/content/docs/guides/neon-authorize.md +++ b/content/docs/guides/neon-authorize.md @@ -64,11 +64,14 @@ export async function insertTodo(newTodo: { newTodo: string; userId: string }) { if (newTodo.userId !== userId) throw new Error('Unauthorized'); // User mismatch // Inserts the new todo, linking it to the authenticated user - const db = drizzle(neon(process.env.DATABASE_AUTHENTICATED_URL!, { - authToken: authToken, - }), { - schema, - }); + const db = drizzle( + neon(process.env.DATABASE_AUTHENTICATED_URL!, { + authToken: authToken, + }), + { + schema, + } + ); await db.insert(schema.todos).values({ task: newTodo.newTodo, From e1cb9b4156456e7877315c8d4f42053bf3981b72 Mon Sep 17 00:00:00 2001 From: bgrenon Date: Thu, 28 Nov 2024 17:05:36 -0400 Subject: [PATCH 04/62] docs: update sidebar --- content/docs/sidebar.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/sidebar.yaml b/content/docs/sidebar.yaml index 83ceb40222..67b6aab4c1 100644 --- a/content/docs/sidebar.yaml +++ b/content/docs/sidebar.yaml @@ -444,7 +444,7 @@ slug: guides/neon-authorize - title: Tutorial slug: guides/neon-authorize-tutorial - - title: Simplified RLS with Drizzle + - title: Drizzle RLS slug: guides/neon-authorize-drizzle - title: Troubleshooting slug: guides/neon-authorize-troubleshooting From 2792bbd09bfb3b7b432de87f4f9f599db52ff18f Mon Sep 17 00:00:00 2001 From: bgrenon Date: Thu, 28 Nov 2024 17:28:20 -0400 Subject: [PATCH 05/62] docs: edits, add the missing example --- content/docs/guides/neon-authorize-drizzle.md | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/content/docs/guides/neon-authorize-drizzle.md b/content/docs/guides/neon-authorize-drizzle.md index 6eb0cf13fa..12c3450c7b 100644 --- a/content/docs/guides/neon-authorize-drizzle.md +++ b/content/docs/guides/neon-authorize-drizzle.md @@ -65,7 +65,30 @@ With each new feature or role, the number of policies grows. This complexity can ## How crudPolicy simplifies RLS -Drizzle's `crudPolicy` simplifies RLS by consolidating rules into a single configuration: +Drizzle's `crudPolicy` simplifies RLS by replacing multiple SQL statements with a single configuration: + +```typescript +import { crudPolicy, authenticatedRole, authUid } from "drizzle-orm/neon"; + +export const todos = pgTable( + "todos", + { + id: bigint().primaryKey(), + userId: text().notNull().default(sql`(auth.user_id())`), + task: text().notNull(), + isComplete: boolean().notNull().default(false), + }, + (table) => [ + crudPolicy({ + role: authenticatedRole, + read: authUid(table.userId), // users can only read their own todos + modify: authUid(table.userId), // users can only modify their own todos + }), + ] +); +``` + +The `crudPolicy` function accepts three key parameters: - `role`: The Postgres role(s) to apply the policy to. Can be a single role or an array of roles - `read`: Controls SELECT operations: @@ -79,17 +102,23 @@ Drizzle's `crudPolicy` simplifies RLS by consolidating rules into a single confi - A custom SQL expression - `null` to prevent policy generation -It returns an array of Postgres RLSpolicy definitions, one for each operation (select, insert, update, delete). - -## Common patterns +It returns an array of RLS policy definitions, one for each operation (select, insert, update, delete). -Before looking at patterns, let's understand the `authUid` function. It provides a simple way to connect `auth.user_id()` to a column in your table: +Notice that `authUid` is a wrapper around Neon Authorize's `auth.user_id()` function. While `auth.user_id()` comes from the [pg_session_jwt](/docs/guides/neon-authorize#how-the-pgsessionjwt-extension-works) Postgres extension, Drizzle provides this wrapper to make it easier to use in your schema: ```typescript -export const authUid = (userIdColumn: AnyPgColumn) => - sql`(select auth.user_id() = ${userIdColumn})`; +export const authUid = (userIdColumn: AnyPgColumn) => sql`(select auth.user_id() = ${userIdColumn})`; ``` +This wrapper: + +- Integrates smoothly with Drizzle schemas +- Simplifies comparing the authenticated user ID with your table's user column + +## Common patterns + +Here are two typical ways to use `crudPolicy` for securing your tables: + ### Basic access control The most common pattern is restricting users to their own data: @@ -138,7 +167,7 @@ export const posts = pgTable( (table) => [ // Public read access crudPolicy({ - role: anonymous, + role: anonymousRole, read: true, // anyone can read posts modify: false, // no modifications allowed }), From 838de26ef98a6a9cd03af06026cbed45524d47dc Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 28 Nov 2024 21:29:45 +0000 Subject: [PATCH 06/62] chore: format content markdown files with Prettier --- content/docs/guides/neon-authorize-drizzle.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/content/docs/guides/neon-authorize-drizzle.md b/content/docs/guides/neon-authorize-drizzle.md index 12c3450c7b..b6103cad14 100644 --- a/content/docs/guides/neon-authorize-drizzle.md +++ b/content/docs/guides/neon-authorize-drizzle.md @@ -68,21 +68,23 @@ With each new feature or role, the number of policies grows. This complexity can Drizzle's `crudPolicy` simplifies RLS by replacing multiple SQL statements with a single configuration: ```typescript -import { crudPolicy, authenticatedRole, authUid } from "drizzle-orm/neon"; +import { crudPolicy, authenticatedRole, authUid } from 'drizzle-orm/neon'; export const todos = pgTable( - "todos", + 'todos', { id: bigint().primaryKey(), - userId: text().notNull().default(sql`(auth.user_id())`), + userId: text() + .notNull() + .default(sql`(auth.user_id())`), task: text().notNull(), isComplete: boolean().notNull().default(false), }, (table) => [ crudPolicy({ role: authenticatedRole, - read: authUid(table.userId), // users can only read their own todos - modify: authUid(table.userId), // users can only modify their own todos + read: authUid(table.userId), // users can only read their own todos + modify: authUid(table.userId), // users can only modify their own todos }), ] ); @@ -107,7 +109,8 @@ It returns an array of RLS policy definitions, one for each operation (select, i Notice that `authUid` is a wrapper around Neon Authorize's `auth.user_id()` function. While `auth.user_id()` comes from the [pg_session_jwt](/docs/guides/neon-authorize#how-the-pgsessionjwt-extension-works) Postgres extension, Drizzle provides this wrapper to make it easier to use in your schema: ```typescript -export const authUid = (userIdColumn: AnyPgColumn) => sql`(select auth.user_id() = ${userIdColumn})`; +export const authUid = (userIdColumn: AnyPgColumn) => + sql`(select auth.user_id() = ${userIdColumn})`; ``` This wrapper: From 223b18edb40704b2ca4f0253f721adfb420a1bf0 Mon Sep 17 00:00:00 2001 From: bgrenon Date: Thu, 28 Nov 2024 18:28:01 -0400 Subject: [PATCH 07/62] docs: edits --- content/docs/guides/neon-authorize.md | 28 +++++++-------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/content/docs/guides/neon-authorize.md b/content/docs/guides/neon-authorize.md index 5d20453498..dd4cd1a9a9 100644 --- a/content/docs/guides/neon-authorize.md +++ b/content/docs/guides/neon-authorize.md @@ -59,27 +59,18 @@ In a traditional setup, you might handle authorization for a function directly i ```typescript shouldWrap export async function insertTodo(newTodo: { newTodo: string; userId: string }) { const { userId } = auth(); // Gets the user's ID from the JWT or session + const authToken = getToken(); if (!userId) throw new Error('No user logged in'); // No user authenticated if (newTodo.userId !== userId) throw new Error('Unauthorized'); // User mismatch - // Inserts the new todo, linking it to the authenticated user - const db = drizzle( - neon(process.env.DATABASE_AUTHENTICATED_URL!, { - authToken: authToken, - }), - { - schema, - } - ); - - await db.insert(schema.todos).values({ + const db = drizzle(process.env.DATABASE_AUTHENTICATED_URL!, { schema }); + + return db.$withAuth(authToken).insert(schema.todos).values({ task: newTodo.newTodo, isComplete: false, userId, // Explicitly ties todo to the user }); - - revalidatePath('/'); } ``` @@ -90,7 +81,7 @@ In this case, you have to: ### After Neon Authorize (RLS in the database): -With Neon Authorize, you can let the database handle the authorization through **Row-Level Security** (RLS) policies. Here's an example of applying authorization for creating new todo items, where only authenticated users can insert data: +With Neon Authorize, you only need to pass the JWT to the database - authorization checks happen automatically through RLS policies: @@ -121,19 +112,14 @@ CREATE POLICY "create todos" ON "todos" Now, in your backend, you can simplify the logic, removing the user authentication checks and explicit authorization handling. ```typescript shouldWrap -export async function insertTodo(newTodo: { newTodo: string }) { +export async function insertTodo({ newTodo }: { newTodo: string }) { const authToken = getToken(); - const db = drizzle(process.env.DATABASE_AUTHENTICATED_URL!, { - schema, - }); + const db = drizzle(process.env.DATABASE_AUTHENTICATED_URL!, { schema }); return db.$withAuth(authToken).insert(schema.todos).values({ task: newTodo.newTodo, isComplete: false, - userId, // Explicitly ties todo to the user }); - - revalidatePath('/'); } ``` From d76c4dc8caa0ff41d897a24c9371b134f1d665d9 Mon Sep 17 00:00:00 2001 From: bgrenon Date: Fri, 29 Nov 2024 16:08:00 -0400 Subject: [PATCH 08/62] docs: edits --- content/docs/guides/neon-authorize-drizzle.md | 39 +++++++++++-------- content/docs/guides/neon-authorize.md | 13 ++++++- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/content/docs/guides/neon-authorize-drizzle.md b/content/docs/guides/neon-authorize-drizzle.md index b6103cad14..88a1977ba6 100644 --- a/content/docs/guides/neon-authorize-drizzle.md +++ b/content/docs/guides/neon-authorize-drizzle.md @@ -19,9 +19,7 @@ enableTableOfContents: true ## Why simplify RLS policies? -Using Row-Level Security (RLS) policies in your database schema is a best practice in application development. While it may not be your only security measure, RLS acts as a reliable last line of defense, protecting your data from unauthorized access where it lives: at the database level. - -However, implementing RLS comes with a few challenges. Writing and maintaining SQL for each CRUD operation (Create, Read, Update, Delete) can lead to repetitive code and increased complexity: both tedious and prone to errors. +Row-Level Security (RLS) is an important last line of defense for protecting your data at the database level. However, implementing RLS requires writing and maintaining separate SQL policies for each CRUD operation (Create, Read, Update, Delete), which can be both tedious and error-prone. ### For example @@ -61,15 +59,17 @@ CREATE POLICY "crud-authenticated-policy-update" ON "todos" AS PERMISSIVE FOR UP CREATE POLICY "delete todos" ON "todos" AS PERMISSIVE FOR DELETE TO "authenticated" USING ((select auth.user_id() = user_id)); ``` -With each new feature or role, the number of policies grows. This complexity can lead to subtle bugs, like accidentally allowing a user to update todos they can't read, or forgetting to apply a policy to a new table. These issues can be hard to spot in a large schema file filled with SQL statements. +As you add new features, you'll need to add more policies to match. This growing complexity can lead to subtle bugs that can be hard to spot in a large schema file filled with SQL statements. + +## Simplifying RLS with crudPolicy -## How crudPolicy simplifies RLS +The `crudPolicy` function generates RLS policies by accepting a simple configuration object. Let's break down its usage: -Drizzle's `crudPolicy` simplifies RLS by replacing multiple SQL statements with a single configuration: ```typescript import { crudPolicy, authenticatedRole, authUid } from 'drizzle-orm/neon'; +// Define a table with RLS policies export const todos = pgTable( 'todos', { @@ -81,16 +81,19 @@ export const todos = pgTable( isComplete: boolean().notNull().default(false), }, (table) => [ + // Apply RLS policy crudPolicy({ role: authenticatedRole, - read: authUid(table.userId), // users can only read their own todos - modify: authUid(table.userId), // users can only modify their own todos + read: authUid(table.userId), + modify: authUid(table.userId), }), ] ); ``` -The `crudPolicy` function accepts three key parameters: +### Configuration parameters + +The `crudPolicy` function accepts these parameters: - `role`: The Postgres role(s) to apply the policy to. Can be a single role or an array of roles - `read`: Controls SELECT operations: @@ -104,23 +107,25 @@ The `crudPolicy` function accepts three key parameters: - A custom SQL expression - `null` to prevent policy generation -It returns an array of RLS policy definitions, one for each operation (select, insert, update, delete). +When executed, `crudPolicy` generates an array of RLS policy definitions covering all CRUD operations (select, insert, update, delete). + +### The authUid Helper -Notice that `authUid` is a wrapper around Neon Authorize's `auth.user_id()` function. While `auth.user_id()` comes from the [pg_session_jwt](/docs/guides/neon-authorize#how-the-pgsessionjwt-extension-works) Postgres extension, Drizzle provides this wrapper to make it easier to use in your schema: +For user-specific policies, Drizzle provides the `authUid` helper function: ```typescript export const authUid = (userIdColumn: AnyPgColumn) => sql`(select auth.user_id() = ${userIdColumn})`; ``` -This wrapper: - -- Integrates smoothly with Drizzle schemas -- Simplifies comparing the authenticated user ID with your table's user column +This helper: +1. Wraps Neon Authorize's `auth.user_id()` function (from the [pg_session_jwt](/docs/guides/neon-authorize#how-the-pgsessionjwt-extension-works) extension) +2. Compares the authenticated user's ID with a table column +3. Returns a SQL expression suitable for use in `read` and `modify` parameters ## Common patterns -Here are two typical ways to use `crudPolicy` for securing your tables: +Now that we understand how `crudPolicy` works, let's look at two typical ways to secure your tables: ### Basic access control @@ -191,4 +196,4 @@ Check out our [social wall sample application](https://github.com/neondatabase-l - Anyone can view the wall - Authenticated users can modify their own posts - + \ No newline at end of file diff --git a/content/docs/guides/neon-authorize.md b/content/docs/guides/neon-authorize.md index dd4cd1a9a9..be4f05a6f7 100644 --- a/content/docs/guides/neon-authorize.md +++ b/content/docs/guides/neon-authorize.md @@ -44,6 +44,17 @@ Behind the scenes, the [Neon Proxy](#the-role-of-the-neon-proxy) performs the va ![neon authorize architecture](/docs/guides/neon_authorize_architecture.png) +## Database Roles + +Neon Authorize works with two primary database roles: + +- **Authenticated role**: This role is intended for users who are logged in. Your application should send the authorization token when connecting using this role. +- **Anonymous role**: This role is intended for users who are not logged in. It should allow limited access, such as reading public content (e.g., blog posts) without authentication. + + +Some authentication providers, like Firebase, support "anonymous authentication" where a unique user ID is automatically generated for visitors who haven't explicitly logged in. This is useful for features like shopping carts, where you want to track a user's actions before they create an account. These anonymous users will still have a valid JWT and can use the anonymous role, making it possible to track their actions while maintaining security. + + ### Using Neon Authorize with custom JWTs If you don’t want to use a third-party authentication provider, you can build your application to generate and sign its own JWTs. Here’s a sample application that demonstrates this approach: [See demo](https://github.com/neondatabase/authorize-demo-custom-jwt) @@ -195,7 +206,7 @@ Here is a non-exhaustive list of authentication providers. The table shows which | **Supabase Auth** | ❌ | N/A | N/A | | **Amazon Cognito** | ✅ | `https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json` | [docs](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html) | | **Azure AD** | ✅ | `https://login.microsoftonline.com/{tenantId}/discovery/v2.0/keys` | [docs](https://learn.microsoft.com/en-us/entra/identity-platform/access-tokens) | -| **GCP Cloud Identity** | ❌ | N/A | N/A | +| **GCP Cloud Identity** | �� | N/A | N/A | | **Descope Auth** | ✅ | `https://api.descope.com/{YOUR_DESCOPE_PROJECT_ID}/.well-known/jwks.json` | [docs](https://docs.descope.com/project-settings/jwt-templates) | ## Sample applications From 3299f30a33d233c7bb2c8c1fa610dde33ef8ecfe Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 29 Nov 2024 20:09:21 +0000 Subject: [PATCH 09/62] chore: format content markdown files with Prettier --- content/docs/guides/neon-authorize-drizzle.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/guides/neon-authorize-drizzle.md b/content/docs/guides/neon-authorize-drizzle.md index 88a1977ba6..e7fb8d2f6e 100644 --- a/content/docs/guides/neon-authorize-drizzle.md +++ b/content/docs/guides/neon-authorize-drizzle.md @@ -65,7 +65,6 @@ As you add new features, you'll need to add more policies to match. This growing The `crudPolicy` function generates RLS policies by accepting a simple configuration object. Let's break down its usage: - ```typescript import { crudPolicy, authenticatedRole, authUid } from 'drizzle-orm/neon'; @@ -119,6 +118,7 @@ export const authUid = (userIdColumn: AnyPgColumn) => ``` This helper: + 1. Wraps Neon Authorize's `auth.user_id()` function (from the [pg_session_jwt](/docs/guides/neon-authorize#how-the-pgsessionjwt-extension-works) extension) 2. Compares the authenticated user's ID with a table column 3. Returns a SQL expression suitable for use in `read` and `modify` parameters @@ -196,4 +196,4 @@ Check out our [social wall sample application](https://github.com/neondatabase-l - Anyone can view the wall - Authenticated users can modify their own posts - \ No newline at end of file + From bb2d2247834459fbe13f0e7d02b349f82d219b34 Mon Sep 17 00:00:00 2001 From: bgrenon Date: Fri, 29 Nov 2024 16:13:18 -0400 Subject: [PATCH 10/62] docs: edit --- content/docs/guides/neon-authorize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/guides/neon-authorize.md b/content/docs/guides/neon-authorize.md index be4f05a6f7..9421c91276 100644 --- a/content/docs/guides/neon-authorize.md +++ b/content/docs/guides/neon-authorize.md @@ -44,7 +44,7 @@ Behind the scenes, the [Neon Proxy](#the-role-of-the-neon-proxy) performs the va ![neon authorize architecture](/docs/guides/neon_authorize_architecture.png) -## Database Roles +## Database roles Neon Authorize works with two primary database roles: From 5787ed4d08752c175c69c222bd9c3eec5c2b9066 Mon Sep 17 00:00:00 2001 From: bgrenon Date: Mon, 2 Dec 2024 17:40:21 +0100 Subject: [PATCH 11/62] docs: edits --- content/docs/guides/neon-authorize-drizzle.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/content/docs/guides/neon-authorize-drizzle.md b/content/docs/guides/neon-authorize-drizzle.md index e7fb8d2f6e..12cff266eb 100644 --- a/content/docs/guides/neon-authorize-drizzle.md +++ b/content/docs/guides/neon-authorize-drizzle.md @@ -28,6 +28,8 @@ To illustrate, let's consider a simple **Todo** list app with RLS policies appli - `USING` clause — controls which existing rows can be accessed - `WITH CHECK` clause — controls what new or modified data can be written +To get an understanding of `auth.user_id()` and the role it plays in these policies, see this [explanation](https://neon.tech/docs/guides/neon-authorize#how-neon-authorize-gets-authuserid-from-the-jwt). + Here's how these clauses apply to each operation: | Operation | USING clause | WITH CHECK clause | @@ -65,7 +67,7 @@ As you add new features, you'll need to add more policies to match. This growing The `crudPolicy` function generates RLS policies by accepting a simple configuration object. Let's break down its usage: -```typescript +```typescript {16-20} import { crudPolicy, authenticatedRole, authUid } from 'drizzle-orm/neon'; // Define a table with RLS policies @@ -131,7 +133,7 @@ Now that we understand how `crudPolicy` works, let's look at two typical ways to The most common pattern is restricting users to their own data: -```typescript shouldWrap +```typescript {15-19} shouldWrap import { crudPolicy, authenticatedRole, authUid } from 'drizzle-orm/neon'; export const todos = pgTable( @@ -159,7 +161,7 @@ export const todos = pgTable( For more complex scenarios, you might want different permissions for different roles: -```typescript shouldWrap +```typescript {15-19,21-25} shouldWrap import { crudPolicy, authenticatedRole, anonymousRole } from 'drizzle-orm/neon'; export const posts = pgTable( From 321c800bba9b010bf1a8c08b391fdf2ecf88e45b Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 2 Dec 2024 16:41:43 +0000 Subject: [PATCH 12/62] chore: format content markdown files with Prettier --- content/docs/guides/neon-authorize-drizzle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/guides/neon-authorize-drizzle.md b/content/docs/guides/neon-authorize-drizzle.md index 12cff266eb..52f757b699 100644 --- a/content/docs/guides/neon-authorize-drizzle.md +++ b/content/docs/guides/neon-authorize-drizzle.md @@ -133,7 +133,7 @@ Now that we understand how `crudPolicy` works, let's look at two typical ways to The most common pattern is restricting users to their own data: -```typescript {15-19} shouldWrap +```typescript {15-19} shouldWrap import { crudPolicy, authenticatedRole, authUid } from 'drizzle-orm/neon'; export const todos = pgTable( From 56a9af31d3f591358f0dee1c420df07d6459cc8e Mon Sep 17 00:00:00 2001 From: Dhanush Reddy <29dhanushreddy@gmail.com> Date: Tue, 3 Dec 2024 23:27:26 +0530 Subject: [PATCH 13/62] docs: add firebase neon authorize guide --- .../docs/guides/neon-authorize-firebase.md | 327 +++++++++++++++++ ...n-authorize-gcp-cloud-identity-platform.md | 331 ++++++++++++++++++ content/docs/sidebar.yaml | 4 + .../docs/guides/firebase_jwks_url_in_neon.png | Bin 0 -> 63993 bytes public/docs/guides/firebase_project_id.png | Bin 0 -> 52488 bytes 5 files changed, 662 insertions(+) create mode 100644 content/docs/guides/neon-authorize-firebase.md create mode 100644 content/docs/guides/neon-authorize-gcp-cloud-identity-platform.md create mode 100644 public/docs/guides/firebase_jwks_url_in_neon.png create mode 100644 public/docs/guides/firebase_project_id.png diff --git a/content/docs/guides/neon-authorize-firebase.md b/content/docs/guides/neon-authorize-firebase.md new file mode 100644 index 0000000000..06585a8c7c --- /dev/null +++ b/content/docs/guides/neon-authorize-firebase.md @@ -0,0 +1,327 @@ +--- +title: Secure your data with Firebase and Neon Authorize +subtitle: Implement Row-level Security policies in Postgres using Firebase and Neon Authorize +enableTableOfContents: true +updatedOn: '2024-12-03T10:00:00.000Z' +--- + + + + Neon Authorize Tutorial + + + +Firebase handles user authentication by generating JSON Web Tokens (JWTs), which are securely passed to Neon Authorize. Neon Authorize validates these tokens and uses the embedded user identity metadata to enforce the Row-Level Security policies that you define directly in Postgres, securing database queries based on that user identity. This authorization flow is made possible using the Postgres extension pg_session_jwt, which you'll install as part of this guide. + +## How it works + +Firebase Authentication handles user authentication by generating JSON Web Tokens (JWTs), which are securely passed to Neon Authorize. Neon Authorize validates these tokens and uses the embedded user identity metadata to enforce the [Row-Level Security](https://neon.tech/postgresql/postgresql-administration/postgresql-row-level-security) policies that you define directly in Postgres, securing database queries based on that user identity. This authorization flow is made possible using the Postgres extension [pg_session_jwt](https://github.com/neondatabase/pg_session_jwt). + +## Prerequisites + +To follow along with this guide, you will need: + +- A Neon account. Sign up at [Neon](https://neon.tech) if you don't have one. +- A [Firebase](https://firebase.google.com) project with Authentication enabled. If you haven't set up Firebase Auth yet, follow the [Firebase Authentication documentation](https://firebase.google.com/docs/auth). + +## Integrate Firebase with Neon Authorize + +In this first set of steps, we'll integrate Firebase as an authorization provider in Neon. When these steps are complete, Firebase will start passing JWTs to your Neon database, which you can then use to create policies. + +### 1. Get your Firebase JWKS URL + +When integrating Firebase with Neon, you'll need to provide the JWKS (JSON Web Key Set) URL. This allows your database to validate the JWT tokens and extract the user_id for use in RLS policies. + +The Firebase JWKS URL is always in the format: + +``` +https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com +``` + +To use Firebase, you'll need the JWT Audience value, which is your Firebase project's `project_id`. Find this under **Project settings** > **General** > **Project ID** in the Firebase Console. + +
+ Firebase Project Id +
+ +### 2. Add Firebase as an authorization provider in the Neon Console + +Once you have the JWKS URL, go to the **Neon Console** and add Firebase as an authentication provider under the **Authorize** page. Paste your copied URL and Firebase will be automatically recognized and selected. Add the JWT Audience value (your Firebase project's `project_id`) and click **Set Up**. + +
+ Add Authentication Provider +
+ +At this point, you can use the **Get Started** setup steps from the Authorize page in Neon to complete the setup — this guide is modeled on those steps. Or feel free to keep following along in this guide, where we'll give you a bit more context. + +### 3. Install the pg_session_jwt extension in your database + +Neon Authorize uses the [pg_session_jwt](https://github.com/neondatabase/pg_session_jwt) extension to handle authenticated sessions through JSON Web Tokens (JWTs). This extension allows secure transmission of authentication data from your application to Postgres, where you can enforce Row-Level Security (RLS) policies based on the user's identity. + +To install the extension in the `neondb` database, run: + +```sql +CREATE EXTENSION IF NOT EXISTS pg_session_jwt; +``` + +### 4. Set up Postgres roles + +The integration creates the `authenticated` and `anonymous` roles for you. Let's define table-level permissions for these roles. To allow both roles to read and write to tables in your public schema, run: + +```sql shouldWrap +-- For existing tables +GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES + IN SCHEMA public + to authenticated; + +GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES + IN SCHEMA public + to anonymous; + +-- For future tables +ALTER DEFAULT PRIVILEGES + IN SCHEMA public + GRANT SELECT, UPDATE, INSERT, DELETE ON TABLES + TO authenticated; + +ALTER DEFAULT PRIVILEGES + IN SCHEMA public + GRANT SELECT, UPDATE, INSERT, DELETE ON TABLES + TO anonymous; + +-- Grant USAGE on "public" schema +GRANT USAGE ON SCHEMA public TO authenticated; +GRANT USAGE ON SCHEMA public TO anonymous; +``` + +- **Authenticated role**: This role is intended for users who are logged in. Your application should send the authorization token when connecting using this role. +- **Anonymous role**: This role is intended for users who are not logged in. It should allow limited access, such as reading public content (e.g., blog posts) without authentication. + +### 5. Install the Neon Serverless Driver + +Neon’s Serverless Driver manages the connection between your application and the Neon Postgres database. For Neon Authorize, you must use HTTP. While it is technically possible to access the HTTP API without using our driver, we recommend using the driver for best performance. The driver also supports WebSockets and TCP connections, so make sure you use the HTTP method when working with Neon Authorize. + +Install it using the following command: + +```bash +npm install @neondatabase/serverless +``` + +To learn more about the driver, see [Neon Serverless Driver](/docs/serverless/serverless-driver). + +### 6. Set up environment variables + +Here is an example of setting up administrative and authenticated database connections in your `.env` file. Copy the connection strings for both the `neondb_owner` and `authenticated` roles. You can find them from **Connection Details** in the Neon Console, or using the Neon CLI: + +```bash +neonctl connection-string --role-name neondb_owner +neonctl connection-string --role-name authenticated +``` + +Add these to your `.env` file. + +```bash shouldWrap +# Database owner connection string +DATABASE_URL='' + +# Neon "authenticated" role connection string +DATABASE_AUTHENTICATED_URL='' +``` + +The `DATABASE_URL` is intended for admin tasks and can run any query while the `DATABASE_AUTHENTICATED_URL` should be used for connections from authorized users, where you pass the required authorization token. You can see an example in [Run your first authorized query](#2-run-your-first-authorized-query) below. + +## Add RLS policies + +Now that you’ve integrated Descope with Neon Authorize, you can securely pass JWTs to your Neon database. Let's start looking at how to add RLS policies to your schema and how you can execute authenticated queries from your application. + +### 1. Add Row-Level Security policies + +Here are examples of implementing RLS policies for a **todos** table – the Drizzle example leverages the simplified `crudPolicy` function, while the SQL example demonstrates the use of individual RLS policies. + + + + + +```typescript shouldWrap +import { InferSelectModel, sql } from 'drizzle-orm'; +import { bigint, boolean, pgTable, text, timestamp } from 'drizzle-orm/pg-core'; +import { authenticatedRole, authUid, crudPolicy } from 'drizzle-orm/neon'; + +// schema for TODOs table +export const todos = pgTable( + 'todos', + { + id: bigint('id', { mode: 'bigint' }).primaryKey().generatedByDefaultAsIdentity(), + userId: text('user_id') + .notNull() + .default(sql`(auth.user_id())`), + task: text('task').notNull(), + isComplete: boolean('is_complete').notNull().default(false), + insertedAt: timestamp('inserted_at', { withTimezone: true }).defaultNow().notNull(), + }, + // Create RLS policy for the table + (table) => [ + crudPolicy({ + role: authenticatedRole, + read: authUid(table.userId), + modify: authUid(table.userId), + }), + ] +); + +export type Todo = InferSelectModel; +``` + + + + + +```sql shouldWrap +-- schema for TODOs table +CREATE TABLE todos ( + id bigint generated by default as identity primary key, + user_id text not null default (auth.user_id()), + task text check (char_length(task) > 0), + is_complete boolean default false, + inserted_at timestamptz not null default now() +); + +-- 1st enable row level security for your table +ALTER TABLE todos ENABLE ROW LEVEL SECURITY; + +-- 2nd create policies for your table +CREATE POLICY "Individuals can create todos." ON todos FOR INSERT +TO authenticated +WITH CHECK ((select auth.user_id()) = user_id); + +CREATE POLICY "Individuals can view their own todos." ON todos FOR SELECT +TO authenticated +USING ((select auth.user_id()) = user_id); + +CREATE POLICY "Individuals can update their own todos." ON todos FOR UPDATE +TO authenticated +USING ((select auth.user_id()) = user_id) +WITH CHECK ((select auth.user_id()) = user_id); + +CREATE POLICY "Individuals can delete their own todos." ON todos FOR DELETE +TO authenticated +USING ((select auth.user_id()) = user_id); +``` + + + + +The `crudPolicy` function simplifies policy creation by generating all necessary CRUD policies with a single declaration. + +### 2. Run your first authorized query + +Here's how to run authenticated queries using Firebase Auth tokens: + + + + + +```typescript +'use server'; + +import { neon } from '@neondatabase/serverless'; +import { getUserInfo } from '@/lib/auth' + +export default async function TodoList() { + const userInfo = await getUserInfo(); // [!code highlight] + if (!userInfo) { + throw new Error('No user info available'); + } + + const sql = neon(process.env.DATABASE_AUTHENTICATED_URL!, { + authToken: async () => { + const jwt = userInfo.token; // [!code highlight] + if (!jwt) { + throw new Error('No JWT token available'); + } + return jwt; + }, + }); + + // WHERE filter is optional because of RLS. + // But we send it anyway for performance reasons. + const todos = await + sql('SELECT * FROM todos WHERE user_id = auth.user_id()'); // [!code highlight] + + return ( +
    + {todos.map((todo) => ( +
  • {todo.task}
  • + ))} +
+ ); +} +``` + +
+ + + +```typescript +'use client'; + +import type { Todo } from '@/app/schema'; +import { neon } from '@neondatabase/serverless'; +import { getAuth } from 'firebase/auth'; +import { useEffect, useState } from 'react'; + +const getDb = (token: string) => + neon(process.env.NEXT_PUBLIC_DATABASE_AUTHENTICATED_URL!, { + authToken: token, // [!code highlight] + }); + +export function TodoList() { + const auth = getAuth(); + const [todos, setTodos] = useState>(); + + useEffect(() => { + async function loadTodos() { + const user = auth.currentUser; + if (!user) { + return; + } + + const idToken = await user.getIdToken(); // [!code highlight] + const sql = getDb(idToken); + + const todosResponse = await + sql('SELECT * FROM todos WHERE user_id = auth.user_id()'); // [!code highlight] + setTodos(todosResponse as Array); + } + + loadTodos(); + }, [auth.currentUser]); + + return ( +
    + {todos?.map((todo) => ( +
  • {todo.task}
  • + ))} +
+ ); +} +``` + +
+ + + +```bash shouldWrap +# Used for database migrations +DATABASE_URL='' + +# Used for server-side fetching +DATABASE_AUTHENTICATED_URL='' + +# Used for client-side fetching +NEXT_PUBLIC_DATABASE_AUTHENTICATED_URL='' +``` + + + +
diff --git a/content/docs/guides/neon-authorize-gcp-cloud-identity-platform.md b/content/docs/guides/neon-authorize-gcp-cloud-identity-platform.md new file mode 100644 index 0000000000..26606676a8 --- /dev/null +++ b/content/docs/guides/neon-authorize-gcp-cloud-identity-platform.md @@ -0,0 +1,331 @@ +--- +title: Secure your data with Google Cloud Identity Platform and Neon Authorize +subtitle: Implement Row-level Security policies in Postgres using Google Cloud Identity Platform and Neon Authorize +enableTableOfContents: true +updatedOn: '2024-12-03T10:00:00.000Z' +--- + + + + Neon Authorize Tutorial + + + +GCP Identity Platform handles user authentication by generating JSON Web Tokens (JWTs), which are securely passed to Neon Authorize. Neon Authorize validates these tokens and uses the embedded user identity metadata to enforce the Row-Level Security policies that you define directly in Postgres, securing database queries based on that user identity. This authorization flow is made possible using the Postgres extension pg_session_jwt, which you'll install as part of this guide. + +## How it works + +GCP Identity Platform Authentication handles user authentication by generating JSON Web Tokens (JWTs), which are securely passed to Neon Authorize. Neon Authorize validates these tokens and uses the embedded user identity metadata to enforce the [Row-Level Security](https://neon.tech/postgresql/postgresql-administration/postgresql-row-level-security) policies that you define directly in Postgres, securing database queries based on that user identity. This authorization flow is made possible using the Postgres extension [pg_session_jwt](https://github.com/neondatabase/pg_session_jwt). + +## Prerequisites + +To follow along with this guide, you will need: + +- A Neon account. Sign up at [Neon](https://neon.tech) if you don't have one. +- A [GCP Identity Platform](https://cloud.google.com/security/products/identity-platform) project with Authentication enabled. If you haven't set up Identity Platform yet, follow the [Quickstart](https://cloud.google.com/identity-platform/docs/sign-in-user-email). + +## Integrate GCP Identity Platform with Neon Authorize + +In this first set of steps, we'll integrate GCP Identity Platform as an authorization provider in Neon. When these steps are complete, GCP Identity Platform will start passing JWTs to your Neon database, which you can then use to create policies. + +### 1. Get your GCP Identity Platform JWKS URL + +When integrating GCP Identity Platform with Neon, you'll need to provide the JWKS (JSON Web Key Set) URL. This allows your database to validate the JWT tokens and extract the user_id for use in RLS policies. + +The GCP Identity Platform JWKS URL is always in the format: + +``` +https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com +``` + + +Every GCP Identity Platform project automatically creates a corresponding Firebase project. The Firebase project's ID serves as the JWT Audience value required by Neon Authorize. You can find this ID in the [Firebase Console](https://console.firebase.google.com/) + + +To use GCP Identity Platform, you'll need the JWT Audience value, which is your Firebase project's `project_id`. Find this under **Project settings** > **General** > **Project ID** in the Firebase Console. + +
+ Firebase Project Id +
+ +### 2. Add GCP Identity Platform as an authorization provider in the Neon Console + +Once you have the JWKS URL, go to the **Neon Console** and add GCP Identity Platform as an authentication provider under the **Authorize** page. Paste your copied URL and GCP Identity Platform will be automatically recognized and selected. Add the JWT Audience value (your Firebase project's `project_id`) and click **Set Up**. + +
+ Add Authentication Provider +
+ +At this point, you can use the **Get Started** setup steps from the Authorize page in Neon to complete the setup — this guide is modeled on those steps. Or feel free to keep following along in this guide, where we'll give you a bit more context. + +### 3. Install the pg_session_jwt extension in your database + +Neon Authorize uses the [pg_session_jwt](https://github.com/neondatabase/pg_session_jwt) extension to handle authenticated sessions through JSON Web Tokens (JWTs). This extension allows secure transmission of authentication data from your application to Postgres, where you can enforce Row-Level Security (RLS) policies based on the user's identity. + +To install the extension in the `neondb` database, run: + +```sql +CREATE EXTENSION IF NOT EXISTS pg_session_jwt; +``` + +### 4. Set up Postgres roles + +The integration creates the `authenticated` and `anonymous` roles for you. Let's define table-level permissions for these roles. To allow both roles to read and write to tables in your public schema, run: + +```sql shouldWrap +-- For existing tables +GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES + IN SCHEMA public + to authenticated; + +GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES + IN SCHEMA public + to anonymous; + +-- For future tables +ALTER DEFAULT PRIVILEGES + IN SCHEMA public + GRANT SELECT, UPDATE, INSERT, DELETE ON TABLES + TO authenticated; + +ALTER DEFAULT PRIVILEGES + IN SCHEMA public + GRANT SELECT, UPDATE, INSERT, DELETE ON TABLES + TO anonymous; + +-- Grant USAGE on "public" schema +GRANT USAGE ON SCHEMA public TO authenticated; +GRANT USAGE ON SCHEMA public TO anonymous; +``` + +- **Authenticated role**: This role is intended for users who are logged in. Your application should send the authorization token when connecting using this role. +- **Anonymous role**: This role is intended for users who are not logged in. It should allow limited access, such as reading public content (e.g., blog posts) without authentication. + +### 5. Install the Neon Serverless Driver + +Neon’s Serverless Driver manages the connection between your application and the Neon Postgres database. For Neon Authorize, you must use HTTP. While it is technically possible to access the HTTP API without using our driver, we recommend using the driver for best performance. The driver also supports WebSockets and TCP connections, so make sure you use the HTTP method when working with Neon Authorize. + +Install it using the following command: + +```bash +npm install @neondatabase/serverless +``` + +To learn more about the driver, see [Neon Serverless Driver](/docs/serverless/serverless-driver). + +### 6. Set up environment variables + +Here is an example of setting up administrative and authenticated database connections in your `.env` file. Copy the connection strings for both the `neondb_owner` and `authenticated` roles. You can find them from **Connection Details** in the Neon Console, or using the Neon CLI: + +```bash +neonctl connection-string --role-name neondb_owner +neonctl connection-string --role-name authenticated +``` + +Add these to your `.env` file. + +```bash shouldWrap +# Database owner connection string +DATABASE_URL='' + +# Neon "authenticated" role connection string +DATABASE_AUTHENTICATED_URL='' +``` + +The `DATABASE_URL` is intended for admin tasks and can run any query while the `DATABASE_AUTHENTICATED_URL` should be used for connections from authorized users, where you pass the required authorization token. You can see an example in [Run your first authorized query](#2-run-your-first-authorized-query) below. + +## Add RLS policies + +Now that you’ve integrated Descope with Neon Authorize, you can securely pass JWTs to your Neon database. Let's start looking at how to add RLS policies to your schema and how you can execute authenticated queries from your application. + +### 1. Add Row-Level Security policies + +Here are examples of implementing RLS policies for a **todos** table – the Drizzle example leverages the simplified `crudPolicy` function, while the SQL example demonstrates the use of individual RLS policies. + + + + + +```typescript shouldWrap +import { InferSelectModel, sql } from 'drizzle-orm'; +import { bigint, boolean, pgTable, text, timestamp } from 'drizzle-orm/pg-core'; +import { authenticatedRole, authUid, crudPolicy } from 'drizzle-orm/neon'; + +// schema for TODOs table +export const todos = pgTable( + 'todos', + { + id: bigint('id', { mode: 'bigint' }).primaryKey().generatedByDefaultAsIdentity(), + userId: text('user_id') + .notNull() + .default(sql`(auth.user_id())`), + task: text('task').notNull(), + isComplete: boolean('is_complete').notNull().default(false), + insertedAt: timestamp('inserted_at', { withTimezone: true }).defaultNow().notNull(), + }, + // Create RLS policy for the table + (table) => [ + crudPolicy({ + role: authenticatedRole, + read: authUid(table.userId), + modify: authUid(table.userId), + }), + ] +); + +export type Todo = InferSelectModel; +``` + + + + + +```sql shouldWrap +-- schema for TODOs table +CREATE TABLE todos ( + id bigint generated by default as identity primary key, + user_id text not null default (auth.user_id()), + task text check (char_length(task) > 0), + is_complete boolean default false, + inserted_at timestamptz not null default now() +); + +-- 1st enable row level security for your table +ALTER TABLE todos ENABLE ROW LEVEL SECURITY; + +-- 2nd create policies for your table +CREATE POLICY "Individuals can create todos." ON todos FOR INSERT +TO authenticated +WITH CHECK ((select auth.user_id()) = user_id); + +CREATE POLICY "Individuals can view their own todos." ON todos FOR SELECT +TO authenticated +USING ((select auth.user_id()) = user_id); + +CREATE POLICY "Individuals can update their own todos." ON todos FOR UPDATE +TO authenticated +USING ((select auth.user_id()) = user_id) +WITH CHECK ((select auth.user_id()) = user_id); + +CREATE POLICY "Individuals can delete their own todos." ON todos FOR DELETE +TO authenticated +USING ((select auth.user_id()) = user_id); +``` + + + + +The `crudPolicy` function simplifies policy creation by generating all necessary CRUD policies with a single declaration. + +### 2. Run your first authorized query + +Here's how to run authenticated queries using Firebase Auth tokens: + + + + + +```typescript +'use server'; + +import { neon } from '@neondatabase/serverless'; +import { getUserInfo } from '@/lib/auth' + +export default async function TodoList() { + const userInfo = await getUserInfo(); // [!code highlight] + if (!userInfo) { + throw new Error('No user info available'); + } + + const sql = neon(process.env.DATABASE_AUTHENTICATED_URL!, { + authToken: async () => { + const jwt = userInfo.token; // [!code highlight] + if (!jwt) { + throw new Error('No JWT token available'); + } + return jwt; + }, + }); + + // WHERE filter is optional because of RLS. + // But we send it anyway for performance reasons. + const todos = await + sql('SELECT * FROM todos WHERE user_id = auth.user_id()'); // [!code highlight] + + return ( +
    + {todos.map((todo) => ( +
  • {todo.task}
  • + ))} +
+ ); +} +``` + +
+ + + +```typescript +'use client'; + +import type { Todo } from '@/app/schema'; +import { neon } from '@neondatabase/serverless'; +import { getAuth } from 'firebase/auth'; +import { useEffect, useState } from 'react'; + +const getDb = (token: string) => + neon(process.env.NEXT_PUBLIC_DATABASE_AUTHENTICATED_URL!, { + authToken: token, // [!code highlight] + }); + +export function TodoList() { + const auth = getAuth(); + const [todos, setTodos] = useState>(); + + useEffect(() => { + async function loadTodos() { + const user = auth.currentUser; + if (!user) { + return; + } + + const idToken = await user.getIdToken(); // [!code highlight] + const sql = getDb(idToken); + + const todosResponse = await + sql('SELECT * FROM todos WHERE user_id = auth.user_id()'); // [!code highlight] + setTodos(todosResponse as Array); + } + + loadTodos(); + }, [auth.currentUser]); + + return ( +
    + {todos?.map((todo) => ( +
  • {todo.task}
  • + ))} +
+ ); +} +``` + +
+ + + +```bash shouldWrap +# Used for database migrations +DATABASE_URL='' + +# Used for server-side fetching +DATABASE_AUTHENTICATED_URL='' + +# Used for client-side fetching +NEXT_PUBLIC_DATABASE_AUTHENTICATED_URL='' +``` + + + +
diff --git a/content/docs/sidebar.yaml b/content/docs/sidebar.yaml index f95a52b5ee..9cf18c9ec1 100644 --- a/content/docs/sidebar.yaml +++ b/content/docs/sidebar.yaml @@ -481,6 +481,10 @@ slug: guides/neon-authorize-azure-ad - title: Descope slug: guides/neon-authorize-descope + - title: Firebase + slug: guides/neon-authorize-firebase + - title: GCP Identity Platform + slug: guides/neon-authorize-gcp-cloud-identity-platform - title: Integrations icon: integrations slug: guides/integrations diff --git a/public/docs/guides/firebase_jwks_url_in_neon.png b/public/docs/guides/firebase_jwks_url_in_neon.png new file mode 100644 index 0000000000000000000000000000000000000000..2d1a401a3c96b9b94fa2b1caabb7ad4922312867 GIT binary patch literal 63993 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 P;7>zU=XmBZGq3*x&Aw)K literal 0 HcmV?d00001 From 79c3f4cc300dc969ff835b314ff92682d7f39a0c Mon Sep 17 00:00:00 2001 From: Simon Kataev Date: Wed, 4 Dec 2024 12:18:22 +0100 Subject: [PATCH 14/62] refactor: move checkCookie redirects from homepage to middleware --- src/app/(home-page)/home/page.jsx | 38 ++++++++------------- src/app/(home-page)/page.jsx | 55 ++++++++++--------------------- src/middleware.js | 21 +++++++++--- 3 files changed, 49 insertions(+), 65 deletions(-) diff --git a/src/app/(home-page)/home/page.jsx b/src/app/(home-page)/home/page.jsx index 5e94337cf6..823a877f39 100644 --- a/src/app/(home-page)/home/page.jsx +++ b/src/app/(home-page)/home/page.jsx @@ -1,6 +1,3 @@ -import { redirect } from 'next/navigation'; - -import { checkCookie } from 'app/actions'; import AiIndex from 'components/pages/home/ai-index'; import Bento from 'components/pages/home/bento'; import Hero from 'components/pages/home/hero'; @@ -19,27 +16,20 @@ export const metadata = getMetadata({ robotsNoindex: 'noindex', }); -const HomePage = async () => { - const is_logged_in = await checkCookie('neon_login_indicator'); - if (!is_logged_in) { - return redirect('/'); - } - - return ( - <> - - - - - - - - - - - - ); -}; +const HomePage = () => ( + <> + + + + + + + + + + + +); export default HomePage; diff --git a/src/app/(home-page)/page.jsx b/src/app/(home-page)/page.jsx index bec7e32e85..589ed67955 100644 --- a/src/app/(home-page)/page.jsx +++ b/src/app/(home-page)/page.jsx @@ -1,6 +1,3 @@ -import { redirect } from 'next/navigation'; - -import { checkCookie, getReferer } from 'app/actions'; import AiIndex from 'components/pages/home/ai-index'; import Bento from 'components/pages/home/bento'; import Hero from 'components/pages/home/hero/hero'; @@ -11,7 +8,6 @@ import Logos from 'components/pages/home/logos'; import Multitenancy from 'components/pages/home/multitenancy'; import Trusted from 'components/pages/home/trusted'; import Cta from 'components/shared/cta'; -import LINKS from 'constants/links'; import SEO_DATA from 'constants/seo-data'; import getMetadata from 'utils/get-metadata'; @@ -28,39 +24,24 @@ const jsonLd = { url: 'https://neon.tech/', }; -const HomePage = async () => { - const is_logged_in = await checkCookie('neon_login_indicator'); - if (process.env.NODE_ENV === 'production' && is_logged_in) { - const referer = await getReferer(); - if ( - referer.includes(process.env.VERCEL_BRANCH_URL) || - referer.includes(process.env.NEXT_PUBLIC_DEFAULT_SITE_URL) - ) { - return redirect('/home'); - } - - return redirect(LINKS.console); - } - - return ( - <> -