{"record":{"id":"f611c4c25b897e8f","repo":"calcom/cal.diy","slug":"permissionsguard-oauth-client-with-id-oauthcli","errorCode":null,"errorMessage":"PermissionsGuard - oAuth client with id=${oAuthClient.id} does not have the required permissions=${requiredPermissions}. Go to platform dashboard settings and add the required permissions to the oAuth client.","messagePattern":"PermissionsGuard - oAuth client with id=(.+?) does not have the required permissions=(.+?)\\. Go to platform dashboard settings and add the required permissions to the oAuth client\\.","errorType":"http","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"apps/api/v2/src/modules/auth/guards/permissions/permissions.guard.ts","lineNumber":60,"sourceCode":"    // only check permissions for accessTokens attached to platform oAuth Client or platform oAuth credentials, not for next token or api key or third party oauth client\n    if (nextAuthToken || apiKey || isThirdPartyBearerToken) {\n      return true;\n    }\n\n    if (!bearerToken && !oAuthClientId) {\n      throw new ForbiddenException(\n        \"PermissionsGuard - no authentication provided. Provide either authorization bearer token containing managed user access token or oAuth client id in 'x-cal-client-id' header.\"\n      );\n    }\n\n    const oAuthClient = bearerToken\n      ? await this.getOAuthClientByAccessToken(bearerToken)\n      : await this.getOAuthClientById(oAuthClientId);\n\n    const hasRequiredPermissions = hasPermissions(oAuthClient.permissions, [...requiredPermissions]);\n\n    if (!hasRequiredPermissions) {\n      throw new ForbiddenException(\n        `PermissionsGuard - oAuth client with id=${\n          oAuthClient.id\n        } does not have the required permissions=${requiredPermissions\n          .map((permission) => this.oAuthClientsOutputService.transformOAuthClientPermission(permission))\n          .join(\n            \", \"\n          )}. Go to platform dashboard settings and add the required permissions to the oAuth client.`\n      );\n    }\n\n    return true;\n  }\n\n  async getOAuthClientByAccessToken(\n    accessToken: string\n  ): Promise<Pick<PlatformOAuthClient, \"id\" | \"permissions\">> {\n    const oAuthClient = await this.tokensRepository.getAccessTokenClient(accessToken);\n    if (!oAuthClient) {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/auth/guards/permissions/permissions.guard.ts#L42-L78","documentation":"ForbiddenException from PermissionsGuard when the resolved OAuth client lacks one or more of the route's required permissions. The message lists the oAuthClient.id and the human-readable required permission names (transformed by oAuthClientsOutputService.transformOAuthClientPermission) and points the user to the platform dashboard to add them. Permissions are checked via hasPermissions(oAuthClient.permissions, requiredPermissions).","triggerScenarios":"Calling a PermissionsGuard-protected endpoint whose @Permissions(...) metadata includes a permission not granted to the OAuth client identified by the Bearer access token or the x-cal-client-id. Example: an endpoint requiring READ_BOOKINGS while the client only has READ_USERS.","commonSituations":"New endpoint shipped behind a permission the existing OAuth client doesn't have; client created with a minimal scope and now calling a broader endpoint; permission name renamed; dashboard permissions UI out of sync with code constants.","solutions":["Open the Cal.com platform dashboard → OAuth client settings and add every permission listed in the error message to that client.","If the permission set looks correct, confirm you are authenticating as the right OAuth client (check the oAuthClient.id in the message).","As a platform maintainer: verify the @Permissions(...) decorator on the route lists only what it truly needs; trim over-broad requirements.","Regenerate/issue a new access token after updating permissions if the cached token predates the change."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const required = ['READ_BOOKINGS']; /* route metadata */\nconst granted = oAuthClient.permissions; /* string[] */\nconst ok = required.every(p => granted.includes(p));\nif (!ok) {\n  // tell user to add missing permissions in the dashboard\n}","typeGuard":"function clientHasPermissions(client: { permissions: string[] }, required: string[]): boolean {\n  return required.every(p => client.permissions.includes(p));\n}","tryCatchPattern":null,"preventionTips":["When creating an OAuth client, grant the full set of permissions the integration will need.","After changing permissions, mint a new access token.","Keep route @Permissions(...) decorators minimal."],"tags":["auth","permissions","oauth","authorization","guard"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}