Skip to content

OAuth credential sync and app integration enhancements#8

Open
everettbu wants to merge 1 commit into
oauth-security-basefrom
oauth-security-enhanced
Open

OAuth credential sync and app integration enhancements#8
everettbu wants to merge 1 commit into
oauth-security-basefrom
oauth-security-enhanced

Conversation

@everettbu

@everettbu everettbu commented Jul 28, 2025

Copy link
Copy Markdown

Test 8

Summary by CodeRabbit

  • New Features

    • Introduced support for app credential synchronization, including a new API endpoint for secure credential sharing and synchronization with external applications.
    • Added new environment variables to support credential sync, webhook verification, and encryption for enhanced integration flexibility.
    • Enabled OAuth token refresh centralization across multiple integrations for improved reliability and maintainability.
  • Refactor

    • Standardized and centralized OAuth token refresh logic across various calendar and video integrations, improving consistency and code maintainability.
    • Updated import paths and internal logic to streamline integration code structure.
  • Chores

    • Updated configuration files to recognize new environment variables related to credential synchronization.
    • Enhanced documentation in environment example files for easier setup and integration by self-hosters.

…11059)

* Add credential sync .env variables

* Add webhook to send app credentials

* Upsert credentials when webhook called

* Refresh oauth token from a specific endpoint

* Pass appSlug

* Add credential encryption

* Move oauth helps into a folder

* Create parse token response wrapper

* Add OAuth helpers to apps

* Clean up

* Refactor `appDirName` to `appSlug`

* Address feedback

* Change to safe parse

* Remove console.log

---------

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
@coderabbitai

coderabbitai Bot commented Jul 28, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This update introduces a centralized app credential sharing and synchronization mechanism, primarily by adding new environment variables and feature flags, a webhook API endpoint, and utility functions for OAuth token refresh and response parsing. Several integration modules are refactored to use these new utilities, and import paths are reorganized for consistency. No existing logic is removed.

Changes

Cohort / File(s) Change Summary
Credential Sharing: Environment & Config
.env.example, turbo.json, packages/lib/constants.ts
Added new environment variables and feature flag for app credential sharing and synchronization; updated global env config.
Webhook API Endpoint
apps/web/pages/api/webhook/app-credential.ts
Introduced a new API route for processing app credential webhooks, including validation, decryption, and DB operations.
OAuth Credential Sync Utilities
packages/app-store/_utils/oauth/parseRefreshTokenResponse.ts,
packages/app-store/_utils/oauth/refreshOAuthTokens.ts
Added new utility functions for parsing OAuth refresh responses and centralized token refresh logic with conditional sync.
OAuth State/Utility Path Refactors
packages/app-store/_utils/oauth/createOAuthAppCredential.ts,
packages/app-store/_utils/oauth/decodeOAuthState.ts,
packages/app-store/_utils/oauth/encodeOAuthState.ts,
packages/app-store/googlecalendar/api/add.ts,
packages/app-store/googlecalendar/api/callback.ts,
packages/app-store/hubspot/api/add.ts,
packages/app-store/hubspot/api/callback.ts,
packages/app-store/larkcalendar/api/add.ts,
packages/app-store/larkcalendar/api/callback.ts,
packages/app-store/office365calendar/api/add.ts,
packages/app-store/office365calendar/api/callback.ts,
packages/app-store/office365video/api/add.ts,
packages/app-store/office365video/api/callback.ts,
packages/app-store/salesforce/api/add.ts,
packages/app-store/salesforce/api/callback.ts,
packages/app-store/stripepayment/api/callback.ts,
packages/app-store/tandemvideo/api/callback.ts,
packages/app-store/webex/api/callback.ts,
packages/app-store/zoho-bigin/api/add.ts,
packages/app-store/zoho-bigin/api/callback.ts,
packages/app-store/zohocrm/api/_getAdd.ts,
packages/app-store/zohocrm/api/callback.ts,
packages/app-store/zoomvideo/api/add.ts,
packages/app-store/zoomvideo/api/callback.ts
Updated import paths for OAuth utility functions and state encoding/decoding for consistency.
Integration Token Refresh Refactors
packages/app-store/googlecalendar/lib/CalendarService.ts,
packages/app-store/hubspot/lib/CalendarService.ts,
packages/app-store/larkcalendar/lib/CalendarService.ts,
packages/app-store/office365calendar/lib/CalendarService.ts,
packages/app-store/office365video/lib/VideoApiAdapter.ts,
packages/app-store/salesforce/lib/CalendarService.ts,
packages/app-store/webex/lib/VideoApiAdapter.ts,
packages/app-store/zoho-bigin/lib/CalendarService.ts,
packages/app-store/zohocrm/lib/CalendarService.ts,
packages/app-store/zoomvideo/lib/VideoApiAdapter.ts
Refactored token refresh logic to use new centralized utility functions; improved validation and error handling; introduced schema validation for Salesforce.

