AI and data stack advisories

Severe, 6 weeks1498Projects326

1498 severe, 6 weeks · 326 projects

FlowiseGHSA-wch5-xp77-fxg4

Flowise: Cross-Workspace OAuth2 Credential Metadata Leak

Flowise

CVE-2026-70474 · Published Aug 4, 2026

High
Fix: upgrade to 3.1.3 or later
GitHub advisory

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

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