Skip to content

Commit 5cd5114

Browse files
authored
First pass at auth0 integration docs (#403)
* First pass at auth0 integration docs * Update auth0.md Address PR comments * Update auth0.md Added more detail to the section on working alongside a User model * Update auth0.md Wording improvement * Update auth0.md A few more comments * Update auth0.md Improved gramma
1 parent 9a8c4b5 commit 5cd5114

File tree

1 file changed

+164
-3
lines changed

1 file changed

+164
-3
lines changed

docs/guides/authentication/auth0.md

Lines changed: 164 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,170 @@
11
---
22
description: Integrating with Auth0.
33
sidebar_position: 6
4-
sidebar_label: 🚧 Auth0
4+
sidebar_label: Auth0
55
---
66

7-
# 🚧 Integrating With Auth0
7+
# Integrating With Auth0
8+
9+
This guide provides simple examples of using Auth0 authentication with ZenStack. While Auth0 offers various authentication methods, this guide assumes you understand Auth0's authentication basics and can obtain a user object after authentication.
10+
11+
## The basic premise of applying a custom session object to ZenStack.
12+
13+
This section explains how to apply a custom session object to ZenStack by creating a user object and providing it to the enhance function when creating the Prisma client.
14+
15+
Create a user object and provide it to the enhance function when creating the Prisma client.
16+
17+
```ts
18+
export const getPrisma = async (req) => {
19+
const user = await getAuthenticatedAuth0User(req);
20+
return enhance(user);
21+
};
22+
```
23+
24+
You can provide a type in the ZModel to express what the contents of the user object is.
25+
26+
```prisma
27+
type Auth {
28+
id String @id
29+
specialKey String
30+
@@auth
31+
}
32+
```
33+
34+
## Adding Auth0 authentication
35+
36+
You can authenticate an Auth0 user and extract information from the authentication and apply it to the ZenStack session.
37+
38+
Here's an example using a JWT:
39+
40+
```ts
41+
export const getPrismaJWT = async (req) => {
42+
try {
43+
const jwks = jose.createRemoteJWKSet(new URL(process.env.AUTH0_JWKS_URI));
44+
const token = toString(req.headers.get('authorization')).replace('Bearer ', '');
45+
const res = await jose.jwtVerify(token, jwks, {
46+
issuer: `${process.env.AUTH0_ISSUER_BASE_URL}/`,
47+
audience: process.env.AUTH0_AUDIENCE,
48+
algorithms: ['RS256'],
49+
});
50+
51+
const userId = res.payload.sub;
52+
const user = {
53+
id: userId,
54+
specialKey: res.payload.metadata.specialKey
55+
};
56+
57+
return enhance(prisma, {user});
58+
catch (err) {
59+
// unauthenticated error
60+
}
61+
};
62+
```
63+
64+
This would populate your `auth()` in the Zmodel with the object you've just created from auth0; enabling checks like:
65+
66+
```prisma
67+
@@allow('read, update, create', auth().id == this.id)
68+
```
69+
70+
or
71+
72+
```prisma
73+
@@allow('read, update, create', auth().specialKey == 'SUPERMAN')
74+
```
75+
76+
You can add what you need to this variable and set the types for it as referred to in the *Enhancing the prisma client* section.
77+
78+
79+
## Working along side a user model
80+
81+
You may want to keep a record of User's in your own database.
82+
83+
You can create your application in such a way that a lack of the user existing in the managed database triggers a process to create one, such as a user onboarding flow.
84+
85+
```ts
86+
const currentUser = async (req) => {
87+
const session = await getSession(req); // get your auth0 auth session
88+
89+
if (!session?.user.sub) {
90+
throw new Error('UNAUTHENTICATED'); // Throw an error if the user isn't authenticated
91+
}
92+
93+
const dbUser = await prisma.user.findUnique({ // Find the user in the db
94+
where: { id: session.user.sub },
95+
});
96+
97+
return {
98+
id: session.user.sub,
99+
dbUserExists: !isNull(dbUser), // If the user doesn't exist in the database, this variable can be set in the session
100+
};
101+
};
102+
103+
// Create the client using the currentUser
104+
export const getPrisma = async (req) => {
105+
const user = await currentUser(req);
106+
return enhance(user);
107+
};
108+
```
109+
110+
You can use the result of this token to redirect a user to an onboarding flow, such as a signup form:
111+
112+
```ts
113+
app.get('/', async (req, res) => {
114+
const user = await currentUser(req);
115+
if (!user.dbUserExists) {
116+
res.redirect('/onboarding');
117+
}
118+
res.send('hello user');
119+
});
120+
```
121+
122+
and create a user record using the ID from the Auth0 session, here's a small example using the Auth0 React SDK:
123+
124+
```ts
125+
import React from "react";
126+
import { useAuth0 } from "@auth0/auth0-react";
127+
128+
const Profile = () => {
129+
const { user, isAuthenticated, isLoading } = useAuth0();
130+
const { trigger, isMutating } = useCreateUser();
131+
132+
const createUser = useCallback(async (event: FormEvent<HTMLFormElement>) => {
133+
const formData = new FormData(event.currentTarget);
134+
const name = formData.get('name');
135+
136+
await trigger({
137+
data: {
138+
id: user.sub,
139+
name: name,
140+
},
141+
});
142+
}, [trigger, user])
143+
144+
return <UserForm onSubmit={createUser}/>
145+
};
146+
```
147+
148+
149+
When the client is created, the database is queried using the contents of the Auth0 token.
150+
151+
In this case, the Auth type is what provide authentication, not the User model, for example:
152+
153+
```prisma
154+
// Specify the auth type
155+
type Auth {
156+
id String @id
157+
@@auth // And decorate it with @@auth to tell ZenStack to use this as the session object
158+
}
159+
160+
// add your user model as a regular model
161+
model User {
162+
id String @id
163+
name String?
164+
email String?
165+
166+
// You can now use the Auth object, populated by Auth0, to write policies
167+
@@allow('create, read, update, delete', auth().id == this.id)
168+
}
169+
```
8170
9-
Coming soon.

0 commit comments

Comments
 (0)