Skip to content

fix(external-apps): harden OAuth authorization attempts - #14147

Open
wenxi-onyx wants to merge 1 commit into
whuang/oauth-authorization-consolidationfrom
whuang/external-app-oauth-attempts
Open

fix(external-apps): harden OAuth authorization attempts#14147
wenxi-onyx wants to merge 1 commit into
whuang/oauth-authorization-consolidationfrom
whuang/external-app-oauth-attempts

Conversation

@wenxi-onyx

@wenxi-onyx wenxi-onyx commented Aug 21, 2026

Copy link
Copy Markdown
Member

Description

Craft external-app OAuth previously maintained its own Redis state record and deleted it only after token exchange. A concurrent callback could therefore reuse the same state, and this flow had a separate lifecycle from the connector and MCP OAuth implementations.

This change moves external apps onto the shared, tenant-aware authorization-attempt store introduced by the base PR. The feature now declares its namespace and payload type once; shared cache resolution, TTL, fingerprint validation, and PKCE-verifier constraints are not reimplemented locally. Each attempt is bound to the initiating user and claimed atomically before the upstream token request. The callback also rejects attempts when the app or OAuth client configuration changed while authorization was pending.

PKCE verifiers remain server-side. GitHub and Linear use the S256 flow, while providers without PKCE support preserve their existing exchange behavior. Provider-specific request construction, fingerprint inputs, token interpretation, credential persistence, and skill updates remain in the external-app domain.

How Has This Been Tested?

Additional Options

  • [Optional] Please cherry-pick this PR to the latest release version.
  • [Optional] Override Linear Check

@wenxi-onyx
wenxi-onyx requested a review from a team as a code owner August 21, 2026 17:21
@greptile-apps

greptile-apps Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR migrates external-app OAuth state to the shared tenant-aware authorization-attempt store and hardens callbacks with owner binding, atomic one-shot consumption, and configuration fingerprint validation.

  • Adds server-side PKCE for GitHub and Linear while preserving provider-specific token request construction.
  • Rejects callbacks when app credentials or OAuth configuration change during authorization.
  • Expands unit coverage for overlapping attempts, owner isolation, failed exchanges, configuration changes, and non-PKCE providers.

Confidence Score: 5/5

The PR appears safe to merge with no concrete correctness or security defects identified.

The shared attempt store preserves tenant-aware backend selection and atomic owner-bound state handling, while provider-specific PKCE behavior and configuration-change rejection are consistently wired and tested.

Important Files Changed

Filename Overview
backend/onyx/server/features/build/external_apps/oauth.py Replaces custom Redis OAuth state with tenant-aware, owner-bound authorization attempts, atomic consumption, configuration fingerprinting, and optional PKCE.
backend/onyx/external_apps/providers/base.py Extends OAuth provider metadata and token-exchange construction with opt-in PKCE support.
backend/onyx/external_apps/providers/github.py Enables S256 PKCE for GitHub OAuth.
backend/onyx/external_apps/providers/linear.py Enables S256 PKCE for Linear OAuth.
backend/onyx/external_apps/providers/notion.py Updates Notion’s provider-specific token-exchange override to remain compatible with the extended base interface while rejecting unexpected PKCE input.
backend/tests/unit/external_apps/test_oauth_flow.py Covers overlapping PKCE attempts, ownership isolation, one-shot consumption, configuration changes, and provider-specific exchange behavior.
backend/tests/unit/external_apps/test_hubspot_provider.py Adapts HubSpot authorization tests to the shared cache-backed attempt flow and verifies that PKCE remains disabled.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant API as Onyx API
    participant Store as Authorization Attempt Store
    participant P as OAuth Provider
    U->>API: Start external-app OAuth
    API->>API: Generate PKCE pair when supported
    API->>API: Fingerprint OAuth configuration
    API->>Store: Store owner-bound attempt
    API-->>U: Authorization URL with state and challenge
    U->>P: Authorize application
    P-->>U: Redirect with code and state
    U->>API: Callback with code and state
    API->>Store: Atomically consume owner-bound attempt
    API->>API: Validate current configuration fingerprint
    API->>P: Exchange code and server-side verifier
    P-->>API: OAuth credentials
    API->>API: Persist user credentials and refresh skills
    API-->>U: Success
Loading

Reviews (1): Last reviewed commit: "fix(external-apps): harden OAuth authori..." | Re-trigger Greptile

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 7 files

Confidence score: 4/5

  • backend/tests/unit/external_apps/test_oauth_flow.py uses ExternalAppType.LINEAR, whose provider supports PKCE, so these tests do not validate the claimed non-PKCE paths in start_external_app_oauth and the callback; add a fixture or provider configuration with supports_pkce=False to cover that branch.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/tests/unit/external_apps/test_oauth_flow.py">

<violation number="1" location="backend/tests/unit/external_apps/test_oauth_flow.py:39">
P3: The PR description says these tests exercise non-PKCE provider behavior, but every test here uses ExternalAppType.LINEAR, whose provider has supports_pkce=True. The non-PKCE branch in start_external_app_oauth/callback (no code_challenge/verifier, payload.code_verifier=None) is left untested. If that coverage lives in another file of this PR, note it here; otherwise add a test with a non-PKCE OAuth provider asserting the authorize URL omits code_challenge/code_challenge_method and the callback sends no code_verifier.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

id=7,
name="Linear",
enabled=True,
app_type=ExternalAppType.LINEAR,

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.

P3: The PR description says these tests exercise non-PKCE provider behavior, but every test here uses ExternalAppType.LINEAR, whose provider has supports_pkce=True. The non-PKCE branch in start_external_app_oauth/callback (no code_challenge/verifier, payload.code_verifier=None) is left untested. If that coverage lives in another file of this PR, note it here; otherwise add a test with a non-PKCE OAuth provider asserting the authorize URL omits code_challenge/code_challenge_method and the callback sends no code_verifier.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/tests/unit/external_apps/test_oauth_flow.py, line 39:

<comment>The PR description says these tests exercise non-PKCE provider behavior, but every test here uses ExternalAppType.LINEAR, whose provider has supports_pkce=True. The non-PKCE branch in start_external_app_oauth/callback (no code_challenge/verifier, payload.code_verifier=None) is left untested. If that coverage lives in another file of this PR, note it here; otherwise add a test with a non-PKCE OAuth provider asserting the authorize URL omits code_challenge/code_challenge_method and the callback sends no code_verifier.</comment>

<file context>
@@ -0,0 +1,192 @@
+            id=7,
+            name="Linear",
+            enabled=True,
+            app_type=ExternalAppType.LINEAR,
+            organization_credentials=credentials,
+        ),
</file context>

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

🖼️ Visual Regression Report

Project Changed Added Removed Unchanged Report
admin 0 187 0 0 View Report
exclusive 0 10 0 0 View Report

@wenxi-onyx
wenxi-onyx force-pushed the whuang/external-app-oauth-attempts branch from 253567a to dfafe8e Compare August 22, 2026 00:04
@wenxi-onyx
wenxi-onyx changed the base branch from main to whuang/oauth-authorization-consolidation August 22, 2026 00:04
@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Full-stack Preview (frontend + backend)

Status Preview Commit Updated
https://b140fcf-onyx.preview.onyxcorp.dev/ b140fcf 2026-08-22 00:24:39 UTC

Sign in with GitHub as an onyx-dot-app member to view it.

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Status Preview Commit Updated
https://onyx-preview-74zhfka3o-danswer.vercel.app dfafe8e 2026-08-22 00:08:46 UTC

@wenxi-onyx
wenxi-onyx force-pushed the whuang/external-app-oauth-attempts branch from 7c6e8b2 to b140fcf Compare August 22, 2026 00:21
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.

1 participant