Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@steven-tey
Copy link
Collaborator

@steven-tey steven-tey commented Dec 11, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Fixed a typo in OAuth authorization validation to ensure consistent request validation.
  • Changes

    • Removed workspace read/write from OAuth permissions and from validation/description.
    • OAuth scope validation now accepts missing values, strips workspace.* scopes, and clarifies the invalid-scope message.
    • OAuth authorize flow no longer sends an explicit scope parameter.
    • Type annotations updated to use inferred schema types.
  • Chores

    • Added a maintenance script to remove workspace.* scopes from existing tokens.
  • Security

    • Stripe integration PATCH now requires token-based authorization checks.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Contributor

vercel bot commented Dec 11, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
dub Ready Ready Preview Dec 15, 2025 10:08am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Walkthrough

Renamed an OAuth validator, removed workspace-related OAuth scopes and filtered them during request validation, threaded a token through withWorkspace and a Stripe PATCH route with token-based checks, added a script to strip workspace scopes from tokens, adjusted OAuth URL scope usage, and fixed a few types/comments and UI text.

Changes

Cohort / File(s) Summary
OAuth validator rename
apps/web/lib/api/oauth/actions.ts, apps/web/app/app.dub.co/(auth)/oauth/authorize/page.tsx
Renamed exported function vaidateAuthorizeRequestvalidateAuthorizeRequest and updated import/call site.
Authorize form types
apps/web/app/app.dub.co/(auth)/oauth/authorize/authorize-form.tsx
Removed AuthorizeFormProps interface and inlined z.infer<typeof authorizeRequestSchema> into the AuthorizeForm props signature.
OAuth scope removals & schema change
apps/web/lib/api/oauth/constants.ts, apps/web/lib/zod/schemas/oauth.ts
Removed workspaces.read/workspaces.write from OAUTH_SCOPES and descriptions; changed scope from .nullable() to .nullish(), deduped/normalized scopes, filter out workspaces.* in transform, and updated error message.
Workspace auth token propagation
apps/web/lib/auth/workspace.ts
Added `token: TokenCacheItem
Stripe integration PATCH authorization
apps/web/app/(ee)/api/stripe/integration/route.ts
PATCH handler signature now receives token, fetches installation by token.installationId, validates installation.integrationId against STRIPE_INTEGRATION_ID, and removed requiredPermissions: ["workspaces.write"] from route config.
Remove workspace scopes script
apps/web/scripts/remove-workspace-scopes.ts
New script: finds restricted tokens with workspaces.* scopes (limit 100), strips those scopes, updates tokens (concurrently), and logs results.
Stripe app OAuth URL change
packages/stripe-app/src/utils/oauth.ts
Removed scope parameter when constructing the external OAuth URL.
Prisma schema comment update
packages/prisma/schema/token.prisma
Updated RestrictedToken.scopes example in comment to dot-based notation (comment-only).
UI text fix
apps/web/ui/partners/fraud-risks/fraud-disclaimer-banner.tsx
Minor copy edits: "We recommended" → "We recommend", "potentially reach out" → "potentially reaching out".

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Route as Stripe PATCH Route
  participant Auth as withWorkspace
  participant TokenStore as Token Cache / DB
  participant Install as Installations DB

  Client->>Route: PATCH /api/stripe/integration (req, session, token)
  Route->>Auth: enter withWorkspace(handler) with (req, session, token)
  Auth->>TokenStore: resolve token (if present)
  TokenStore-->>Auth: TokenCacheItem | null
  Auth->>Auth: include token in handler args
  Auth->>Install: fetch installation by token.installationId
  Install-->>Auth: installation
  Auth->>Route: invoke handler with token + installation
  Route->>Install: validate installation.integrationId == STRIPE_INTEGRATION_ID
  alt valid
    Route->>Install: perform update
    Install-->>Route: success
    Route-->>Client: 200 OK
  else invalid
    Route-->>Client: 403 Forbidden
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Check propagation and nullability of token in apps/web/lib/auth/workspace.ts.
  • Validate new token-based checks and error handling in apps/web/app/(ee)/api/stripe/integration/route.ts.
  • Confirm zod transform/filter behavior in apps/web/lib/zod/schemas/oauth.ts (scope dedupe/filter).
  • Review apps/web/scripts/remove-workspace-scopes.ts for DB update safety and concurrency behavior.

Possibly related PRs

Suggested reviewers

  • steven-tey

Poem

"I nibble at a typo, hop and pry, 🥕
I peel old scopes from tokens nigh,
A validator's name made right,
Tokens threaded through the night,
Hops and bytes — the code feels light!" 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Simplify OAuth scopes' directly and accurately reflects the main changes in the pull request, which involve removing workspace scopes and simplifying OAuth scope handling across multiple files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch simplify-oauth

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b016815 and ef0b0e0.

📒 Files selected for processing (11)
  • apps/web/app/(ee)/api/stripe/integration/route.ts (1 hunks)
  • apps/web/app/app.dub.co/(auth)/oauth/authorize/authorize-form.tsx (1 hunks)
  • apps/web/app/app.dub.co/(auth)/oauth/authorize/page.tsx (2 hunks)
  • apps/web/lib/api/oauth/actions.ts (1 hunks)
  • apps/web/lib/api/oauth/constants.ts (0 hunks)
  • apps/web/lib/auth/workspace.ts (3 hunks)
  • apps/web/lib/zod/schemas/oauth.ts (1 hunks)
  • apps/web/scripts/remove-workspace-scopes.ts (1 hunks)
  • apps/web/ui/partners/fraud-risks/fraud-disclaimer-banner.tsx (1 hunks)
  • packages/prisma/schema/token.prisma (1 hunks)
  • packages/stripe-app/src/utils/oauth.ts (0 hunks)
💤 Files with no reviewable changes (2)
  • packages/stripe-app/src/utils/oauth.ts
  • apps/web/lib/api/oauth/constants.ts
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-09-17T02:53:28.359Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2839
File: apps/web/lib/integrations/hubspot/schema.ts:5-12
Timestamp: 2025-09-17T02:53:28.359Z
Learning: HubSpot's OAuth token response returns `scopes` as an array of strings, not as a space-delimited string. The schema `scopes: z.array(z.string())` in hubSpotAuthTokenSchema is correct for HubSpot's actual API response format.

Applied to files:

  • apps/web/lib/zod/schemas/oauth.ts
  • packages/prisma/schema/token.prisma
📚 Learning: 2025-10-17T08:18:19.278Z
Learnt from: devkiran
Repo: dubinc/dub PR: 0
File: :0-0
Timestamp: 2025-10-17T08:18:19.278Z
Learning: In the apps/web codebase, `@/lib/zod` should only be used for places that need OpenAPI extended zod schema. All other places should import from the standard `zod` package directly using `import { z } from "zod"`.

Applied to files:

  • apps/web/lib/zod/schemas/oauth.ts
📚 Learning: 2025-10-15T01:05:43.266Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx:432-457
Timestamp: 2025-10-15T01:05:43.266Z
Learning: In apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx, defer refactoring the custom MenuItem component (lines 432-457) to use the shared dub/ui MenuItem component to a future PR, as requested by steven-tey.

Applied to files:

  • apps/web/app/app.dub.co/(auth)/oauth/authorize/authorize-form.tsx
📚 Learning: 2025-06-04T15:09:51.562Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2471
File: apps/web/ui/auth/reset-password-form.tsx:52-65
Timestamp: 2025-06-04T15:09:51.562Z
Learning: In the Dub codebase, server-side validation errors for password fields are handled via toast notifications rather than using react-hook-form's setError method to display field-specific errors. This architectural pattern separates client-side validation feedback (inline) from server-side error handling (toast).

Applied to files:

  • apps/web/app/app.dub.co/(auth)/oauth/authorize/authorize-form.tsx
📚 Learning: 2025-12-08T09:44:28.429Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3200
File: apps/web/lib/api/fraud/detect-duplicate-payout-method-fraud.ts:55-73
Timestamp: 2025-12-08T09:44:28.429Z
Learning: In apps/web/lib/api/fraud/detect-duplicate-payout-method-fraud.ts, the fraud event creation logic intentionally generates self-referential fraud events (where partnerId equals duplicatePartnerId) for partners with duplicate payout methods. This is by design to create raw events for all partners involved in a duplicate payout method scenario, regardless of whether they reference themselves.

Applied to files:

  • apps/web/ui/partners/fraud-risks/fraud-disclaimer-banner.tsx
🧬 Code graph analysis (4)
apps/web/lib/auth/workspace.ts (1)
apps/web/lib/auth/token-cache.ts (1)
  • TokenCacheItem (25-25)
apps/web/app/app.dub.co/(auth)/oauth/authorize/authorize-form.tsx (1)
apps/web/lib/zod/schemas/oauth.ts (1)
  • authorizeRequestSchema (42-77)
apps/web/app/(ee)/api/stripe/integration/route.ts (2)
apps/web/lib/api/errors.ts (1)
  • DubApiError (58-75)
packages/prisma/index.ts (1)
  • prisma (3-9)
apps/web/app/app.dub.co/(auth)/oauth/authorize/page.tsx (1)
apps/web/lib/api/oauth/actions.ts (1)
  • validateAuthorizeRequest (7-58)
🔇 Additional comments (11)
apps/web/ui/partners/fraud-risks/fraud-disclaimer-banner.tsx (1)

14-15: LGTM: Grammatical corrections improve UI copy.

The text updates fix grammar ("We recommend" vs "We recommended") and improve parallelism ("reaching out" to match "reviewing").

packages/prisma/schema/token.prisma (1)

21-21: LGTM: Comment updated to reflect actual scope format.

The example now uses dot notation ("links.write domains.read") instead of colon notation, aligning with the actual format used throughout the codebase.

apps/web/lib/api/oauth/actions.ts (1)

7-7: LGTM: Typo fixed in function name.

The function name is corrected from vaidateAuthorizeRequest to validateAuthorizeRequest, with usage sites updated accordingly.

apps/web/scripts/remove-workspace-scopes.ts (1)

27-45: LGTM: Scope filtering and update logic is correct.

The logic correctly:

  • Filters out all scopes starting with "workspaces."
  • Sets scopes to null when empty after filtering
  • Uses Promise.allSettled for safe concurrent updates
apps/web/lib/zod/schemas/oauth.ts (2)

51-54: LGTM: Schema updated to handle null and undefined scopes.

The change from .nullable() to .nullish() allows both null and undefined to pass validation. The const to let change at Line 54 is necessary since scopes is reassigned during filtering at Line 63.


60-68: LGTM: Temporary workaround correctly filters deprecated workspace scopes.

The filtering of workspaces.* scopes prevents validation errors during the transition period. The comment appropriately notes this is temporary until existing integrations are updated. The improved error message provides clearer feedback.

apps/web/app/app.dub.co/(auth)/oauth/authorize/page.tsx (1)

1-1: LGTM: Import and usage updated for corrected function name.

The changes correctly reflect the function rename from vaidateAuthorizeRequest to validateAuthorizeRequest.

Also applies to: 32-32

apps/web/lib/auth/workspace.ts (1)

44-44: LGTM: Token correctly threaded through workspace middleware.

The token parameter is properly added to the handler interface (Line 44), typed as TokenCacheItem | null (Line 53), and passed to the final handler invocation (Line 469). The token is populated from the API key authentication flow and remains null for session-based requests.

Also applies to: 53-53, 469-469

apps/web/app/(ee)/api/stripe/integration/route.ts (2)

27-48: LGTM: Token-based authorization correctly validates Stripe integration access.

The new authorization checks properly:

  • Require a token with an installationId (Line 27)
  • Fetch and validate the installation belongs to the Stripe integration (Lines 34-48)
  • Throw appropriate forbidden errors when checks fail

This provides more granular, integration-specific authorization compared to workspace-level permissions.


118-127: The removal of requiredPermissions: ["workspaces.write"] is intentional and properly secured.

The endpoint now relies on specific token-based checks (lines 27-48) that verify the token is for an authorized Stripe installation. These checks—validating token.installationId and confirming the integration is STRIPE_INTEGRATION_ID—provide equivalent or stronger authorization than the broad workspaces.write permission and align with the OAuth scope simplification pattern.

apps/web/app/app.dub.co/(auth)/oauth/authorize/authorize-form.tsx (1)

13-21: Inline z.infer<typeof authorizeRequestSchema> keeps props in sync and removes dead indirection (LGTM).

Switching from a separate AuthorizeFormProps interface to an inline z.infer<typeof authorizeRequestSchema> is type‑equivalent and simplifies the typing surface without changing runtime behavior; just ensure callers continue to pass data shaped by authorizeRequestSchema (i.e., with scope as the transformed string[]), not raw query/search params.


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.

@steven-tey
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

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

@steven-tey
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

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

@steven-tey steven-tey marked this pull request as draft December 11, 2025 05:25
@steven-tey steven-tey changed the title Simplify OAuth scopes (remove workspace resource) Simplify OAuth scopes Dec 11, 2025
….write' instead of 'workspaces.write'; add script to remove deprecated workspace scopes from tokens.
…on checks; remove 'workspaces.settings.write' scope from various files to streamline permissions.
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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/app/(ee)/api/stripe/integration/route.ts (1)

118-128: Apply consistent token-based authorization to Shopify integration endpoint.

The Stripe integration route correctly implements token-based authorization requiring a valid Stripe installationId (lines 27-48), replacing the broader requiredPermissions: ["workspaces.write"] check. This is more secure and appropriate for integration-specific endpoints.

However, the Shopify integration callback route (apps/web/app/(ee)/api/shopify/integration/callback/route.ts, line 88) still uses requiredPermissions: ["workspaces.write"] without the corresponding token-based check. Both endpoints perform similar operations (updating workspace integration configuration) and should follow the same authorization pattern.

Update Shopify integration callback to use the same token-based installationId validation as Stripe, or document why they differ.

🧹 Nitpick comments (1)
apps/web/lib/zod/schemas/oauth.ts (1)

60-64: Track removal of workspace scope filtering as technical debt.

The workspace scope filtering is a temporary workaround to prevent validation errors during the migration period. Consider creating a tracking issue to remove this filter once all existing integrations have been updated.

Would you like me to open an issue to track the removal of this workaround after the migration is complete?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c49f03a and 2f4d0fb.

📒 Files selected for processing (6)
  • apps/web/app/(ee)/api/stripe/integration/route.ts (1 hunks)
  • apps/web/lib/auth/workspace.ts (3 hunks)
  • apps/web/lib/zod/schemas/oauth.ts (1 hunks)
  • apps/web/scripts/remove-workspace-scopes.ts (1 hunks)
  • packages/prisma/schema/token.prisma (1 hunks)
  • packages/stripe-app/src/utils/oauth.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • packages/stripe-app/src/utils/oauth.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/prisma/schema/token.prisma
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-17T02:53:28.359Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2839
File: apps/web/lib/integrations/hubspot/schema.ts:5-12
Timestamp: 2025-09-17T02:53:28.359Z
Learning: HubSpot's OAuth token response returns `scopes` as an array of strings, not as a space-delimited string. The schema `scopes: z.array(z.string())` in hubSpotAuthTokenSchema is correct for HubSpot's actual API response format.

Applied to files:

  • apps/web/lib/zod/schemas/oauth.ts
📚 Learning: 2025-10-17T08:18:19.278Z
Learnt from: devkiran
Repo: dubinc/dub PR: 0
File: :0-0
Timestamp: 2025-10-17T08:18:19.278Z
Learning: In the apps/web codebase, `@/lib/zod` should only be used for places that need OpenAPI extended zod schema. All other places should import from the standard `zod` package directly using `import { z } from "zod"`.

Applied to files:

  • apps/web/lib/zod/schemas/oauth.ts
🧬 Code graph analysis (4)
apps/web/lib/auth/workspace.ts (1)
apps/web/lib/auth/token-cache.ts (1)
  • TokenCacheItem (25-25)
apps/web/scripts/remove-workspace-scopes.ts (1)
packages/prisma/index.ts (1)
  • prisma (3-9)
apps/web/app/(ee)/api/stripe/integration/route.ts (2)
apps/web/lib/api/errors.ts (1)
  • DubApiError (58-75)
packages/prisma/index.ts (1)
  • prisma (3-9)
apps/web/lib/zod/schemas/oauth.ts (1)
apps/web/lib/api/oauth/constants.ts (1)
  • OAUTH_SCOPES (21-34)
🔇 Additional comments (5)
apps/web/scripts/remove-workspace-scopes.ts (1)

25-46: LGTM! Good error handling and edge case management.

The scope filtering logic correctly:

  • Handles null scopes (Line 28)
  • Removes all workspaces.* scopes using startsWith check
  • Sets scopes to null when no scopes remain
  • Uses Promise.allSettled to handle failures gracefully
apps/web/lib/zod/schemas/oauth.ts (2)

49-51: LGTM! More correct handling of optional scope parameter.

Changing from nullable() to nullish() properly handles cases where the scope parameter is omitted entirely (undefined) in addition to being explicitly null. This aligns better with OAuth 2.0 specification where scope is an optional parameter.


67-69: LGTM! Improved error message clarity.

The updated error message "One or more provided OAuth scopes are invalid or unsupported." is more informative than the previous "Invalid scopes" and helps developers understand the validation failure.

apps/web/lib/auth/workspace.ts (1)

35-55: LGTM! Clean token propagation through handler chain.

The changes correctly thread the token parameter through the workspace authentication flow:

  • Declared at line 116 with proper type TokenCacheItem | null
  • Populated during token authentication (existing logic)
  • Passed to downstream handler at line 469

This enables downstream handlers to access token information for authorization decisions, such as the Stripe integration route that validates installation IDs.

Also applies to: 116-116, 461-470

apps/web/app/(ee)/api/stripe/integration/route.ts (1)

27-48: LGTM! Defense-in-depth token validation.

The token-based installation validation correctly:

  1. Verifies the request has a token with an installation ID (lines 27-32)
  2. Confirms the installation exists and belongs to Stripe integration (lines 34-48)
  3. Returns clear forbidden errors for unauthorized access

This ensures only requests from the Stripe integration can update the Stripe Connect account, preventing misuse from other integrations or generic workspace tokens.

@devkiran
Copy link
Collaborator

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

✅ Actions performed

Full review triggered.

@steven-tey steven-tey merged commit 1117445 into main Dec 15, 2025
7 of 8 checks passed
@steven-tey steven-tey deleted the simplify-oauth branch December 15, 2025 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants