Cannot try-catch permission errors in webhooks

Issue Description: What’s happening?

I have a webhook that uses context.api.gqlRequest to connect to the API. My code looks something like this:

import GetUsersQuery from '../queries/GetUsersQuery'

export default async (event, { api }) => {
  try {
    const response = await api.gqlRequest(GetUsersQuery)

    return {
      statusCode: 200,
      body: response.usersList.items
    }

  } catch (error) {

    return {
      statusCode: 404,
      body: {
        message: 'Not found'
      }
    }
  }
}

If I invoke the webhook locally when logged in, this works. When I’m logged out, I get a permission error, as I should and want.

But, the response from the webhook is an Internal server error (caused by the permission error?). Based on my logging, I think the catch block does execute, but the actual webhook response is not what I return, but rather the Internal server error. So, instead of my own custom { message: "Not found" }, I get this:

My production log has something like this:

ERROR System Error: User's errors: [{"message":"You don't have permission to perform this operation","locations":[{"line":2,"column":3}],"path":["user"],"code":"NotAuthorizedError","details":{"permissions":"You don't have permission to perform this operation"}}]

So I know the permission error does happen.

Reproduce the Issue: What steps can someone take to replicate the problem?

  1. Create a webhook that uses ctx.api.gqlRequest to make a query to e.g. the user endpoint
  2. Wrap the gqlRequest() inside a try-catch block
  3. Fire the webhook without being logged in

Expected Behavior: What did you expect to happen?

The webhook response should be whatever I define in my webhook

Actual Behavior: What actually happened?

Webhook responds with Internal server error

@jerryjappinen Hello, looking into that. Thanks for the feedback!

1 Like

Hello, Jerry! @jerryjappinen

  1. Webhooks are public functions by default and are not permissioned using 8base’s native authorization system (they don’t know if you’re logged in/out).
  2. So the error is not related to permissions, as you thought, but to the body block structure specifically. In the return { } block body is always a String, not an Object.
    So if you want to make it work, you can wrap body: { //any code } to the JSON.stringify() function =>
    body: JSON.stringify ( { //any code } )
    and then you will see your custom error message instead of “Internal server error” while deploying your webhook (you can check it here: Workspace → Functions → Webhook → Settings item → Endpoint).
  3. If you are interested in implementing secure webhooks, you can get more info about permissioning webhooks in our documentation (Webhooks - 8base Documentation) and the checkPermissions flag while using gqlRequest (Environment - 8base Documentation).
2 Likes

Thanks a lot. Indeed the issue was just because of the wrong type for body. If I stringify the JSON manually, it works as intended. Maybe that’s something the platform could do for the user here, seems like this would be a common developer mistake. Not a big deal though, I can just write a wrapper for my handlers to do this kind of stuff.

Regarding the permissioning, I did read that part about webhooks in the documentation and I’m a bit confused why it says it’s not permissioned using the default system.

When I invoke webhooks and use gqlRequest there, it does seem to know when I’m logged in (when using the CLI or SDK) and the permissioning seems to work fine. Of course when I’m not logged in or send a separate GET request, whatever the webhook does is limited to public permissions.

I use this piece of code to check whether the user is logged in:

const getUser = async (ctx) => {
  try {
    const response = await ctx.api.gqlRequest(`query { user { id, email } }`)
    return response.user
  } catch (error) {
    console.log('Not logged in')
  }

  return null
}

In a webhook like this:

const user = await getUser(ctx)

if (user) {
  return {
    statusCode: 200,
    body: JSON.stringify(user)
  }
}

return {
  statusCode: 404,
  body: 'Not found'
}

Is there something wrong with this approach? To me this makes sense and it seems to work just fine as well.

2 Likes

Hello again! Your approach is allright!
Yeap, you’re right and the doc section @ekaterina.efimova linked relates to this case you mentioned:

I guess wording about native authorization system in docs is not the best though.

1 Like