Skip to content

Commit 88c6a57

Browse files
jiashengguocoderabbitai[bot]ymc9
authored
doc: add Lucia Auth doc (#323)
* doc: add Lucia Auth doc * Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update lucia.md * Update docs/guides/authentication/lucia.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update docs/guides/authentication/lucia.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * use consistent indentation * fix missing end enclosure * consistent tab * tab --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: ymc9 <[email protected]>
1 parent f3333a7 commit 88c6a57

File tree

1 file changed

+148
-3
lines changed

1 file changed

+148
-3
lines changed
Lines changed: 148 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,154 @@
11
---
22
description: Integrating with Lucia.
33
sidebar_position: 5
4-
sidebar_label: 🚧 Lucia
4+
sidebar_label: Lucia
55
---
66

7-
# 🚧 Integrating With Lucia
7+
# Integrating With Lucia
88

9-
Coming soon.
9+
[Lucia](https://lucia-auth.com/) is an auth library for your server that abstracts away the complexity of handling sessions. It is a good choice if you need to add custom logic to the auth flow or use email and password authentication.
10+
11+
To get access policies to work, ZenStack needs to be connected to the authentication system to get the user's identity. This guide introduces tasks required for integrating ZenStack with Lucia Auth. You can find a complete example [here](https://github.com/zenstackhq/sample-luciaAuth-nextjs).
12+
13+
## Data model requirement
14+
15+
Lucia needs to store your users and sessions in the database. So, your ZModel definition needs to include these two models. Here is the sample schema:
16+
17+
```zmodel title='/schema.zmodel'
18+
model User {
19+
id String @id @default(uuid())
20+
userName String @unique
21+
password String @omit
22+
sessions Session[]
23+
24+
@@allow('read', true)
25+
@@allow('all', auth() == this)
26+
}
27+
28+
model Session {
29+
id String @id
30+
userId String
31+
expiresAt DateTime
32+
33+
user User @relation(references: [id], fields: [userId], onDelete: Cascade)
34+
}
35+
```
36+
37+
The data field names and types in the `session` model must exactly match the ones in the above. While you can change the model names, the relation name in the session model (`Session.user`) must be the camel-case version of the user model name. For example, if the user model was named `AuthUser`, the relation must be named `Session.authUser`.
38+
39+
## Prisma adapter
40+
41+
Lucia connects to your database via an adapter, which provides a set of basic, standardized querying methods that Lucia can use. Since ZenStack is based on Prisma, you can
42+
43+
Lucia has its own Prisma adapter `@lucia-auth/adapter-prisma`, which provides a set of basic, standardized querying methods. Since ZenStack is based on Prisma, you can use PrismaAdapter for the job:
44+
45+
```tsx title='/lib/auth.ts'
46+
import { PrismaAdapter } from "@lucia-auth/adapter-prisma";
47+
import { PrismaClient } from "@prisma/client";
48+
49+
const client = new PrismaClient();
50+
51+
const adapter = new PrismaAdapter(client.session, client.user);
52+
```
53+
54+
## Validate requests
55+
56+
Create `validateRequest()`. This will check for the session cookie, validate it, and set a new cookie if necessary. Make sure to catch errors when setting cookies and wrap the function with `cache()` to prevent unnecessary database calls.  To learn more, see Lucia’s  [Validating requests](https://lucia-auth.com/guides/validate-session-cookies/nextjs-app) page.
57+
58+
```tsx title='/lib/auth.ts'
59+
export const validateRequest = cache(
60+
async (): Promise<{ user: User; session: Session } | { user: null; session: null }> => {
61+
const sessionId = cookies().get(lucia.sessionCookieName)?.value ?? null;
62+
if (!sessionId) {
63+
return {
64+
user: null,
65+
session: null
66+
};
67+
}
68+
69+
const result = await lucia.validateSession(sessionId);
70+
// next.js throws when you attempt to set cookie when rendering page
71+
try {
72+
if (result.session && result.session.fresh) {
73+
const sessionCookie = lucia.createSessionCookie(result.session.id);
74+
cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
75+
}
76+
if (!result.session) {
77+
const sessionCookie = lucia.createBlankSessionCookie();
78+
cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
79+
}
80+
} catch {}
81+
return result;
82+
}
83+
);
84+
```
85+
86+
This function can be used in server components and form actions to get the current session and user.
87+
88+
```tsx title='/app/page.tsx'
89+
const { user } = await validateRequest();
90+
91+
if (!user) {
92+
return redirect("/login");
93+
}
94+
```
95+
96+
## Create an enhanced Prisma client
97+
98+
You can create an enhanced Prisma client which automatically validates access policies, field validation rules etc., during CRUD operations. For more details, please refer to [ZModel Language](https://zenstack.dev/docs/reference/zmodel-language) reference.
99+
100+
To create such a client, simply call the `validateRequest` function to get the current user id and then call the `enhance` API to pass the user identity.
101+
102+
```tsx title='/lib/db.ts'
103+
import { PrismaClient } from '@prisma/client';
104+
import { validateRequest } from './auth';
105+
import { enhance } from '@zenstackhq/runtime';
106+
107+
export const prisma = new PrismaClient();
108+
109+
export async function getEnhancedPrisma(): Promise<PrismaClient> {
110+
const { user } = await validateRequest();
111+
// create a wrapper of Prisma client that enforces access policy,
112+
// data validation, and @password, @omit behaviors
113+
return enhance(prisma, { user: {id: user?.id!}});
114+
}
115+
```
116+
117+
## Expose more user data
118+
119+
By default, Lucia will not expose any database columns to the `User` type. To add a `userName` field to it, use the `getUserAttributes()` option.
120+
121+
```tsx title='/lib/auth.ts'
122+
declare module "lucia" {
123+
interface Register {
124+
Lucia: typeof lucia;
125+
DatabaseUserAttributes: DatabaseUserAttributes
126+
}
127+
}
128+
129+
interface DatabaseUserAttributes {
130+
userName: string;
131+
}
132+
133+
export const lucia = new Lucia(adapter, {
134+
getUserAttributes: (attributes) => {
135+
return {
136+
userName: attributes.userName
137+
};
138+
}
139+
});
140+
```
141+
142+
Then, you can directly access it from the result returned by `validateRequest` function:
143+
144+
```tsx title='/app/page.tsx'
145+
export default async function Page() {
146+
const { user } = await validateRequest();
147+
return (
148+
<>
149+
<h1>Hi, {user.userName}!</h1>
150+
<p>Your user ID is {user.id}.</p>
151+
</>
152+
);
153+
}
154+
```

0 commit comments

Comments
 (0)