feat: add send & verify code flow to booker atom when email verification is turned on#23074
feat: add send & verify code flow to booker atom when email verification is turned on#23074
Conversation
…e v2 atoms endpoints - Extract verifyCodeUnAuthenticated, verifyCodeAuthenticated, and sendVerifyEmailCode logic from tRPC handlers into reusable platform library functions - Create VerificationAtomsService and AtomsVerificationController following atoms pattern - Add v2 endpoints: /atoms/verification/email/send-code, /atoms/verification/email/verify-code, /atoms/verification/email/verify-code-authenticated - Update useVerifyEmail and useVerifyCode hooks to use new v2 endpoints instead of verified-resources endpoints - Export verification functions from @calcom/platform-libraries following getPublicEvent pattern - Integrate platform-specific verification hooks into BookerPlatformWrapper Co-Authored-By: somay@cal.com <somaychauhan98@gmail.com>
- Update useVerifyEmail hook to use correct response type for v2 endpoint - Update useVerifyCode hook to use v2 verification endpoint - All verification functions now properly exported from platform libraries - Type checking passes with no errors Co-Authored-By: somay@cal.com <somaychauhan98@gmail.com>
- Change package.json dependency from pinned version to workspace version - Add proper error handling to verification service with NestJS HTTP exceptions - Convert generic Error objects to BadRequestException and UnauthorizedException - Ensure verification endpoints return proper HTTP status codes instead of 500 errors Co-Authored-By: somay@cal.com <somaychauhan98@gmail.com>
- Update OpenAPI documentation for v2 verification endpoints - Update yarn.lock after changing platform-libraries dependency Co-Authored-By: somay@cal.com <somaychauhan98@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
WalkthroughAdds an email verification flow across backend and frontend. Backend: new AtomsVerificationController and VerificationAtomsService, DTOs and outputs for send/check/verify endpoints, new exports in platform libraries, refactors that extract/send/verify TRPC handlers (sendVerifyEmailCode, verifyCodeUnAuthenticated, verifyCode, checkEmailVerificationRequired), and removal of an OAuthClientModule import. Frontend: new hooks useVerifyEmail and useVerifyCode, integration into BookerPlatformWrapper, and a changeset plus a platform-libraries version bump in apps/api/v2/package.json. Possibly related PRs
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
…le functions - Refactor sendVerifyEmailCode.handler.ts to extract core logic into sendVerifyEmailCode function - Refactor verifyCodeUnAuthenticated.handler.ts to extract core logic into verifyCodeUnAuthenticated function - Refactor organizations/verifyCode.handler.ts to extract core logic into verifyCodeAuthenticated function - Update platform/libraries/index.ts to import from refactored handler files instead of standalone verification.ts - Remove standalone verification.ts file as requested by user - Keep original tRPC handlers as wrappers that call the extracted functions - Maintain backward compatibility for existing tRPC endpoints Co-Authored-By: somay@cal.com <somaychauhan98@gmail.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
- Add SendVerificationEmailInput and VerifyEmailCodeInput with validation decorators - Add SendVerificationEmailOutput and VerifyEmailCodeOutput following v2 atoms patterns - Update atoms verification controller to use custom types instead of platform types - Auto-update openapi.json with new type definitions Co-Authored-By: somay@cal.com <somaychauhan98@gmail.com>
- Import ZVerifyCodeInputSchema from @calcom/prisma/zod-utils - Import VerifyCodeAuthenticatedInput from organizations handler - Import TSendVerifyEmailCodeSchema from sendVerifyEmailCode schema - Resolves CI build failure in API v2 tests Co-Authored-By: somay@cal.com <somaychauhan98@gmail.com>
| @Post("/verification/email/send-code") | ||
| @Version(VERSION_NEUTRAL) | ||
| @HttpCode(HttpStatus.OK) | ||
| @Throttle({ limit: 3, ttl: 60000, blockDuration: 60000, name: "atoms_verification_email_send_code" }) |
There was a problem hiding this comment.
added this for rate limiting
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts (1)
1-1: Replace MD5 with HMAC-SHA256 and fail closed when encryption key is missing.MD5 is deprecated for security-sensitive derivations. Use a keyed HMAC (SHA-256) and normalize the email. Also, if
CALENDSO_ENCRYPTION_KEYis absent, verification silently degrades to a weak, predictable secret — fail closed.Apply this diff:
- import { createHash } from "crypto"; + import { createHmac } from "crypto"; @@ - await checkRateLimitAndThrowError({ - rateLimitingType: "core", - identifier: hashEmail(email), - }); - - const secret = createHash("md5") - .update(email + process.env.CALENDSO_ENCRYPTION_KEY) - .digest("hex"); + const normalizedEmail = email.trim().toLowerCase(); + + const encryptionKey = process.env.CALENDSO_ENCRYPTION_KEY; + if (!encryptionKey) { + logger.error("Missing CALENDSO_ENCRYPTION_KEY; refusing to verify email code"); + throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" }); + } + + await checkRateLimitAndThrowError({ + rateLimitingType: "core", + identifier: hashEmail(normalizedEmail), + }); + + const secret = createHmac("sha256", encryptionKey) + .update(normalizedEmail) + .digest("hex");Optional hardening (not shown in diff): also bind the rate-limit to a secondary key when available (e.g.,
ctx.user.idor request IP) to reduce brute-force attempts per identity.Also applies to: 49-56
🧹 Nitpick comments (5)
packages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts (5)
28-36: MakeverifyCodeparameters required and annotate return type.All three inputs are mandatory at runtime; model that in the type to catch mistakes at compile time and make the function easier to consume.
Apply this diff to tighten the signature:
-export const verifyCode = async ({ +export const verifyCode = async ({ user, email, code, -}: { - user?: Pick<NonNullable<TrpcSessionUser>, "role">; - email?: string; - code?: string; -}) => { +}: { + user: Pick<NonNullable<TrpcSessionUser>, "role">; + email: string; + code: string; +}): Promise<boolean> => {
62-63: Simplify return; it’s always true at this point.You throw on invalid codes; returning the variable adds no value.
- return isValidToken; + return true;
39-42: Revise server-side E2E flag usage and lower log levelWe’ve confirmed that
NEXT_PUBLIC_IS_E2Eis currently used in dozens of server-side modules (e.g.verifyCode.handler.ts,validateLicense.handler.ts,LicenseKeyService.ts, telemetry, CF Turnstile check, username checks, etc.), and only one place already reads a server-onlyprocess.env.IS_E2E(intwilioProvider.ts). SinceNEXT_PUBLIC_*vars get baked into client bundles, it’s safer to switch all server logic to a dedicated flag (e.g.IS_E2E) and reserveNEXT_PUBLIC_IS_E2Esolely for front-end feature toggles.• Replace all
process.env.NEXT_PUBLIC_IS_E2Echecks in server code withprocess.env.IS_E2E === "1".
• Inpackages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts, changelogger.warn(`Skipping code verification in dev/E2E environment`);to
logger.debug(`Skipping code verification in dev/E2E environment`);to reduce noise.
• Update your CI/E2E startup command (e.g. inplaywright.config.ts) to exportIS_E2E=1alongsideNEXT_PUBLIC_IS_E2E=1.Before merging, please verify which env var your CI sets for E2E runs and ensure
IS_E2Eis enabled there.
65-65: Remove the default export in favor of the existing named exportI confirmed that
verifyCodeHandleris only ever referenced via its named export in this repo—there are no default‐import sites to update. You can safely drop the finalexport defaultand rely solely on the named export for better tree-shaking and future refactors.• File to update:
- packages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts, remove line 65 (
export default verifyCodeHandler;)
• No importers need adjustment—there are zero occurrences ofimport … from '…/verifyCode.handler'pulling in the default export.-export default verifyCodeHandler;
58-58: Extract the TOTP step into a constant and trim incoming codesTo avoid “magic numbers” and guard against stray whitespace in user-entered codes, factor out the 900-second window into a named constant and apply
.trim()to the input before validation. We’ve confirmed that the email sender (packages/features/auth/lib/verifyEmail.ts) uses the same{ step: 900 }setting and identical secret derivation (createHash("md5").update(email + process.env.CALENDSO_ENCRYPTION_KEY).digest("hex")), so this change stays in sync.• packages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts @ line 58
• packages/trpc/server/routers/viewer/workflows/verifyEmailCode.handler.ts @ line 28
• packages/trpc/server/routers/viewer/auth/verifyCodeUnAuthenticated.handler.ts @ line 25Apply this diff to each handler:
- const isValidToken = totpRawCheck(code, secret, { step: 900 }); + const isValidToken = totpRawCheck(code.trim(), secret, { step: EMAIL_VERIFICATION_TOTP_STEP_SECONDS });And add near the top of the file (alongside your other imports):
// TOTP validity window in seconds; must match the sender’s totp.options.step const EMAIL_VERIFICATION_TOTP_STEP_SECONDS = 900 as const;
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/trpc/server/routers/viewer/organizations/verifyCode.handler.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 useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
packages/trpc/server/routers/viewer/organizations/verifyCode.handler.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:
packages/trpc/server/routers/viewer/organizations/verifyCode.handler.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:
packages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts
🧬 Code graph analysis (1)
packages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts (1)
packages/platform/libraries/index.ts (1)
verifyCode(125-125)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Production builds / Build Docs
- GitHub Check: Production builds / Build Atoms
- GitHub Check: Production builds / Build API v1
- GitHub Check: Tests / Unit
- GitHub Check: Atoms E2E Tests
🔇 Additional comments (3)
packages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts (3)
6-6: Good call using PII-safe identifier.Importing and using
hashEmailis the right direction to avoid leaking raw emails in rate-limit identifiers. See my later comment about normalizing emails before hashing for consistency.
21-26: Nice delegation and separation of concerns.Having the handler delegate to a reusable
verifyCodefunction improves testability and cross-surface reuse.
44-47: Confirm ADMIN bypass is intended and scoped.Bypassing verification for
ADMINis a policy choice. If you only intend to bypass for instance-level admins, verify that"ADMIN"can’t be assigned at a narrower scope. Also consider downgrading toinfo/debuglog to avoid “warn” spam.
E2E results are ready! |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/seated-bookings.e2e-spec.ts (3)
261-264: Make the guests check resilientSame rationale as above; prefer asserting array type over strict emptiness.
- guests: [], + guests: expect.any(Array),
276-279: Resilience: guests fieldMirror the resilient matcher here as well.
- guests: [], + guests: expect.any(Array),
516-519: Consistent guests assertion across testsUse the same asymmetric matcher for consistency and to reduce brittleness.
- guests: [], + guests: expect.any(Array),
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/seated-bookings.e2e-spec.ts(5 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 useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/seated-bookings.e2e-spec.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/v2/src/ee/bookings/2024-08-13/controllers/e2e/seated-bookings.e2e-spec.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/v2/src/ee/bookings/2024-08-13/controllers/e2e/seated-bookings.e2e-spec.ts
🧠 Learnings (1)
📚 Learning: 2025-08-21T13:44:06.784Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.784Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.
Applied to files:
apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/seated-bookings.e2e-spec.ts
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Atoms E2E Tests
🔇 Additional comments (1)
apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/seated-bookings.e2e-spec.ts (1)
36-36: LGTM: narrowed Prisma import to Team onlyThe import is scoped to just Team and aligns with actual usage in this spec. No issues.
https://www.loom.com/share/5a7e19eb50504908ad5c562174e82c51?sid=67f6ff14-1d69-4f75-8603-5c68130b9c5e