calcom/cal.diy · error · ForbiddenException

PermissionsGuard - oAuth client with id=${oAuthClient.id} do

Error message

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.

What it means

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).

Source

Thrown at apps/api/v2/src/modules/auth/guards/permissions/permissions.guard.ts:60

    // 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
    if (nextAuthToken || apiKey || isThirdPartyBearerToken) {
      return true;
    }

    if (!bearerToken && !oAuthClientId) {
      throw new ForbiddenException(
        "PermissionsGuard - no authentication provided. Provide either authorization bearer token containing managed user access token or oAuth client id in 'x-cal-client-id' header."
      );
    }

    const oAuthClient = bearerToken
      ? await this.getOAuthClientByAccessToken(bearerToken)
      : await this.getOAuthClientById(oAuthClientId);

    const hasRequiredPermissions = hasPermissions(oAuthClient.permissions, [...requiredPermissions]);

    if (!hasRequiredPermissions) {
      throw new ForbiddenException(
        `PermissionsGuard - oAuth client with id=${
          oAuthClient.id
        } does not have the required permissions=${requiredPermissions
          .map((permission) => this.oAuthClientsOutputService.transformOAuthClientPermission(permission))
          .join(
            ", "
          )}. Go to platform dashboard settings and add the required permissions to the oAuth client.`
      );
    }

    return true;
  }

  async getOAuthClientByAccessToken(
    accessToken: string
  ): Promise<Pick<PlatformOAuthClient, "id" | "permissions">> {
    const oAuthClient = await this.tokensRepository.getAccessTokenClient(accessToken);
    if (!oAuthClient) {

View on GitHub (pinned to 176037d0af)

Solutions

  1. Open the Cal.com platform dashboard → OAuth client settings and add every permission listed in the error message to that client.
  2. If the permission set looks correct, confirm you are authenticating as the right OAuth client (check the oAuthClient.id in the message).
  3. As a platform maintainer: verify the @Permissions(...) decorator on the route lists only what it truly needs; trim over-broad requirements.
  4. Regenerate/issue a new access token after updating permissions if the cached token predates the change.
Defensive patterns

Strategy: validation

Validate before calling

const required = ['READ_BOOKINGS']; /* route metadata */
const granted = oAuthClient.permissions; /* string[] */
const ok = required.every(p => granted.includes(p));
if (!ok) {
  // tell user to add missing permissions in the dashboard
}

Type guard

function clientHasPermissions(client: { permissions: string[] }, required: string[]): boolean {
  return required.every(p => client.permissions.includes(p));
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/f611c4c25b897e8f. Report an issue: GitHub.