Sequence Diagram(s)

sequenceDiagram
    participant ExternalApp as External App
    participant WebhookAPI as Webhook API Endpoint
    participant DB as Database

    ExternalApp->>WebhookAPI: POST /api/webhook/app-credential (with encrypted keys)
    WebhookAPI->>WebhookAPI: Validate headers & body, decrypt keys
    WebhookAPI->>DB: Find or create credential for user & app
    WebhookAPI-->>ExternalApp: Respond with status (created/updated/error)
Loading
sequenceDiagram
    participant Integration as Integration Service
    participant RefreshUtil as refreshOAuthTokens Utility
    participant SyncEndpoint as Credential Sync Endpoint

    Integration->>RefreshUtil: Request token refresh (with appSlug, userId)
    alt Sharing enabled & userId present
        RefreshUtil->>SyncEndpoint: POST /sync-endpoint (userId, appSlug)
        SyncEndpoint-->>RefreshUtil: Token response
    else
        RefreshUtil->>Integration: Call original refresh function
    end
    RefreshUtil-->>Integration: Return token data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A rabbit with keys and a secret to share,
Hopped through the code with diligent care.
Webhooks now listen, credentials align,
Tokens refreshed in a centralized line.
With imports in order and configs anew,
This bunny brings sync for the whole crew!
🐇🔑✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch oauth-security-enhanced

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@everettbu

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 28, 2025

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

Copy link
Copy Markdown
Contributor

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "OAuth credential sync and app integration enhancements". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🔭 Outside diff range comments (5)
packages/app-store/salesforce/api/callback.ts (1)

18-21: Guard condition lets code slip through when it is an array

req.query.code can be string | string[] | undefined.
Using logical AND (&&) only rejects the case where code is simultaneously undefined and not a string – it silently accepts an array (e.g. ["a","b"]). That breaks the OAuth flow and can be abused.

