New "How To" for implementing flexible authorization

Hello guys,

I’m the creator of the ZenStack project. I had the chance to meet with some wonderful folks from the Redwood team at the RedwoodJS Conf back in Sept. There I also gave a quick demo to Tom and Rob (@rob I believe :smile:) about how it feels to model complex authorization using the toolkit. As we’ve released several more iterations since then, and the project is in a stable V1 now, I think it makes sense to propose an integration guide into Redwood docs now.

Just a quick recap about ZenStack. It uses an extended Prisma Schema Language to model data and access policies together. It allows you to model RBAC, ABAC, or mixed. For example:

model Post {
  author User @relation(fields: [authorId], references: [id])
  authorId Int
  published Boolean

  @@allow('read', published)        // everyone can read published posts
  @@allow('all', author == auth())  // author has full access
}

At runtime, you create an enhanced PrismaClient, which automatically enforces the policies. It can be used as a drop-in replacement to the regular PrismaClient.

// `db` has the same typing as `prisma`
const db = enhance(prisma, { user: context.currentUser });

Before starting to work on a “How To” PR, I wanted to first check if the team likes the idea and, if so, whether the integration approach that I’m thinking of makes sense.

Here are the current thoughts in my head:

Set up

  • ZenStack provides a @zenstackhq/redwood package for supporting project setup and runtime.
  • Setup should be as easy as running npx @zenstackhq/redwood or yarn rw setup package @zenstackhq/redwood. I’m not sure if the latter is the preferred method.
  • What the setup does is:
    1. Install yarn dependencies
    2. Copy “db/schema.prisma” to “db/schema.model” (ZModel is the extended PSL)
    3. Install a GraphQLYoga plugin like:
      // api/src/functions/graphql.ts
      export const handler = createGraphQLHandler({
          ...
          extraPlugins: [useZenStack(db)]
      });
      
      The useZenStack plugin creates an enhanced PrismaClient for the current user and adds it into the GraphQL context as context.db.

Usage

For developers, only two main differences:

  1. Instead of modeling in db/schema.prisma, do it in db/schema.zmodel. Then run npx zenstack generate and schema.prisma will be regenerated (together with other code for supporting access policy enforcement). All Prisma workflows, like migration generation, db push, etc. stay unchanged.
  2. Instead of using db in GQL services, use context.db instead for access policy enforcement.
    export const posts = () => {
      return context.db.post.findMany()
    }
    

Btw, we should probably wrap zenstack generate into yarn rw zenstack generate. My understanding is that the CLI commands are not pluggable so it’ll require changes to the Redwood code base. Right?

Other notes

Developers can continue using the @requireAuth to guard the GQL queries and mutations, but it’s optional if the ZModel has fully expressed authorization requirements.

The Redwood RBAC can be used together with ZenStack. Developers can still use “roles” to guard frontend routes, and they can also use “role” to define policies in ZModel.

Demo project

You can find a fully implemented multi-tenant Todo app here.


Please let me know if you feel the proposal aligns with the Redwood team’s plan for the documentation. And I’d appreciate any suggestions for how to make the integration more frictionless.

I’ll be happy to make a PR if you think it’s the way to go! Thanks!

5 Likes

Hi @ymc9
Thanks for your detailed proposal! I think this all sounds really good, but let bring it up with the team.

  • Setup should be as easy as running npx @zenstackhq/redwood or yarn rw setup package @zenstackhq/redwood. I’m not sure if the latter is the preferred method.

Both would work, but we’d prefer yarn rw setup package @zenstackhq/redwood

Btw, we should probably wrap zenstack generate into yarn rw zenstack generate . My understanding is that the CLI commands are not pluggable so it’ll require changes to the Redwood code base. Right?

It’s not fully pluggable yet, but it’s something we’ve been talking about and wanted forever! Maybe we can work together to make it happen :slight_smile:

I’ve been keeping my eye on ZenStack for a while and like what you’re doing with it. But as I said, I do want to check with the team before I promise too much :smiley:

We have a team meeting scheduled on Tuesday – I’ll make sure to bring it up for discussion

1 Like

Thank you for your attention and reply @Tobbe ! Please take your time to discuss it with the team. I’m excited about making Redwood a more versatile framework!

Hi @ymc9 and thanks for thinking of having ZenStack support RedwoodJS.

We’ve had devs often ask about the abstract model and field data validations before – and inquire about using ZenStack.

I had a quick look at:

https://github.com/zenstackhq/sample-todo-redwood/blob/main/api/schema.zmodel

And I think it might be helpful if you and I got on a call to chat about the implementation and the RW integration (especially the Yoga plugin) – and how things like releases line up when Prisma/RW/ZS update.

Having a how to would be a nice addition for those who want to use ZenStack; let’s see how we can make this happen.

I’ll try to reach out to you directly to coordinate.

Hi @dthyresson ,

Sure, a call would be great! Talk to you soon!

Hi @ymc9! ZenStack looks super cool.

One thing I would find valuable as someone first hearing about ZenStack is a clearer description of what problem it solves from an auth perspective - from skimming the docs, my understanding is that while ZenStack doesn’t necessarily enable any new behavior in Redwood, it can make doing complex auth much more straightforward (and the automatic CRUD API is also very cool, although maybe less useful for Redwood given the tight GraphQL integration/impending shift to RSC).

So what I would find useful is before/after examples of without/with ZenStack to perform certain tasks, and also a clearer definition of new behavior that ZenStack can enable.

Hi @arimendelow ,

You’ve made a very precise summary! Yes, in the context of RedwoodJS, ZenStack’s participation consolidates authorization to the ORM layer (declaratively) and makes it much more flexible compared to the RBAC today. I feel it’s most needed for people who are building SaaS-like apps.

One of the design goals of ZenStack is to make it straightforward to adopt in existing Prisma projects - for which RedwoodJS projects are perfect examples :smile:. As you said, other high-level features like automatic CRUD APIs and frontend hooks are more for SPA apps and less relevant here.

Yes, we should definitely give good context about what problems it solves in the guide, and it’s a great idea to illustrate it in a before/after fashion.

1 Like