This example demonstrates how to use Protect.js with Next.js. It also demonstrates how to use Lock Contexts to ensure that only the intended users can access sensitive data, by using Clerk for authentication.
This project uses the following technologies:
- pnpm for package management
- Next.js for the application framework
- Clerk for auth
- Supabase for database
- Drizzle ORM for database access
- CipherStash for data encryption
First, install dependencies:
pnpm install
Second, create a .env.local
file in the root directory with the following content:
# Clerk auth
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
# Supabase postgres connection string
POSTGRES_URL=
# CipherStash encryption and access keys
CS_CLIENT_ID=
CS_CLIENT_KEY=
CS_CLIENT_ACCESS_KEY=
CS_WORKSPACE_ID=
Finally, run the development server:
pnpm run dev
Open http://localhost:3000 with your browser to see the result.
The database is hosted on Supabase and has the following schema which is defined using the Drizzle ORM:
// Data that is encrypted using protect.js is stored as jsonb in postgres
export const users = pgTable("users", {
id: serial("id").primaryKey(),
name: varchar("name").notNull(),
email: jsonb("email").notNull(),
role: varchar("role").notNull(),
});
Note
This example does not include any searchable encrypted fields. If you want to search on encrypted fields, you will need to install EQL. The EQL library ships with custom types that are used to define encrypted fields. See the EQL documentation for more information.
All the email data is encrypted using Protect.js.
The cipherstext is stored in the email
column of the users
table.
The application is configured to only decrypt the data when the user is signed in, otherwise it will display the encrypted data.
@cipherstash/protect
uses custom Rust bindings to the CipherStash Client in order to perform encryptions and decryptions.
We leverage the Neon project to provide a JavaScript API for these bindings.
When a user is added to the database, the email address is encrypted using Protect.js.
To view the encryption implementation, see the addUser
function in src/lib/actions.ts.
To view the decrpytion implementation, see the getUsers
function in src/app/page.tsx.
Since @cipherstash/protect
is a native Node.js module, you need to opt-out from the Server Components bundling and use native Node.js require
instead.
next.config.ts
configuration:
const nextConfig = {
...
serverExternalPackages: ['@cipherstash/protect'],
}
next.config.mjs
configuration:
const nextConfig = {
...
experimental: {
serverComponentsExternalPackages: ['@cipherstash/protect'],
},
}
serverExternalPackages
does not work with workspace packages and the issues is being tracked here.
Once this is fixed upstream, this application can use the workspace package for development.
For the time being, it used @cipherstash/protect
from the npm registry.