-  if (code === undefined && typeof code !== "string") {
+  if (code === undefined || typeof code !== "string") {
packages/app-store/hubspot/api/callback.ts (1)

24-28: Missing check for absent code parameter

With the current condition the handler accepts a request that omits code entirely, which causes HubSpot to throw later. Reject early:

-  if (code && typeof code !== "string") {
+  if (code === undefined || typeof code !== "string") {

The same validation pattern appears in several callback handlers – consider extracting a small helper to enforce it consistently.

packages/app-store/zohocrm/api/callback.ts (1)

18-22: Same code validation bug as in Salesforce handler

-  if (code === undefined && typeof code !== "string") {
+  if (code === undefined || typeof code !== "string") {

Please patch across all similar handlers to avoid inconsistent behaviour.

packages/app-store/zoho-bigin/api/callback.ts (1)

17-20: code may be undefined yet no error is returned

-  if (code && typeof code !== "string") {
+  if (code === undefined || typeof code !== "string") {

This aligns the behaviour with other OAuth callbacks.

packages/app-store/salesforce/lib/CalendarService.ts (1)

101-108: Use refreshed tokens for connection.

The jsforce connection is created with the original credential tokens instead of the refreshed ones:

  return new jsforce.Connection({
    clientId: consumer_key,
    clientSecret: consumer_secret,
    redirectUri: WEBAPP_URL + "/api/integrations/salesforce/callback",
-   instanceUrl: credentialKey.instance_url,
-   accessToken: credentialKey.access_token,
-   refreshToken: credentialKey.refresh_token,
+   instanceUrl: accessTokenParsed.data.instance_url,
+   accessToken: accessTokenParsed.data.access_token,
+   refreshToken: credentialKey.refresh_token,
  });
♻️ Duplicate comments (4)
packages/app-store/webex/api/callback.ts (1)

7-7: Same alias recommendation as for TandemVideo callback

See previous comment about replacing deep relative imports with a path alias for OAuth utilities.

packages/app-store/zoomvideo/api/callback.ts (1)

7-7: Same alias recommendation as for TandemVideo callback

See earlier note about using a path alias for createOAuthAppCredential.

packages/app-store/larkcalendar/api/callback.ts (1)

10-10: Same alias recommendation as for TandemVideo callback

Using an alias (e.g., @utils/oauth/decodeOAuthState) will make this import more resilient.

packages/app-store/office365video/api/callback.ts (1)

9-10: Same alias recommendation as for TandemVideo callback

Apply a path alias for both createOAuthAppCredential and decodeOAuthState to avoid long relative paths.

🧹 Nitpick comments (8)
.env.example (1)

233-245: Well-documented credential sync configuration with minor formatting improvements needed.

The new APP CREDENTIAL SYNC section is comprehensive and well-documented. However, there are some formatting inconsistencies to address:

-CALCOM_WEBHOOK_SECRET=""
+CALCOM_WEBHOOK_SECRET=
-CALCOM_WEBHOOK_HEADER_NAME="calcom-webhook-secret"
+CALCOM_WEBHOOK_HEADER_NAME=calcom-webhook-secret
-CALCOM_CREDENTIAL_SYNC_ENDPOINT=""
+CALCOM_CREDENTIAL_SYNC_ENDPOINT=
-CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY=""
+CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY=

The documentation clearly explains the 32-byte requirement for AES256 encryption and provides helpful openssl commands for key generation.

packages/app-store/zoomvideo/api/add.ts (1)

8-8: Consider introducing a path alias to avoid deep relative imports

With OAuth helpers now living under _utils/oauth, a growing number of files use long relative paths like "../../_utils/oauth/encodeOAuthState". Adding a paths alias in tsconfig.json, e.g.

"paths": {
  "@utils/oauth/*": ["packages/app-store/_utils/oauth/*"]
}

would shorten these imports to @utils/oauth/encodeOAuthState, improving readability and easing future moves.

packages/app-store/tandemvideo/api/callback.ts (1)

7-7: Prefer a path alias over deep relative import

The new path works, but ../../_utils/oauth/createOAuthAppCredential is brittle; folder moves will break multiple files.
Consider adding a @utils/oauth (or similar) alias in tsconfig.json and switching to:

-import createOAuthAppCredential from "../../_utils/oauth/createOAuthAppCredential";
+import createOAuthAppCredential from "@utils/oauth/createOAuthAppCredential";

This keeps imports stable and improves readability across the app-store packages.

packages/app-store/_utils/oauth/createOAuthAppCredential.ts (1)

6-7: Redundant directory hop in relative import

createOAuthAppCredential.ts already lives inside …/_utils/oauth/.
Using ../oauth/decodeOAuthState climbs up one level only to descend back into the same folder. This works but is brittle if the folder is renamed.

-import { decodeOAuthState } from "../oauth/decodeOAuthState";
+import { decodeOAuthState } from "./decodeOAuthState";

[note: keep throwIfNotHaveAdminAccessToTeam as-is]

packages/app-store/_utils/oauth/refreshOAuthTokens.ts (2)

3-3: Improve type safety for better maintainability.

The function signature uses any for both the refreshFunction parameter and return type, which reduces type safety.

Consider improving the typing:

-const refreshOAuthTokens = async (refreshFunction: () => any, appSlug: string, userId: number | null) => {
+const refreshOAuthTokens = async <T>(refreshFunction: () => Promise<T>, appSlug: string, userId: number | null): Promise<T> => {

5-5: Consider validating the credential sync endpoint URL.

While the current checks are adequate, validating the endpoint URL format could prevent runtime errors.

- if (APP_CREDENTIAL_SHARING_ENABLED && process.env.CALCOM_CREDENTIAL_SYNC_ENDPOINT && userId) {
+ if (APP_CREDENTIAL_SHARING_ENABLED && process.env.CALCOM_CREDENTIAL_SYNC_ENDPOINT && userId) {
+   try {
+     new URL(process.env.CALCOM_CREDENTIAL_SYNC_ENDPOINT);
+   } catch {
+     console.warn('Invalid CALCOM_CREDENTIAL_SYNC_ENDPOINT URL, falling back to original refresh');
+     return await refreshFunction();
+   }
apps/web/pages/api/webhook/app-credential.ts (1)

56-92: Solid credential management logic with one improvement opportunity.

The encryption/decryption and database operations are well implemented. The manual upsert logic is appropriate given the constraint.

Consider adding error handling around the JSON.parse operation to gracefully handle malformed encrypted data:

  // Decrypt the keys
- const keys = JSON.parse(
-   symmetricDecrypt(reqBody.keys, process.env.CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY || "")
- );
+ let keys;
+ try {
+   keys = JSON.parse(
+     symmetricDecrypt(reqBody.keys, process.env.CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY || "")
+   );
+ } catch (error) {
+   return res.status(400).json({ message: "Invalid encrypted keys format" });
+ }
packages/app-store/salesforce/lib/CalendarService.ts (1)

75-99: Consider consistency with other integrations.

This integration implements token refresh directly while others use the refreshOAuthTokens utility. Consider whether this should follow the same pattern for consistency, especially if the credential sharing feature should apply to Salesforce as well.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc89fe0 and 824145b.

📒 Files selected for processing (40)
  • .env.example (2 hunks)
  • apps/web/pages/api/webhook/app-credential.ts (1 hunks)
  • packages/app-store/_utils/oauth/createOAuthAppCredential.ts (1 hunks)
  • packages/app-store/_utils/oauth/decodeOAuthState.ts (1 hunks)
  • packages/app-store/_utils/oauth/encodeOAuthState.ts (1 hunks)
  • packages/app-store/_utils/oauth/parseRefreshTokenResponse.ts (1 hunks)
  • packages/app-store/_utils/oauth/refreshOAuthTokens.ts (1 hunks)
  • packages/app-store/googlecalendar/api/add.ts (1 hunks)
  • packages/app-store/googlecalendar/api/callback.ts (1 hunks)
  • packages/app-store/googlecalendar/lib/CalendarService.ts (2 hunks)
  • packages/app-store/hubspot/api/add.ts (1 hunks)
  • packages/app-store/hubspot/api/callback.ts (1 hunks)
  • packages/app-store/hubspot/lib/CalendarService.ts (2 hunks)
  • packages/app-store/larkcalendar/api/add.ts (1 hunks)
  • packages/app-store/larkcalendar/api/callback.ts (1 hunks)
  • packages/app-store/larkcalendar/lib/CalendarService.ts (2 hunks)
  • packages/app-store/office365calendar/api/add.ts (1 hunks)
  • packages/app-store/office365calendar/api/callback.ts (1 hunks)
  • packages/app-store/office365calendar/lib/CalendarService.ts (2 hunks)
  • packages/app-store/office365video/api/add.ts (1 hunks)
  • packages/app-store/office365video/api/callback.ts (1 hunks)
  • packages/app-store/office365video/lib/VideoApiAdapter.ts (2 hunks)
  • packages/app-store/salesforce/api/add.ts (1 hunks)
  • packages/app-store/salesforce/api/callback.ts (1 hunks)
  • packages/app-store/salesforce/lib/CalendarService.ts (4 hunks)
  • packages/app-store/stripepayment/api/callback.ts (1 hunks)
  • packages/app-store/tandemvideo/api/callback.ts (1 hunks)
  • packages/app-store/webex/api/callback.ts (1 hunks)
  • packages/app-store/webex/lib/VideoApiAdapter.ts (2 hunks)
  • packages/app-store/zoho-bigin/api/add.ts (2 hunks)
  • packages/app-store/zoho-bigin/api/callback.ts (1 hunks)
  • packages/app-store/zoho-bigin/lib/CalendarService.ts (2 hunks)
  • packages/app-store/zohocrm/api/_getAdd.ts (1 hunks)
  • packages/app-store/zohocrm/api/callback.ts (1 hunks)
  • packages/app-store/zohocrm/lib/CalendarService.ts (2 hunks)
  • packages/app-store/zoomvideo/api/add.ts (1 hunks)
  • packages/app-store/zoomvideo/api/callback.ts (1 hunks)
  • packages/app-store/zoomvideo/lib/VideoApiAdapter.ts (3 hunks)
  • packages/lib/constants.ts (1 hunks)
  • turbo.json (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
packages/app-store/zoho-bigin/api/add.ts (1)
packages/lib/constants.ts (1)
  • WEBAPP_URL (9-15)
packages/app-store/salesforce/lib/CalendarService.ts (1)
packages/prisma/index.ts (1)
  • prisma (56-56)
packages/app-store/hubspot/lib/CalendarService.ts (2)
packages/app-store/hubspot/api/callback.ts (1)
  • HubspotToken (17-19)
packages/lib/constants.ts (1)
  • WEBAPP_URL (9-15)
packages/app-store/_utils/oauth/refreshOAuthTokens.ts (1)
packages/lib/constants.ts (1)
  • APP_CREDENTIAL_SHARING_ENABLED (103-104)
packages/app-store/office365calendar/lib/CalendarService.ts (1)
packages/lib/errors.ts (1)
  • handleErrorsJson (14-30)
🪛 dotenv-linter (3.3.0)
.env.example

[warning] 237-237: [QuoteCharacter] The value has quote characters (', ")


[warning] 239-239: [QuoteCharacter] The value has quote characters (', ")


[warning] 239-239: [UnorderedKey] The CALCOM_WEBHOOK_HEADER_NAME key should go before the CALCOM_WEBHOOK_SECRET key


[warning] 240-240: [QuoteCharacter] The value has quote characters (', ")


[warning] 240-240: [UnorderedKey] The CALCOM_CREDENTIAL_SYNC_ENDPOINT key should go before the CALCOM_WEBHOOK_HEADER_NAME key


[warning] 244-244: [QuoteCharacter] The value has quote characters (', ")


[warning] 244-244: [UnorderedKey] The CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY key should go before the CALCOM_CREDENTIAL_SYNC_ENDPOINT key

🔇 Additional comments (48)
packages/lib/constants.ts (1)

103-104: LGTM! Well-implemented feature flag.

The APP_CREDENTIAL_SHARING_ENABLED constant properly checks for both required environment variables, ensuring the credential sharing feature is only enabled when fully configured. This prevents runtime errors from partial configurations.

packages/app-store/_utils/oauth/encodeOAuthState.ts (1)

3-3: LGTM! Correct import path adjustment.

The import path update correctly reflects the OAuth utilities reorganization into the dedicated oauth subdirectory.

packages/app-store/_utils/oauth/decodeOAuthState.ts (1)

3-3: LGTM! Consistent import path update.

The import path correctly reflects the OAuth utilities reorganization, maintaining consistency with the other OAuth utility files.

packages/app-store/larkcalendar/api/add.ts (1)

9-9: LGTM! Import path updated for OAuth utility reorganization.

The import path correctly reflects the new location of OAuth utilities in the dedicated oauth subdirectory, maintaining consistency with the broader refactoring effort.

packages/app-store/hubspot/api/add.ts (1)

7-7: Import path realigned to new OAuth utils folder – looks correct

Nothing else to flag for this change.

packages/app-store/zohocrm/api/_getAdd.ts (1)

7-7: Import path update looks correct

No issues spotted; the handler still calls encodeOAuthState right below.

packages/app-store/office365calendar/api/callback.ts (1)

9-9: decodeOAuthState Module Verified

The file packages/app-store/_utils/oauth/decodeOAuthState.ts exists and exports decodeOAuthState as a named function (line 5). No further changes are needed.

packages/app-store/salesforce/api/add.ts (1)

7-7: Import path realigned – looks good

No further action needed.

packages/app-store/stripepayment/api/callback.ts (1)

5-7: Import paths verified – no changes needed

I confirmed that both imports resolve correctly:

  • createOAuthAppCredential exists at packages/app-store/_utils/oauth/createOAuthAppCredential.ts
  • getInstalledAppPath exists at packages/app-store/_utils/getInstalledAppPath.ts

All relative paths are valid; the code can be merged as-is.

packages/app-store/googlecalendar/api/add.ts (1)

6-8: Import path confirmed for encodeOAuthState

The helper file exists at packages/app-store/_utils/oauth/encodeOAuthState.ts, so the import in packages/app-store/googlecalendar/api/add.ts is correct. No changes needed.

packages/app-store/office365calendar/api/add.ts (1)

6-8: encodeOAuthState import verified
The encodeOAuthState utility exists at packages/app-store/_utils/oauth/encodeOAuthState.ts. No changes are required here.

packages/app-store/googlecalendar/api/callback.ts (1)

8-11: decodeOAuthState import path is correct

Verified that packages/app-store/_utils/oauth/decodeOAuthState.ts exists and the relative import in packages/app-store/googlecalendar/api/callback.ts ("../../_utils/oauth/decodeOAuthState") correctly resolves. No changes needed.

packages/app-store/office365video/api/add.ts (1)

6-8: encodeOAuthState Import Path Confirmed

The encodeOAuthState helper exists at packages/app-store/_utils/oauth/encodeOAuthState.ts, so the import in packages/app-store/office365video/api/add.ts is correct and OAuth initiation won’t fail. No further changes needed.

packages/app-store/salesforce/api/callback.ts (1)

9-11: Import-path update looks correct

Moving OAuth helpers under _utils/oauth is consistent with the new structure and the relative path resolves correctly from this location. No action needed.

packages/app-store/hubspot/api/callback.ts (1)

10-12: Import-path update LGTM

packages/app-store/zohocrm/api/callback.ts (1)

10-12: Import-path update LGTM

packages/app-store/zoho-bigin/api/callback.ts (1)

10-12: Import-path update LGTM

packages/app-store/zoho-bigin/api/add.ts (2)

7-7: LGTM! Import path correctly updated.

The import path change aligns with the OAuth utilities reorganization into the oauth subdirectory.


17-17: No breaking change: hardcoded slug matches config.json

The config.json for this package defines "slug": "zoho-bigin", so the literal /api/integrations/zoho-bigin/callback URI is identical to using /api/integrations/${appConfig.slug}/callback here. No other references to appConfig.slug in add.ts need updating.

turbo.json (1)

200-201: LGTM! Environment variables properly added for credential sharing feature.

The new environment variables for app credential sharing (CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY, CALCOM_CREDENTIAL_SYNC_ENDPOINT, CALCOM_WEBHOOK_HEADER_NAME, CALCOM_WEBHOOK_SECRET) are correctly added to the globalEnv array and maintain alphabetical ordering.

Also applies to: 205-205, 207-207

packages/app-store/webex/lib/VideoApiAdapter.ts (2)

11-11: LGTM! Import correctly added for OAuth utility.

The import for refreshOAuthTokens follows the consistent OAuth utilities reorganization.


62-78: LGTM! OAuth token refresh properly refactored.

The token refresh logic has been correctly refactored to use the refreshOAuthTokens utility while preserving the original fetch implementation. The service identifier "webex" and credential.userId are properly passed as parameters.

packages/app-store/zohocrm/lib/CalendarService.ts (2)

19-19: LGTM! Import correctly added for OAuth utility.

The import for refreshOAuthTokens aligns with the OAuth utilities reorganization.


204-216: LGTM! OAuth token refresh properly refactored.

The token refresh logic has been correctly refactored to use the refreshOAuthTokens utility while preserving the original axios implementation. The service identifier "zohocrm" and credential.userId are properly passed.

packages/app-store/office365video/lib/VideoApiAdapter.ts (2)

12-12: LGTM! Import correctly added for OAuth utility.

The import for refreshOAuthTokens follows the consistent OAuth utilities reorganization pattern.


61-75: LGTM! OAuth token refresh properly refactored.

The token refresh logic has been correctly refactored to use the refreshOAuthTokens utility while preserving the original fetch implementation. The service identifier "msteams" appropriately represents the Microsoft Teams service, and credential.userId is properly passed.

packages/app-store/larkcalendar/lib/CalendarService.ts (2)

14-14: LGTM: Import addition is correct.

The import for refreshOAuthTokens is properly structured and follows the project's import patterns.


67-82: LGTM: OAuth token refresh refactor is well-implemented.

The refactor correctly wraps the original Lark API token refresh call in the refreshOAuthTokens utility while preserving all existing logic. The integration name and user ID are properly passed for credential sync functionality.

packages/app-store/zoho-bigin/lib/CalendarService.ts (2)

18-18: LGTM: Import addition is correct.

The import for refreshOAuthTokens follows the established pattern and is properly positioned.


85-94: LGTM: OAuth token refresh refactor is correctly implemented.

The refactor properly wraps the axios POST request in the refreshOAuthTokens utility while maintaining all existing token handling logic. Note that this implementation passes credentialId as the third parameter, which is consistent with this method's signature that receives the credential ID rather than the full credential object.

packages/app-store/hubspot/lib/CalendarService.ts (2)

26-26: LGTM: Import addition is correct.

The import for refreshOAuthTokens is properly structured and consistent with other integrations.


177-189: LGTM: OAuth token refresh refactor is well-implemented.

The refactor correctly wraps the HubSpot SDK token refresh call in the refreshOAuthTokens utility while preserving all existing logic. The integration name and user ID are properly passed for credential sync functionality.

packages/app-store/googlecalendar/lib/CalendarService.ts (2)

21-22: LGTM: Import additions are correct.

Both imports for parseRefreshTokenResponse and refreshOAuthTokens follow the established pattern and are properly positioned.


86-93: LGTM: OAuth token refresh refactor is well-implemented.

The refactor correctly wraps the Google OAuth token refresh call in the refreshOAuthTokens utility and introduces the parseRefreshTokenResponse helper for standardized token parsing. This enables both local and centralized credential handling while preserving all existing logic.

Also applies to: 97-97

packages/app-store/zoomvideo/lib/VideoApiAdapter.ts (3)

12-13: LGTM! Clean import organization.

The new utility imports for OAuth token refresh and response parsing are properly structured and follow consistent naming conventions.


79-94: Well-structured refactoring of token refresh logic.

The delegation to refreshOAuthTokens utility centralizes the OAuth flow while preserving the Zoom-specific request details through the callback pattern. The additional parameters (app identifier and user ID) properly support the new credential sharing feature.


104-104: Good standardization of token response parsing.

The use of parseRefreshTokenResponse utility centralizes response validation logic while maintaining type safety through the schema parameter.

apps/web/pages/api/webhook/app-credential.ts (3)

17-29: Excellent security implementation.

The webhook properly validates both the feature flag and webhook secret before processing requests. The configurable header name via environment variable adds good flexibility while maintaining security.


31-54: Comprehensive validation logic.

The request validation covers all necessary checks: schema validation, user existence, app existence, and metadata validation. Error handling provides appropriate HTTP status codes and informative messages.


9-15: Well-defined request schema.

The Zod schema is appropriately typed with clear documentation about the encryption requirement for the keys field.

packages/app-store/_utils/oauth/parseRefreshTokenResponse.ts (3)

15-19: Smart conditional validation approach.

The logic appropriately uses minimal validation when credential sharing is enabled with a sync endpoint, while maintaining strict validation otherwise. This provides good flexibility for different deployment scenarios.


21-23: Appropriate error handling.

The error handling correctly throws on validation failure with a generic message that doesn't expose implementation details.


25-27: Clarify the purpose of default refresh_token assignment.

Setting "refresh_token" as a literal default value seems unusual. Could you clarify:

  1. Is this intentional for credential sharing scenarios where refresh tokens aren't needed?
  2. Would it be better to document this behavior or use a more descriptive placeholder?
  3. Could this mask legitimate cases where refresh tokens are unexpectedly missing?
packages/app-store/office365calendar/lib/CalendarService.ts (3)

20-21: Consistent utility imports.

The OAuth utility imports follow the same pattern as other integrations and are properly structured.


246-261: Consistent token refresh refactoring.

The refactoring properly delegates to the refreshOAuthTokens utility while preserving Office365-specific OAuth request details. The callback approach maintains the existing logic structure.


263-263: Standardized response parsing.

The use of parseRefreshTokenResponse utility maintains consistency across OAuth integrations and supports the credential sharing feature.

packages/app-store/salesforce/lib/CalendarService.ts (2)

4-4: Appropriate imports for Salesforce integration.

The zod and parseRefreshTokenResponse imports are correctly added for the token validation functionality.

Also applies to: 20-20


39-47: Well-defined Salesforce token schema.

The schema comprehensively covers all expected Salesforce OAuth token response fields with appropriate types.

Comment on lines +5 to +11
const minimumTokenResponseSchema = z.object({
access_token: z.string(),
// Assume that any property with a number is the expiry
[z.string().toString()]: z.number(),
// Allow other properties in the token response
[z.string().optional().toString()]: z.unknown().optional(),
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix the schema definition syntax.

The dynamic key syntax appears incorrect and may not work as intended:

const minimumTokenResponseSchema = z.object({
  access_token: z.string(),
-  //   Assume that any property with a number is the expiry
-  [z.string().toString()]: z.number(),
-  //   Allow other properties in the token response
-  [z.string().optional().toString()]: z.unknown().optional(),
+}).catchall(z.unknown());

Alternatively, if you need to capture numeric expiry fields specifically, consider using z.record() or defining expected fields explicitly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const minimumTokenResponseSchema = z.object({
access_token: z.string(),
// Assume that any property with a number is the expiry
[z.string().toString()]: z.number(),
// Allow other properties in the token response
[z.string().optional().toString()]: z.unknown().optional(),
});
const minimumTokenResponseSchema = z.object({
access_token: z.string(),
}).catchall(z.unknown());
🤖 Prompt for AI Agents
In packages/app-store/_utils/oauth/parseRefreshTokenResponse.ts around lines 5
to 11, the schema definition uses invalid dynamic key syntax with
z.string().toString() which is incorrect. Replace the dynamic keys with a proper
z.record() to capture keys with number values for expiry fields, and use
z.record(z.unknown()) or explicitly define expected fields for other properties.
This will fix the schema validation and correctly allow additional properties.

Comment on lines +5 to +15
if (APP_CREDENTIAL_SHARING_ENABLED && process.env.CALCOM_CREDENTIAL_SYNC_ENDPOINT && userId) {
// Customize the payload based on what your endpoint requires
// The response should only contain the access token and expiry date
const response = await fetch(process.env.CALCOM_CREDENTIAL_SYNC_ENDPOINT, {
method: "POST",
body: new URLSearchParams({
calcomUserId: userId.toString(),
appSlug,
}),
});
return response;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Address security and reliability concerns in credential sync.

The credential sync request has several critical issues:

  1. Missing error handling: The fetch request can throw exceptions that aren't caught
  2. No authentication: The endpoint request lacks authentication headers or signing
  3. No response validation: The response status and content aren't validated
  4. Missing timeout: No request timeout configured for external API calls
  5. Sensitive data exposure: User ID sent in URLSearchParams without encryption

Consider these improvements:

  if (APP_CREDENTIAL_SHARING_ENABLED && process.env.CALCOM_CREDENTIAL_SYNC_ENDPOINT && userId) {
+   try {
      const response = await fetch(process.env.CALCOM_CREDENTIAL_SYNC_ENDPOINT, {
        method: "POST",
+       headers: {
+         'Authorization': `Bearer ${process.env.CALCOM_WEBHOOK_SECRET}`,
+         'Content-Type': 'application/x-www-form-urlencoded',
+       },
        body: new URLSearchParams({
          calcomUserId: userId.toString(),
          appSlug,
        }),
+       signal: AbortSignal.timeout(10000), // 10 second timeout
      });
+     
+     if (!response.ok) {
+       throw new Error(`Credential sync failed: ${response.status}`);
+     }
+     
      return response;
+   } catch (error) {
+     // Log error and fallback to original refresh function
+     console.error('Credential sync failed, falling back to original refresh:', error);
+     return await refreshFunction();
+   }
  } else {

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/app-store/_utils/oauth/refreshOAuthTokens.ts between lines 5 and 15,
the fetch request for credential sync lacks error handling, authentication,
response validation, timeout, and sends sensitive data unencrypted. To fix this,
wrap the fetch call in a try-catch block to handle exceptions, add appropriate
authentication headers or signing to the request, check the response status and
validate the response content before returning, implement a timeout mechanism
for the fetch request, and avoid sending sensitive user ID data in plain
URLSearchParams by encrypting or securely encoding it.

}),
});

if (response.statusText !== "OK") throw new HttpError({ statusCode: 400, message: response.statusText });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix HTTP status validation.

The status text check is incorrect. HTTP responses typically have "OK" as status text, not "OK":

- if (response.statusText !== "OK") throw new HttpError({ statusCode: 400, message: response.statusText });
+ if (!response.ok) throw new HttpError({ statusCode: response.status, message: response.statusText });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (response.statusText !== "OK") throw new HttpError({ statusCode: 400, message: response.statusText });
if (!response.ok) throw new HttpError({ statusCode: response.status, message: response.statusText });
🤖 Prompt for AI Agents
In packages/app-store/salesforce/lib/CalendarService.ts at line 86, the code
incorrectly checks response.statusText against "OK" with wrong casing or format.
Fix this by validating the HTTP status code instead of statusText, for example,
check if response.status is 200 to confirm success, and throw the HttpError if
it is not 200.

@github-actions

Copy link
Copy Markdown
Contributor

This PR is being marked as stale due to inactivity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants