Skip to content

Commit 0449518

Browse files
authored
doc: migrate more auth guide to v3 (#511)
* doc: migrate more auth guide to v3 * update
1 parent 56847d8 commit 0449518

File tree

3 files changed

+138
-3
lines changed

3 files changed

+138
-3
lines changed

versioned_docs/version-3.x/recipe/auth-integration/better-auth.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
sidebar_position: 1
3+
sidebar_label: Better-Auth
34
---
45

56
import PackageInstall from '../../_components/PackageInstall';
@@ -25,7 +26,9 @@ Add the adapter to your better-auth configuration:
2526

2627
```ts
2728
import { zenstackAdapter } from '@zenstackhq/better-auth';
28-
import { db } from './db'; // your ZenStack ORM client
29+
30+
// ZenStack ORM client
31+
import { db } from './db';
2932

3033
const auth = new BetterAuth({
3134
database: zenstackAdapter(db, {
@@ -45,6 +48,8 @@ Then, run the "generate" command to generate the schema:
4548

4649
<PackageExec command="@better-auth/cli generate" />
4750

51+
You should see models like `User`, `Session`, and `Account` added to your `schema.zmodel` file if they don't already exist.
52+
4853
Alternatively, you can refer to [better-auth schema documentation](https://www.better-auth.com/docs/concepts/database#core-schema) to manually add the necessary models.
4954

5055
After the schema is configured, you can then use the regular ZenStack database schema migration workflow to push the schema to your database.
@@ -77,7 +82,10 @@ const userId = session.userId;
7782
Then you can pass it to `ZenStackClient`'s `$setAuth()` method to get a user-bound ORM client.
7883

7984
```tsx
80-
const userDb = db.$setAuth({ userId });
85+
// ZenStack ORM client with access policy plugin installed
86+
import { authDb } from './db';
87+
88+
const userDb = authDb.$setAuth({ userId });
8189
```
8290

8391
### Organization plugin support
@@ -109,7 +117,7 @@ After enabling the Organization plugin and running the CLI to generate the addit
109117
Then you can use the full `userContext` object to get a user-bound client.
110118

111119
```tsx
112-
const userDb = db.$setAuth(userContext);
120+
const userDb = authDb.$setAuth(userContext);
113121
```
114122

115123
The user context will be accessible in ZModel policy rules via the special `auth()` function. To get it to work, let's add a type in ZModel to define the shape of `auth()`:
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
description: Integrating with Clerk.
3+
sidebar_position: 2
4+
sidebar_label: Clerk
5+
---
6+
7+
# Clerk Integration
8+
9+
[Clerk](https://clerk.com/) is a comprehensive authentication and user management platform, providing both APIs and pre-made UI components. This guide will show you how to integrate Clerk with ZenStack's [access control system](../../orm/access-control/).
10+
11+
## Set up Clerk
12+
13+
First, follow Clerk's [quick start guides](https://clerk.com/docs/quickstarts/overview) to set up your project if you haven't already.
14+
15+
## Adjust your ZModel
16+
17+
Since Clerk manages both user authentication and storage, you don't need to store users in your database anymore. However, you still need to provide a type that the `auth()` function can resolve to. Instead of using a regular model, we can declare a `type` instead:
18+
19+
You can include any field you want in the `User` type, as long as you provide the same set of fields in the context object when calling `ZenStackClient`'s `$setAuth()` method.
20+
21+
The following code shows an example blog post schema:
22+
23+
```zmodel
24+
type User {
25+
id String @id
26+
27+
@@auth
28+
}
29+
30+
model Post {
31+
id String @id @default(cuid())
32+
createdAt DateTime @default(now())
33+
updatedAt DateTime @updatedAt
34+
title String
35+
published Boolean @default(false)
36+
authorId String // stores Clerk's user ID
37+
38+
// author has full access
39+
@@allow('all', auth() != null && auth().id == authorId)
40+
41+
// logged-in users can view published posts
42+
@@allow('read', auth() != null && published)
43+
}
44+
```
45+
46+
If you choose to [synchronize user data to your database](https://clerk.com/docs/users/sync-data-to-your-backend), you can define `User` as a regular `model` since it's then backed by a database table.
47+
48+
## Create a user-bound ORM client
49+
50+
When using ZenStack's built-in access control, you often use the `auth()` function in policy rules to reference the current user's identity. The evaluation of `auth()` at runtime requires you to call the `$setAuth()` method and pass in the validated user identity from Clerk.
51+
52+
Please refer to clerk's documentation on how to fetch the current user on the server side for your specific framework. The following code shows an example for Next.js (app router):
53+
54+
```ts
55+
import { auth } from "@clerk/nextjs/server";
56+
57+
// ZenStack ORM client with access policy plugin installed
58+
import { authDb } from './db';
59+
60+
async function getUserDb() {
61+
// get the validated user identity from Clerk
62+
const authObject = await auth();
63+
64+
// create a user-bound ORM client
65+
return authDb.$setAuth(
66+
authObject.userId ? { id: authObject.userId } : undefined);
67+
}
68+
```
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
description: Integrating with a custom authentication system.
3+
sidebar_position: 100
4+
sidebar_label: Custom Authentication
5+
---
6+
7+
# Custom Authentication Integration
8+
9+
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.
10+
11+
## Determine what's needed for access control
12+
13+
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.
14+
15+
The `auth()` call needs to be resolved to a "model" or "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.
16+
17+
```zmodel
18+
model User {
19+
id String @id
20+
role String
21+
permissions String[]
22+
...
23+
}
24+
```
25+
26+
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.
27+
28+
```zmodel
29+
type Auth {
30+
id String @id
31+
role String
32+
permissions String[]
33+
34+
@@auth
35+
}
36+
```
37+
38+
Just remember that any thing that you access from `auth().` must be resolved.
39+
40+
## Fetch the current user along with the additional information
41+
42+
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:
43+
44+
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.
45+
2. If you use a dedicated authentication service, you can call it to get the current user's information.
46+
47+
You must ensure that whatever approach you use, the user information you get can be trusted and free of tampering.
48+
49+
## Create a user-bound ORM client
50+
51+
Finally, you can call the `$setAuth()` method on `ZenStackClient` to create an ORM instance that's bound to the current user.
52+
53+
```ts
54+
// ZenStack ORM client with access policy plugin installed
55+
import { authDb } from './db';
56+
57+
const user = await getCurrentUser(); // your implementation
58+
const db = authDb.$setAuth(user);
59+
```

0 commit comments

Comments
 (0)