traches
traches3mo ago

Can I extend the procedure builder?

Authorization in my app is somewhat complicated; I have various resources, and I often have to validate the current user's relation to that resource before allowing them access. Currently I'm doing this:
myProcedure: protectedProcedure
.input(myQuerySchema)
.query(authorize(myQuery, authorizationConfig)),
myProcedure: protectedProcedure
.input(myQuerySchema)
.query(authorize(myQuery, authorizationConfig)),
- authorize is a custom function that wraps a mutation in an authorization check, which is defined by the config. - The types of the query and config are related, because the authorization check uses the procedure's input. What I really want to do is this:
myProcedure: protectedProcedure
.input(myQuerySchema)
.authorize(authorizationConfig)
.query(myQuery)
myProcedure: protectedProcedure
.input(myQuerySchema)
.authorize(authorizationConfig)
.query(myQuery)
Any advice? I've looked at using .meta(), but middleware doesn't know about the input types does it?
Solution:
Yeah it's possible to do what you want:
No description
Jump to solution
9 Replies
BeBoRE
BeBoRE3mo ago
You can also make protectedProcedure a function and have it return a procedure so it would look like this:
{
myProcedure: protectedProcedure(authorizationConfig)
.input(myQuerySchema)
.query(myQuery)
}
{
myProcedure: protectedProcedure(authorizationConfig)
.input(myQuerySchema)
.query(myQuery)
}
Nick
Nick3mo ago
This is all pretty normal stuff. The ingredients are indeed a middleware, context, and meta Middlewares can access inputs but there isn't much reason to since auth details should be in headers/cookies. Importantly they can gate access to procedures by throwing unauthorised errors (etc) createContext()/Context is the best way to extract some bits from headers in a type-safe way, and middlewares can access this state type safely Meta is great to allow procedures to be configured, like assigning an entitlement key to a procedure which the middleware can then check against the user, again in a type safe way
traches
trachesOP3mo ago
Thanks! I don't see how I can do authorization without input here. Like, say users have posts and they can edit their own posts but not other users' posts. We also have other records they can work with, but determining ownership is more complicated than checking 'userId' on the affected record, and is unique for a few different cases. (For an example let's say users can be members of groups and can only post to groups they're a member of). How can I make the types work? when I try to use the input of protectedProcedure the input is typeof unsetMarker. Here's the type signature for my authorize function:
No description
traches
trachesOP3mo ago
No description
Nick
Nick3mo ago
Got it
Solution
Nick
Nick3mo ago
Yeah it's possible to do what you want:
No description
Nick
Nick3mo ago
you can define a base procedure with authorization for "thing" baked in and then use that for various procedures with extra input data. Inputs and Ctx can both be extended and merged over multiple layers
traches
trachesOP3mo ago
Ahhh cool, I didn't think of defining middleware after defining the input. Awesome, thanks a ton!
y_nk
y_nk3w ago
@traches this achieves what you want
const p = t.procedure

export const protectedProcedure = {
...p,

authorize(this: typeof p, authorizationConfig: any) {
return this.use(
// this is a middleware
async function authorizeMiddleware({ ctx, next }) {

return next({ ...ctx })
}
)
}
}
const p = t.procedure

export const protectedProcedure = {
...p,

authorize(this: typeof p, authorizationConfig: any) {
return this.use(
// this is a middleware
async function authorizeMiddleware({ ctx, next }) {

return next({ ...ctx })
}
)
}
}

Did you find this page helpful?