Skip to content

Comments

OAuth credential sync and app integration enhancements#7

Draft
ShashankFC wants to merge 1 commit intooauth-security-basefrom
oauth-security-enhanced
Draft

OAuth credential sync and app integration enhancements#7
ShashankFC wants to merge 1 commit intooauth-security-basefrom
oauth-security-enhanced

Conversation

@ShashankFC
Copy link
Collaborator

Test 8nn---nReplicated from ai-code-review-evaluation/cal.com-greptile#8

…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>
@github-actions
Copy link
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

@ShashankFC
Copy link
Collaborator Author

@claude

@claude
Copy link

claude bot commented Jan 7, 2026

Claude finished @ShashankFC's task in 2m 35s —— View job


Code Review Complete

Tasks

  • Read repository guidelines (CLAUDE.md)
  • Review PR changes and gather context
  • Analyze security implications of OAuth changes
  • Review new webhook endpoint
  • Review OAuth utility functions
  • Review CalendarService changes
  • Provide comprehensive feedback

Overview

This PR implements OAuth credential synchronization between Cal.com and self-hosted platforms. While the implementation adds useful functionality for self-hosters, there are critical security vulnerabilities and several code quality issues that must be addressed before merging.

Critical Security Issues

1. Missing HMAC Validation in Webhook Endpoint (Critical)

File: apps/web/pages/api/webhook/app-credential.ts:24-28

The webhook endpoint uses simple string comparison for authentication:

if (
  req.headers[process.env.CALCOM_WEBHOOK_HEADER_NAME || "calcom-webhook-secret"] !==
  process.env.CALCOM_WEBHOOK_SECRET
) {

Issues:

  • Vulnerable to timing attacks - use crypto.timingSafeEqual() instead
  • No request body validation/signing - attacker could replay modified requests
  • Should implement HMAC-based signature verification (similar to GitHub webhooks)

Fix timing attack vulnerability →

Add HMAC signature validation →

2. Missing Encryption Key Validation (Critical)

File: apps/web/pages/api/webhook/app-credential.ts:58

symmetricDecrypt(reqBody.keys, process.env.CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY || "")

Issue: If CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY is not set, it defaults to an empty string, which will fail silently or produce incorrect decryption results.

Recommendation: Add validation at application startup:

if (APP_CREDENTIAL_SHARING_ENABLED && !process.env.CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY) {
  throw new Error("CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY must be set when credential sharing is enabled");
}

Fix encryption key validation →

3. Missing HTTP Method Validation (High)

File: apps/web/pages/api/webhook/app-credential.ts:17

The webhook endpoint doesn't validate the HTTP method. It should only accept POST requests.

Add HTTP method validation →

4. Unvalidated External Endpoint Calls (High)

File: packages/app-store/_utils/oauth/refreshOAuthTokens.ts:8

const response = await fetch(process.env.CALCOM_CREDENTIAL_SYNC_ENDPOINT, {
  method: "POST",
  body: new URLSearchParams({
    calcomUserId: userId.toString(),
    appSlug,
  }),
});

Issues:

  • No timeout specified - could hang indefinitely
  • No error handling for network failures
  • No validation of response status or content
  • Missing Content-Type header
  • Credentials sent as URL-encoded form data (should use JSON)

Recommendation:

const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000);
try {
  const response = await fetch(process.env.CALCOM_CREDENTIAL_SYNC_ENDPOINT, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ calcomUserId: userId.toString(), appSlug }),
    signal: controller.signal,
  });
  if (!response.ok) {
    throw new Error(`Credential sync failed: ${response.status}`);
  }
  return response;
} finally {
  clearTimeout(timeout);
}

Fix external endpoint call issues →

High Priority Issues

5. Inconsistent Error Handling in parseRefreshTokenResponse (High)

File: packages/app-store/_utils/oauth/parseRefreshTokenResponse.ts:25-27

if (!refreshTokenResponse.data.refresh_token) {
  refreshTokenResponse.data.refresh_token = "refresh_token";
}

Issue: Setting a literal string "refresh_token" when none is provided is dangerous. This will cause token refresh to fail silently later.

Recommendation: Either throw an error or use the existing refresh token from the credential.

Fix refresh token fallback →

6. Missing Import in Salesforce CalendarService (High)

