From 6d46b546c3b5e459d91894b7991099abe57a24f4 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Sun, 24 Nov 2024 10:33:40 -0800 Subject: [PATCH] doc: guide for custom auth --- docs/guides/authentication/custom.md | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 docs/guides/authentication/custom.md diff --git a/docs/guides/authentication/custom.md b/docs/guides/authentication/custom.md new file mode 100644 index 00000000..848980ff --- /dev/null +++ b/docs/guides/authentication/custom.md @@ -0,0 +1,55 @@ +--- +description: Integrating with a custom authentication system. +sidebar_position: 100 +sidebar_label: Custom Authentication +--- + +# Integrating With a Custom Authentication System + +You may be using an authentication provider that's not mentioned in the guides. Or you may have a custom-implemented one. Integrating ZenStack with any authentication system is pretty straightforward. This guide will provide the general steps to follow. + +## Determining what's needed for access control + +The bridge that connects authentication and authorization is the `auth()` function that represents the authenticated current user. Based on your requirements, you should determine what fields are needed from it. The `auth()` object must at least contain an id field that uniquely identifies the current user. If you do RBAC, you'll very likely need an `auth().role` field available. Or even an `auth().permissions` field for fine-grained control. + +The `auth()` call needs to be resolved to a "type" in ZModel. If you store user data in your database, you may already have a "User" model that carries all the fields you need to access. + +```zmodel +model User { + id String @id + role String + permissions String[] + ... +} +``` + +ZenStack picks up the model named "User" automatically to resolve `auth()` unless another model or type is specifically appointed (by using the `@@auth` attribute). For example, if you're not storing user data locally, you can define a "type" to resolve `auth()`. This way, you can provide typing without being backed by a database table. + +```zmodel +type Auth { + id String @id + role String + permissions String[] + @@auth +} +``` + +Just remember that any thing that you access from `auth().` must be resolved. + +## Fetching the current user along with the additional information + +At runtime in your backend code, you need to provide a value for the `auth()` call to ZenStack, so it can use it to evaluate access policies. How this is done is solely dependent on your authentication mechanism. Here are some examples: + +1. If you use JWT tokens, you can issue tokens with user id and other fields embedded, then validate and extract them from the request. +2. If you use a dedicated authentication service, you can call it to get the current user's information. + +You must ensure that whatever approach you use, the user information you get can be trusted and free of tampering. + +## Creating an enhanced PrismaClient + +Finally, you can call the `enhance` API to create an enhanced PrismaClient with the user information and enjoy automatically enforced access policies. + +```ts +const user = await getCurrentUser(); // your implementation +const db = enhance(prisma, { user }); +```