{"record":{"id":"16cd47b0f6a64457","repo":"calcom/cal.diy","slug":"isuserwebhookguard-user-with-id-user-id-is","errorCode":null,"errorMessage":"IsUserWebhookGuard - user with id=(${user.id}) is not the owner of webhook with id=(${webhookId})","messagePattern":"IsUserWebhookGuard - user with id=\\((.+?)\\) is not the owner of webhook with id=\\((.+?)\\)","errorType":"http","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"apps/api/v2/src/modules/webhooks/guards/is-user-webhook-guard.ts","lineNumber":28,"sourceCode":"  constructor(private readonly webhooksService: WebhooksService) {}\n\n  async canActivate(context: ExecutionContext): Promise<boolean> {\n    const request = context.switchToHttp().getRequest<Request & { webhook: Webhook }>();\n    const user = request.user as ApiAuthGuardUser;\n    const webhookId = request.params.webhookId;\n\n    if (!user) {\n      throw new ForbiddenException(\"IsUserWebhookGuard - No user associated with the request.\");\n    }\n\n    if (!webhookId) {\n      throw new ForbiddenException(\"IsUserWebhookGuard - No webhook id found in request params.\");\n    }\n\n    const webhook = await this.webhooksService.getWebhookById(webhookId);\n\n    if (webhook.userId !== user.id && !user.isSystemAdmin) {\n      throw new ForbiddenException(\n        `IsUserWebhookGuard - user with id=(${user.id}) is not the owner of webhook with id=(${webhookId})`\n      );\n    }\n\n    request.webhook = webhook;\n    return true;\n  }\n}\n","sourceCodeStart":10,"sourceCodeEnd":37,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/webhooks/guards/is-user-webhook-guard.ts#L10-L37","documentation":"IsUserWebhookGuard fetched the webhook by id and found that webhook.userId does not equal the authenticated user's id, and the user is not a system admin. The guard enforces per-user ownership of webhooks; only the owner or a system admin may proceed. It throws ForbiddenException (HTTP 403).","triggerScenarios":"An authenticated user calls a webhook endpoint (GET/PATCH/DELETE) for a webhook owned by a different user, while not having isSystemAdmin=true. The guard compares webhook.userId === user.id after loading the webhook.","commonSituations":"Cross-tenant access bug in the client (wrong user session reused); sharing a webhook id between team members who are not admins; a token from user A used to operate on user B's webhook; stale cached id.","solutions":["Authenticate as the user who owns the webhook (the user whose id equals webhook.userId).","If legitimate admin access is required, perform the call with a system-admin account.","Verify the webhookId belongs to the current user via a list-my-webhooks call before operating on it.","Audit the client's session/token handling to ensure the correct user context is sent."],"exampleFix":"// before\nawait api.delete(`/webhooks/${webhookId}`); // wrong session\n// after\nconst mine = await api.get('/webhooks');\nif (!mine.find(w => w.id === webhookId)) throw new ForbiddenError('not owner');\nawait api.delete(`/webhooks/${webhookId}`);","handlingStrategy":"validation","validationCode":"async function ensureOwnsWebhook(api, user, webhookId) {\n  const mine = await api.listMyWebhooks();\n  if (!mine.find(w => w.id === webhookId) && !user.isSystemAdmin) {\n    throw new ForbiddenError(`user ${user.id} does not own ${webhookId}`);\n  }\n}","typeGuard":"const isWebhookOwner = (webhook: { userId: number }, user: { id: number; isSystemAdmin?: boolean }): boolean =>\n  webhook.userId === user.id || user.isSystemAdmin === true;","tryCatchPattern":"try { await api.delete(`/webhooks/${id}`); }\ncatch (e) {\n  if (e.status === 403 && /not the owner/.test(e.message)) { /* use owner session or admin */ }\n  else throw e;\n}","preventionTips":["Operate on webhooks only from the session of the user who owns them.","Maintain a mapping of webhookId -> ownerId in the client to detect mismatches early.","Reserve system-admin usage for explicit admin tooling."],"tags":["nestjs","guard","authorization","ownership","webhooks"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}