CipherStash Docs

Identity-aware encryption

Tie encryption to a user's JWT so only that user can decrypt their data

Identity-aware encryption

Lock encryption to a specific user by requiring a valid JWT for decryption. When a value is encrypted with a lock context, it can only be decrypted by presenting the same user's identity token.

How it works

  1. Create a LockContext instance.
  2. Identify the user with their JWT.
  3. Pass the lock context to encrypt and decrypt operations.

Basic usage

identity.ts
import { LockContext } from "@cipherstash/stack/identity"

// 1. Create a lock context (defaults to the "sub" claim)
const lc = new LockContext()

// 2. Identify the user with their JWT
const identifyResult = await lc.identify(userJwt)

if (identifyResult.failure) {
  throw new Error(identifyResult.failure.message)
}

const lockContext = identifyResult.data

// 3. Encrypt with lock context
const encrypted = await client
  .encrypt("sensitive data", { column: users.email, table: users })
  .withLockContext(lockContext)

// 4. Decrypt with the same lock context
const decrypted = await client
  .decrypt(encrypted.data)
  .withLockContext(lockContext)

Supported operations

Lock contexts work with all encrypt and decrypt operations:

identity.ts
// Single operations
const encrypted = await client
  .encryptModel(user, users)
  .withLockContext(lockContext)

const decrypted = await client
  .decryptModel(encryptedUser)
  .withLockContext(lockContext)

// Bulk operations
const bulkEncrypted = await client
  .bulkEncryptModels(userModels, users)
  .withLockContext(lockContext)

const bulkDecrypted = await client
  .bulkDecryptModels(encryptedUsers)
  .withLockContext(lockContext)

Custom identity claims

Override the default context by specifying which identity claims to use:

identity.ts
const lc = new LockContext({
  context: {
    identityClaim: ["sub"],  // this is the default
  },
})
Identity claimDescription
subThe user's subject identifier
scopesThe user's scopes set by your IDP policy

Using with Clerk and Next.js

If you're using Clerk as your identity provider, install the @cipherstash/nextjs package for automatic CTS token setup.

npm install @cipherstash/nextjs

Set up middleware

In your middleware.ts, use protectClerkMiddleware to automatically generate CTS tokens for every user session:

middleware.ts
import { clerkMiddleware } from "@clerk/nextjs/server"
import { protectClerkMiddleware } from "@cipherstash/nextjs/clerk"

export default clerkMiddleware(async (auth, req) => {
  return protectClerkMiddleware(auth, req)
})

Retrieve the CTS token

Use getCtsToken to get the CTS token for the current user:

page.tsx
import { getCtsToken } from "@cipherstash/nextjs"

export default async function Page() {
  const ctsToken = await getCtsToken()

  if (!ctsToken.success) {
    // handle error
  }

  // ctsToken is ready to use
}

Create a LockContext with an existing CTS token

Since the CTS token is already available from the middleware, construct the LockContext directly:

page.tsx
import { LockContext } from "@cipherstash/stack/identity"
import { getCtsToken } from "@cipherstash/nextjs"

export default async function Page() {
  const ctsToken = await getCtsToken()

  if (!ctsToken.success) {
    // handle error
  }

  const lockContext = new LockContext({ ctsToken })

  // Use lockContext with encrypt/decrypt operations
}

On this page