Application design hurdles with dependency injection #1264
Replies: 2 comments 3 replies
-
I'm also confused about authentication flow with the user creation process. Zenstack provides automatic password hashing but you need a user in order to enhance prisma so that feels like a 🐓 & 🥚 problem. If I try to pass in a mock user with just an id, zenstack complains that it doesn't have the attributes it needs on the auth() for the rules. |
Beta Was this translation helpful? Give feedback.
-
Here's the preliminary pattern I used for anyone interested (correction: it's function overloading not type refinement): EDIT: my previous snippet TS was a little broken and not succinct enough, updated export async function resolveAuthedResources(): Promise<{
zPrisma: PrismaClient;
}>;
export async function resolveAuthedResources(
request: Request,
): Promise<{ zPrisma: PrismaClient; user: BasicUser }>;
export async function resolveAuthedResources(request?: any): Promise<any> {
if (request) {
const authUser = await authenticator.isAuthenticated(request, {
failureRedirect: ROUTES.LOGIN,
});
if (!authUser) {
throw redirect(ROUTES.LOGIN);
}
const user = await getUserById(authUser.id);
if (!user) {
throw redirect(ROUTES.LOGIN);
}
// can't use the cookie value which doesn't update on admin changes
const zPrisma = await getEnhancedPrismaClient(user);
return { user: authUser, zPrisma };
}
return { zPrisma: getEnhancedPrismaClient() };
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm really excited about enforcing access policies on the level of the ORM. I think using zenstack is going to be a big win for my project and I'm really thankful that you provided some tutorials about remix integration. That being said, I found myself thrashing around a lot on how to redesign my API interfaces after introducing zenstack to my prisma/remix project.
Since all enhancement requires a user, at first I thought I would just put a
currentUserId
into every prisma helper function and pass{ user: {id: userId}}
as the context forenhance
as I saw in examples. However, I discovered that this is not suitable for my app because I need the whole user record to enforce access. I thought that maybe zenstack would be fetching the record behind the scenes since it knows the user table but that is not the case but... 🤦♂️ it wouldn't know which properties to include.For now, I am using
remix-auth
with a form strategy and so I need access to aRequest
instance in order to first check that the session is valid before any prisma requests. From the cookie, I get the user id, and then I can look up the user record with all the data needed for access control. I guessed that meant pushing the auth checks way down into every api helper function. That felt a tad bit inelegant but safer.Considering this, I replaced the
currentUserId
I was passing to all the prisma helper functions with theRequest
instance from loaders/actions. Then I built a functionresolveAuthedResources
to accept the request as a parameter which resolves both the enhanced client and the user record for convenience and used it in all my prisma helper functions.However, I encountered a couple issues with this approach:
auth()
but if I make the resolved user possibly undefined then I have to put existence checks everywhere that usesresolveAuthedResources
which seems yucky.Now I am resigning myself to the approach where I pass a
PrismaClient
into all my helper functions. This way I can use the db functions in my seed script or for testing and I won't have to mock out myauthenticator.isAuthenticated
calls.I'm not entirely satisfied with this approach because it doesn't protect the developer from accidentally passing in a non-enhanced prisma client and thus inadvertently circumventing access control. Hopefully the parameter name
zPrisma
at least gives a hint.I am curious to know what the community thinks about all these approaches. Do you have a better idea about how to manage an enhanced client into a SSR-based frameworks like Remix.js and NextJs? If, so I would be greatful for your feedback!
Beta Was this translation helpful? Give feedback.
All reactions