Skip to content

Comments

fix: resolve 403 Forbidden error when adding attendees to existing booking via v1 api#24310

Closed
dhairyashiil wants to merge 1 commit intocalcom:mainfrom
dhairyashiil:fix/v1-api-attendee-add
Closed

fix: resolve 403 Forbidden error when adding attendees to existing booking via v1 api#24310
dhairyashiil wants to merge 1 commit intocalcom:mainfrom
dhairyashiil:fix/v1-api-attendee-add

Conversation

@dhairyashiil
Copy link
Member

What does this PR do?

Earlier we only checked if the person making the API call is:

  1. A system-wide admin, OR
  2. The direct owner of the booking
    Other than this, we were sending 403.

This implementation now checks if the user is:

  1. System-wide admin
  2. Organization admin with access to the booking owner
  3. The booking owner
  4. An attendee on the booking
  5. The event type owner
  6. A team admin/owner

Referred code from this file to implement the logic in current PR:
apps/api/v1/pages/api/bookings/[id]/_auth-middleware.ts

Visual Demo

Before:

Before we were getting 403 by using api key of an attendee on the booking,

by.host.and.attendee.before.mov

After:

by.host.and.attendee.after.mov

@dhairyashiil dhairyashiil requested a review from a team as a code owner October 6, 2025 22:09
@graphite-app graphite-app bot added the community Created by Linear-GitHub Sync label Oct 6, 2025
@graphite-app graphite-app bot requested a review from a team October 6, 2025 22:09
@vercel
Copy link

vercel bot commented Oct 6, 2025

@dhairyashiil is attempting to deploy a commit to the cal Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions bot added api area: API, enterprise API, access token, OAuth bookings area: bookings, availability, timezones, double booking enterprise area: enterprise, audit log, organisation, SAML, SSO reactive⚡︎ 🐛 bug Something isn't working 🚨 urgent Created by Linear-GitHub Sync labels Oct 6, 2025
@keithwillcode keithwillcode added the community-interns The team responsible for reviewing, testing and shipping low/medium community PRs label Oct 6, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 6, 2025

Walkthrough

The change updates the attendee creation API to add an organization owner/admin authorization path. For non-system admins who are org owners/admins, it fetches the booking’s userId, resolves accessible users, and permits attendee creation if access is valid. Existing non-admin checks are retained and expanded across booking ownership, attendee membership, event type ownership, and team owner/admin roles. These checks run in parallel via Promise.all; if none pass, a 403 is thrown. On success, the attendee is created and returned in a public schema with a success message. A 404 path for missing user in non-admin cases is preserved.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning The changes extend permission checks to allow valid callers to add attendees, satisfying the requirement to stop 403 errors and return a successful response, but they do not include logic to send the standard invitation and confirmation emails to new attendees as specified in the linked issues. Please add or confirm the invocation of the invitation and confirmation email workflows after an attendee is created to fully meet the linked issues’ expected behavior.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly describes the primary change of fixing the 403 Forbidden error when adding attendees to existing bookings via the v1 API and is concise without extraneous details.
Out of Scope Changes Check ✅ Passed The modifications only adjust authorization checks and attendee creation flow in the v1 API endpoint and do not include unrelated alterations outside the scope of fixing the 403 Forbidden error for adding attendees.
Description Check ✅ Passed The pull request description directly outlines the fixed issue, references the relevant bug numbers, and summarizes the updated permission logic and visual demo steps, making it clearly related to the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/api/v1/pages/api/attendees/_post.ts (1)

103-130: Scope Prisma queries to id only

The three findMany checks only need to know whether a booking exists, but without a select they fetch every scalar on the booking. Tightening these queries to select: { id: true } keeps us aligned with the Prisma minimal-selection guideline and avoids pulling unnecessary fields.

Apply the following diff:

 const bookingAsAttendee = prisma.booking.findMany({
   where: {
     id: body.bookingId,
     attendees: { some: { email: user.email } },
   },
+  select: { id: true },
 });

 const bookingAsEventTypeOwner = prisma.booking.findMany({
   where: {
     id: body.bookingId,
     eventType: {
       owner: { id: userId },
     },
   },
+  select: { id: true },
 });

 const bookingAsTeamOwnerOrAdmin = prisma.booking.findMany({
   where: {
     id: body.bookingId,
     eventType: {
       team: {
         members: {
           some: { userId, role: { in: ["ADMIN", "OWNER"] }, accepted: true },
         },
       },
     },
   },
+  select: { id: true },
 });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6197a2a and b777142.

📒 Files selected for processing (1)
  • apps/api/v1/pages/api/attendees/_post.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • apps/api/v1/pages/api/attendees/_post.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • apps/api/v1/pages/api/attendees/_post.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • apps/api/v1/pages/api/attendees/_post.ts

ibex088

This comment was marked as duplicate.

@github-actions github-actions bot marked this pull request as draft October 7, 2025 07:17
Copy link
Contributor

@ibex088 ibex088 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review -

Comment on lines +72 to +85
if (accessibleUsersIds.length > 0) {
const data = await prisma.attendee.create({
data: {
email: body.email,
name: body.name,
timeZone: body.timeZone,
booking: { connect: { id: body.bookingId } },
},
});

return {
attendee: schemaAttendeeReadPublic.parse(data),
message: "Attendee created successfully",
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don’t duplicate the creation logic. If the user lacks access, return an error and exit. Run all checks first; only after they pass, perform the create action once, at the end.
makes code easier to follow and understand

Comment on lines +103 to +137
const bookingAsAttendee = prisma.booking.findMany({
where: {
id: body.bookingId,
attendees: { some: { email: user.email } },
},
});

const bookingAsEventTypeOwner = prisma.booking.findMany({
where: {
id: body.bookingId,
eventType: {
owner: { id: userId },
},
},
});

const bookingAsTeamOwnerOrAdmin = prisma.booking.findMany({
where: {
id: body.bookingId,
eventType: {
team: {
members: {
some: { userId, role: { in: ["ADMIN", "OWNER"] }, accepted: true },
},
},
},
},
});

const [ownerResult, attendeeResult, eventTypeOwnerResult, teamOwnerResult] = await Promise.all([
bookingAsOwner,
bookingAsAttendee,
bookingAsEventTypeOwner,
bookingAsTeamOwnerOrAdmin,
]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach triggers four parallel database calls, which creates unnecessary load on the system. Since the booking table is our largest table, I wouldn’t recommend doing this. We should look for a more efficient solution, as this approach is quite resource-intensive

@ibex088
Copy link
Contributor

ibex088 commented Oct 7, 2025

This would be a high-risk PR. l'll handle this in the current sprint and include the implementation in v2. Closing this for now.

@ibex088 ibex088 closed this Oct 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api area: API, enterprise API, access token, OAuth bookings area: bookings, availability, timezones, double booking 🐛 bug Something isn't working community Created by Linear-GitHub Sync community-interns The team responsible for reviewing, testing and shipping low/medium community PRs enterprise area: enterprise, audit log, organisation, SAML, SSO reactive⚡︎ size/M 🚨 urgent Created by Linear-GitHub Sync

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Adding attendees to existing bookings via the API returns a 403 Forbidden error,

3 participants