File: packages/app-store/salesforce/lib/CalendarService.ts:96

The code uses prisma but doesn't import it:

await prisma.credential.update({

Missing import: import prisma from "@calcom/prisma";

Add missing prisma import →

7. Invalid Zod Schema in parseRefreshTokenResponse (High)

File: packages/app-store/_utils/oauth/parseRefreshTokenResponse.ts:7-10

const minimumTokenResponseSchema = z.object({
  access_token: z.string(),
  [z.string().toString()]: z.number(),
  [z.string().optional().toString()]: z.unknown().optional(),
});

Issue: This is not valid Zod syntax. You cannot use dynamic keys with z.string().toString() in object definitions. This will cause runtime errors.

Correct approach:

const minimumTokenResponseSchema = z.object({
  access_token: z.string(),
}).catchall(z.unknown());

Or use .passthrough() to allow additional properties.

Fix invalid Zod schema →

Medium Priority Issues

8. PR Title Convention (Medium)

The PR title "OAuth credential sync and app integration enhancements" doesn't follow Conventional Commits. It should start with a type prefix like feat:, fix:, etc.

Recommendation: Change to: feat: OAuth credential sync and app integration enhancements

9. Incomplete createOAuthAppCredential Function (Medium)

File: packages/app-store/_utils/oauth/createOAuthAppCredential.ts:59

await throwIfNotHaveAdminAccessToTeam({ teamId: state?.teamId ?? null, userId });

This call happens AFTER the team check on line 30-46, and it's checking when state.teamId is null (user credential). The logic seems inverted - you're checking admin access for non-team credentials.

Likely intended logic:

  • Line 30-46: Handle team credentials (check membership)
  • Line 59+: Handle user credentials (no admin check needed)

Remove line 59 or clarify the intended logic.

Fix inverted logic in createOAuthAppCredential →

10. Missing Error Handling (Medium)

The refreshOAuthTokens function in packages/app-store/_utils/oauth/refreshOAuthTokens.ts doesn't handle fetch failures. If the external endpoint is down, the entire OAuth flow will fail without proper error messages.

11. Documentation (Medium)

The new environment variables in .env.example are well-documented, but there's no documentation about:

  • What the external sync endpoint should return
  • The expected request/response format
  • Security considerations for implementers
  • How to set up and test this feature

Consider adding a docs/credential-sync.md file.

Low Priority / Style Issues

12. Inconsistent Return Statements

Files like createOAuthAppCredential.ts have explicit return; statements (lines 56, 70) that aren't necessary.

13. Type Safety

Consider using stricter types instead of any in several places (e.g., parseRefreshTokenResponse.ts:13).

14. Missing Rate Limiting

The webhook endpoint has no rate limiting, making it vulnerable to DoS attacks.

Testing Recommendations

  1. Unit tests needed for:

    • Webhook authentication logic
    • Encryption/decryption with various inputs
    • Error cases in parseRefreshTokenResponse
    • Schema validation
  2. Integration tests needed for:

    • Full OAuth flow with credential sync enabled
    • Webhook endpoint with valid/invalid requests
    • Token refresh with external endpoint
  3. Security tests needed for:

    • Timing attack resistance
    • Invalid encryption keys
    • Malformed webhook requests
    • SSRF attempts on external endpoint URL

Summary

Must Fix Before Merge:

  1. ✅ Add timing-safe comparison for webhook secret
  2. ✅ Implement HMAC signature verification for webhooks
  3. ✅ Add encryption key validation
  4. ✅ Add HTTP method validation
  5. ✅ Fix Zod schema syntax error
  6. ✅ Add missing prisma import in Salesforce service
  7. ✅ Fix external endpoint call (timeout, error handling, validation)
  8. ✅ Fix refresh_token fallback logic
  9. ✅ Update PR title to follow Conventional Commits

Should Fix:

  • Add comprehensive error handling throughout
  • Add documentation for external sync endpoint format
  • Fix logic in createOAuthAppCredential
  • Add unit and integration tests

Nice to Have:

  • Rate limiting on webhook endpoint
  • Stricter TypeScript types
  • Performance monitoring for external calls

| Branch: oauth-security-enhanced

@github-actions
Copy link
Contributor

This PR is being marked as stale due to inactivity.

@github-actions github-actions bot added the Stale label Jan 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants