Skip to content
FlowiseGHSA-wch5-xp77-fxg4

Flowise: Cross-Workspace OAuth2 Credential Metadata Leak

HighCVE-2026-70474 · Published Aug 4, 2026

## Summary Three OAuth2 credential endpoints look up credentials by `id` alone with no `workspaceId` filter. Two of these endpoints (`callback`, `refresh`) are whitelisted from all authentication. This allows: 1. **Cross-workspace credential access** , Any authenticated user can initiate OAuth2 flows against credentials belonging to other workspaces. 2. **Unauthenticated token injection** , An unauthenticated attacker can forge OAuth2 callbacks to overwrite tokens in any credential. 3. **Unauthenticated token refresh** , An unauthenticated attacker can refresh tokens for any credential. --- ## Root Cause ### Vulnerable code: no workspace scoping All three OAuth2 handlers query the `Credential` table by `id` only: **`packages/server/src/routes/oauth2/index.ts:80-82`** (authorize) ```typescript const credential = await credentialRepository.findOneBy({ id: credentialId // Missing: workspaceId filter }) ``` **`packages/server/src/routes/oauth2/index.ts:183-185`** (callback) ```typescript const credential = await credentialRepository.findOneBy({ id: state as string // Missing: workspaceId filter }) ``` **`packages/server/src/routes/oauth2/index.ts:314-316`** ...

GitHub advisory

Affected versions

PackageAffectedFixed in
flowise
npm
< 3.1.33.1.3
Details and references

## Summary Three OAuth2 credential endpoints look up credentials by `id` alone with no `workspaceId` filter. Two of these endpoints (`callback`, `refresh`) are whitelisted from all authentication. This allows: 1. **Cross-workspace credential access** , Any authenticated user can initiate OAuth2 flows against credentials belonging to other workspaces. 2. **Unauthenticated token injection** , An unauthenticated attacker can forge OAuth2 callbacks to overwrite tokens in any credential. 3. **Unauthenticated token refresh** , An unauthenticated attacker can refresh tokens for any credential. --- ## Root Cause ### Vulnerable code: no workspace scoping All three OAuth2 handlers query the `Credential` table by `id` only: **`packages/server/src/routes/oauth2/index.ts:80-82`** (authorize) ```typescript const credential = await credentialRepository.findOneBy({ id: credentialId // Missing: workspaceId filter }) ``` **`packages/server/src/routes/oauth2/index.ts:183-185`** (callback) ```typescript const credential = await credentialRepository.findOneBy({ id: state as string // Missing: workspaceId filter }) ``` **`packages/server/src/routes/oauth2/index.ts:314-316`** (refresh) ```typescript const credential = await credentialRepository.findOneBy({ id: credentialId // Missing: workspaceId filter }) ``` ### Correct pattern (same codebase) The standard credential service correctly enforces workspace isolation: **`packages/server/src/services/credentials/index.ts:130-132`** ```typescript const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ id: credentialId, workspaceId: workspaceId // <-- Workspace scoping present }) ``` ### Authentication bypass via whitelist **`packages/server/src/utils/constants.ts:40-41`** ```typescript export const WHITELIST_URLS = [ // ... '/api/v1/oauth2-credential/callback', // line 40 '/api/v1/oauth2-credential/refresh', // line 41 // ... ] ``` **`packages/server/src/index.ts:223-225`** , prefix-matched whitelist skips all auth: ```typescript const isWhitelisted = whitelistURLs.some((url) => req.path.startsWith(url)) if (isWhitelisted) { next() // No JWT verification, no API key check } ``` --- ## Attack Scenarios ### Scenario A: Cross-Workspace Credential Metadata Leak An authenticated user in Workspace A initiates an OAuth2 authorize flow for a credential belonging to Workspace B. The server returns an authorization URL containing the victim credential's `client_id`, `scope`, and `redirect_uri`. ``` POST /api/v1/oauth2-credential/authorize/<VICTIM_CREDENTIAL_UUID> Cookie: connect.sid=<ATTACKER_SESSION> ``` **Response:** ```json { "success": true, "credentialId": "<VICTIM_CREDENTIAL_UUID>", "authorizationUrl": "https://provider.com/oauth2/authorize?client_id=LEAKED_CLIENT_ID&scope=LEAKED_SCOPE&...", "redirectUri": "https://flowise-instance/api/v1/oauth2-credential/callback" } ``` ### Scenario B: Unauthenticated Token Injection via Forged Callback The callback endpoint requires no authentication and uses the `state` parameter as the credential lookup key. An attacker who controls an OAuth2 provider (or MitMs the flow) can inject arbitrary tokens into any credential. ``` GET /api/v1/oauth2-credential/callback?code=ATTACKER_AUTH_CODE&state=<VICTIM_CREDENTIAL_UUID> (No authentication required) ``` The server exchanges the code at the credential's `accessTokenUrl`, and whatever tokens the provider returns are encrypted and stored into the victim's credential record (line 271): ```typescript await credentialRepository.update(credential.id, { encryptedData, // Contains attacker-controlled token data updatedDate: new Date() }) ``` ### Scenario C: Unauthenticated Token Refresh An attacker can refresh any credential's OAuth2 tokens without authentication. The server reads the stored `refresh_token`, exchanges it at the `accessTokenUrl`, and returns fresh token metadata. ``` POST /ap

CVSS 4.0
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N
Severity from
GitHub (reviewed advisory)
Weakness
CWE-863
Also known as
CVE-2026-70474

More Flowise advisories

All Flowise

Critical advisories by email

Wednesdays: the week’s critical and high advisories in the AI and data stack, with the fixed versions. Only in weeks that have some.

Double opt-in. Unsubscribe any time.