Skip to content

Fix dev proxy auth header forwarding in federated modules#7065

Merged
openshift-merge-bot[bot] merged 2 commits intoopendatahub-io:mainfrom
mturley:fix/dev-proxy-forward-auth-headers
Apr 27, 2026
Merged

Fix dev proxy auth header forwarding in federated modules#7065
openshift-merge-bot[bot] merged 2 commits intoopendatahub-io:mainfrom
mturley:fix/dev-proxy-forward-auth-headers

Conversation

@mturley
Copy link
Copy Markdown
Contributor

@mturley mturley commented Apr 3, 2026

https://issues.redhat.com/browse/RHOAIENG-56705

Description

Each federated module's webpack dev server captures the kubeconfig/oc token at startup as static proxy headers. This overwrites any dynamically forwarded auth headers from the host backend proxy, breaking dev impersonation (DEV_IMPERSONATE_USER/DEV_IMPERSONATE_PASSWORD).

This PR replaces the static headers approach with an onProxyReq callback that dynamically forwards the Authorization header from each incoming request when present, falling back to the startup-captured token for standalone dev mode.

Modules fixed:

  • gen-ai (uses oc whoami)
  • maas
  • automl
  • autorag
  • eval-hub (both proxy entries)
  • mlflow

Not included:

How Has This Been Tested?

Tested manually in the model-registry module (upstream fix) against an ODH cluster:

  1. Started the host backend with DEV_IMPERSONATE_USER and DEV_IMPERSONATE_PASSWORD set
  2. Verified the bug: with static headers, the impersonated user's token was overwritten by the startup token
  3. Verified the fix: with onProxyReq, the incoming auth header from the host proxy is forwarded correctly, and impersonation works as expected

The same pattern is applied consistently to all other modules.

Test Impact

This is a dev-only webpack config change (not shipped in production builds). No automated tests are applicable — webpack dev server proxy configuration is validated through manual dev workflow testing.

Request review criteria:

Self checklist (all need to be checked):

  • The developer has manually tested the changes and verified that the changes work
  • Testing instructions have been added in the PR body (for PRs involving changes that are not immediately obvious).
  • The developer has added tests or explained why testing cannot be added (unit or cypress tests for related changes)
  • The code follows our Best Practices (React coding standards, PatternFly usage, performance considerations)

After the PR is posted & before it merges:

  • The developer has tested their solution on a cluster by using the image produced by the PR to main

mturley and others added 2 commits April 3, 2026 15:02
Each federated module's webpack dev server captured the kubeconfig token
at startup and set it as static proxy headers, overwriting any auth
headers forwarded by the host backend proxy (e.g. dev impersonation).

Switch all modules to an onProxyReq callback that dynamically forwards
incoming authorization headers when present and falls back to the
startup token for standalone dev mode.

Fixes: RHOAIENG-56705

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mike Turley <mike.turley@alum.cs.umass.edu>
…rst)

The notebooks/upstream/ directory mirrors kubeflow/notebooks and should
be fixed upstream first, similar to model-registry.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mike Turley <mike.turley@alum.cs.umass.edu>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 3, 2026

📝 Walkthrough

Walkthrough

Six webpack configuration files across packages (automl, autorag, eval-hub, gen-ai, maas, mlflow) were refactored to change proxy authentication handling. The changes move Kubernetes/OpenShift token retrieval from synchronous execution during config initialization into a startup capture phase via new helper functions (getKubeconfigToken() or getOcToken()), storing the result as fallbackToken when AUTH_METHOD === 'user_token'. A new onProxyReq hook was added to the devServer proxy configuration that dynamically forwards incoming request Authorization headers to proxied requests, falling back to the previously captured fallbackToken when no incoming authorization is present. The static headers configuration now returns empty objects or only internal auth method headers from getProxyHeaders().

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes


Security & Implementation Concerns

CWE-522 (Insufficiently Protected Credentials): The fallbackToken variable is captured at startup and persists in memory throughout the dev server lifetime. Unlike per-request token injection from incoming headers, this creates a window where a compromised dev server process exposes a static, long-lived token. Ensure fallback tokens are never logged, printed to console, or accessible via introspection.

CWE-347 (Improper Verification of Cryptographic Signature): No validation of the incoming Authorization header format before forwarding. The code extracts bearer tokens via string manipulation (e.g., token.replace('Bearer ', '') patterns) without verifying the header conforms to RFC 6750. Malformed or oversized tokens could bypass downstream validators.

Token Leakage via Proxy Logs: Forwarding raw Authorization headers through the proxy may write bearer tokens to proxy middleware logs, error messages, or debug output. Verify that http-proxy-middleware is configured with logLevel: 'silent' or equivalent for sensitive paths, and that error handlers don't echo headers.

Inconsistent Token Extraction: The gen-ai package uses oc whoami while others use kubectl config view. Verify that both command fallbacks (returning '' on failure) are handled identically across all files—particularly in error states where fallbackToken is empty and onProxyReq might attempt to set an empty Authorization: Bearer header.

Missing Authorization Header Validation: No check for whether req.headers.authorization exists or is a string before calling .substring() or .replace() on it. Add explicit type/existence guards to prevent runtime errors on malformed incoming requests.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix dev proxy auth header forwarding in federated modules' directly describes the main change: replacing static proxy headers with dynamic forwarding via onProxyReq callback across federated modules.
Description check ✅ Passed PR description is comprehensive and addresses all template sections with clear issue reference, detailed explanation of the problem and solution, manual testing methodology, and complete self-checklist.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

Copy link
Copy Markdown
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: 6

Caution

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

⚠️ Outside diff range comments (1)
packages/gen-ai/frontend/config/webpack.dev.js (1)

91-101: ⚠️ Potential issue | 🟠 Major

Add getProxyHeaders() support for internal auth mode.

The proxy config at lines 91–101 lacks the headers property used by maas, eval-hub, automl, autorag, and other packages to handle AUTH_METHOD === 'internal'. Without it, dev mode fails to forward the required kubeflow-userid header when internal auth is configured.

Add getProxyHeaders() function and include headers: getProxyHeaders() in the proxy config:

Example (from maas webpack.dev.js)
const getProxyHeaders = () => {
  if (AUTH_METHOD === 'internal') {
    return {
      'kubeflow-userid': 'user@example.com',
    };
  }
  return {};
};

// In proxy config:
proxy: [
  {
    context: ['/api', '/gen-ai/api'],
    target: { ... },
    changeOrigin: true,
    headers: getProxyHeaders(),
    onProxyReq,
  },
]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/gen-ai/frontend/config/webpack.dev.js` around lines 91 - 101, The
proxy config is missing headers needed for AUTH_METHOD === 'internal'; add a
getProxyHeaders() helper that returns { 'kubeflow-userid': 'user@example.com' }
when AUTH_METHOD === 'internal' (and {} otherwise), then include headers:
getProxyHeaders() in the proxy object alongside context, target, changeOrigin
and onProxyReq so the dev server forwards the internal auth header; reference
AUTH_METHOD, getProxyHeaders(), and the proxy config entry that uses
PROXY_HOST/PROXY_PORT/PROXY_PROTOCOL and onProxyReq.
🧹 Nitpick comments (2)
packages/maas/frontend/config/webpack.dev.js (1)

31-48: Significant code duplication across packages.

This getKubeconfigToken() implementation is nearly identical across 5 package webpack configs. Extract to a shared utility (e.g., @coderabbit/shared/webpack-utils) to reduce maintenance burden and drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/maas/frontend/config/webpack.dev.js` around lines 31 - 48, The
getKubeconfigToken() function is duplicated across multiple webpack configs;
extract it into a shared utility module (e.g., export a function from
`@coderabbit/shared/webpack-utils` named getKubeconfigToken) and replace the local
implementations in packages/maas/frontend/config/webpack.dev.js (and the other 4
configs) with an import from that new module; ensure the shared function
preserves the same behavior (uses execSync, console.info on username,
console.error on failure, and returns the token or empty string) and update
imports in each webpack config to reference the new shared symbol.
packages/automl/frontend/config/webpack.dev.js (1)

107-114: Minor inconsistency: target format differs from other packages.

This uses a template string ${PROXY_PROTOCOL}://${PROXY_HOST}:${PROXY_PORT} while other packages (maas, mlflow, eval-hub, autorag) use an object { host, protocol, port }. Both work, but object format is more consistent across the codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/automl/frontend/config/webpack.dev.js` around lines 107 - 114, The
proxy target in the webpack dev config uses a template string; replace it with
the object-form target used elsewhere for consistency: change the proxy entry in
the proxy array (the object with context ['/api','/automl/api'], changeOrigin,
headers: getProxyHeaders(), onProxyReq) to supply target as { protocol:
PROXY_PROTOCOL, host: PROXY_HOST, port: PROXY_PORT } instead of
`${PROXY_PROTOCOL}://${PROXY_HOST}:${PROXY_PORT}` so the proxy configuration
aligns with other packages.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/automl/frontend/config/webpack.dev.js`:
- Around line 70-74: The current code blindly strips a "Bearer " prefix from
incomingAuth which mangles non-Bearer schemes; update the logic around
incomingAuth and proxyReq.setHeader so you only strip the "Bearer" prefix when
the header actually matches /^Bearer\s+/i (e.g., extract token with a regex
capture and fall back to the original header otherwise), then set
'Authorization' and 'x-forwarded-access-token' using the correctly extracted
values (refer to the variables incomingAuth, token and calls to
proxyReq.setHeader).

In `@packages/autorag/frontend/config/webpack.dev.js`:
- Around line 70-74: The current code always strips a "Bearer " prefix via
replace which can mangle non-Bearer Authorization values; update the extraction
so you only pull the token when the header actually matches the Bearer scheme:
check incomingAuth against /^Bearer\s+(.+)$/i (via match) and if it matches set
x-forwarded-access-token to the captured token, otherwise forward the original
incomingAuth (or omit the x-forwarded-access-token) while still setting the
Authorization header via proxyReq.setHeader; update the logic around
incomingAuth, proxyReq.setHeader('Authorization', ...), and
proxyReq.setHeader('x-forwarded-access-token', ...) accordingly.

In `@packages/eval-hub/frontend/config/webpack.dev.js`:
- Around line 71-75: The Authorization header handling currently strips "Bearer"
unconditionally using replace(/^Bearer\s+/i, ''), which can mangle non-Bearer
tokens; change the logic in the block that reads incomingAuth and sets headers
(variables/methods: incomingAuth, proxyReq.setHeader) so you only remove the
Bearer prefix when the header actually starts with it (use
/^Bearer\s+/i.test(incomingAuth)); if it matches, set x-forwarded-access-token
to the header value with the prefix removed, otherwise set
x-forwarded-access-token to the raw incomingAuth value.

In `@packages/gen-ai/frontend/config/webpack.dev.js`:
- Around line 46-49: The x-forwarded-access-token currently strips "Bearer "
only via replace and accidentally leaves the full header for non-Bearer schemes;
update the logic around incomingAuth in the webpack dev proxy handler (where
proxyReq.setHeader is called) to detect a Bearer scheme first (use
/^Bearer\s+/i.test(incomingAuth)) and only then remove the prefix, otherwise
pass the original incomingAuth through as the token, and then set
'x-forwarded-access-token' to that token.

In `@packages/maas/frontend/config/webpack.dev.js`:
- Around line 71-74: The code sets x-forwarded-access-token using
incomingAuth.replace which incorrectly forwards non-Bearer credentials; update
the block that handles incomingAuth so it only extracts and forwards a token
when incomingAuth is a Bearer token (use /^Bearer\s+/i.test(incomingAuth) and
then token = incomingAuth.replace(/^Bearer\s+/i, '') before calling
proxyReq.setHeader('x-forwarded-access-token', token)), and do not set
x-forwarded-access-token for other schemes (e.g., Basic) — continue to set the
original Authorization header via proxyReq.setHeader('Authorization',
incomingAuth') as before.

In `@packages/mlflow/frontend/config/webpack.dev.js`:
- Around line 58-62: The code currently always derives a token via replace;
instead only extract and forward an access token when the Authorization header
is a Bearer token: check incomingAuth against the /^Bearer\s+(.+)$/i regex, if
it matches capture the token and call
proxyReq.setHeader('x-forwarded-access-token', token); continue to set the
original Authorization header (proxyReq.setHeader('Authorization',
incomingAuth)) but do not set x-forwarded-access-token for non-Bearer schemes;
refer to the incomingAuth variable, the token capture, and the
proxyReq.setHeader calls to locate where to apply this change.

---

Outside diff comments:
In `@packages/gen-ai/frontend/config/webpack.dev.js`:
- Around line 91-101: The proxy config is missing headers needed for AUTH_METHOD
=== 'internal'; add a getProxyHeaders() helper that returns { 'kubeflow-userid':
'user@example.com' } when AUTH_METHOD === 'internal' (and {} otherwise), then
include headers: getProxyHeaders() in the proxy object alongside context,
target, changeOrigin and onProxyReq so the dev server forwards the internal auth
header; reference AUTH_METHOD, getProxyHeaders(), and the proxy config entry
that uses PROXY_HOST/PROXY_PORT/PROXY_PROTOCOL and onProxyReq.

---

Nitpick comments:
In `@packages/automl/frontend/config/webpack.dev.js`:
- Around line 107-114: The proxy target in the webpack dev config uses a
template string; replace it with the object-form target used elsewhere for
consistency: change the proxy entry in the proxy array (the object with context
['/api','/automl/api'], changeOrigin, headers: getProxyHeaders(), onProxyReq) to
supply target as { protocol: PROXY_PROTOCOL, host: PROXY_HOST, port: PROXY_PORT
} instead of `${PROXY_PROTOCOL}://${PROXY_HOST}:${PROXY_PORT}` so the proxy
configuration aligns with other packages.

In `@packages/maas/frontend/config/webpack.dev.js`:
- Around line 31-48: The getKubeconfigToken() function is duplicated across
multiple webpack configs; extract it into a shared utility module (e.g., export
a function from `@coderabbit/shared/webpack-utils` named getKubeconfigToken) and
replace the local implementations in
packages/maas/frontend/config/webpack.dev.js (and the other 4 configs) with an
import from that new module; ensure the shared function preserves the same
behavior (uses execSync, console.info on username, console.error on failure, and
returns the token or empty string) and update imports in each webpack config to
reference the new shared symbol.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: fdf5e434-48cd-40d2-954a-1f81ac7e0b82

📥 Commits

Reviewing files that changed from the base of the PR and between f309bee and 2312cc3.

📒 Files selected for processing (6)
  • packages/automl/frontend/config/webpack.dev.js
  • packages/autorag/frontend/config/webpack.dev.js
  • packages/eval-hub/frontend/config/webpack.dev.js
  • packages/gen-ai/frontend/config/webpack.dev.js
  • packages/maas/frontend/config/webpack.dev.js
  • packages/mlflow/frontend/config/webpack.dev.js

Comment thread packages/automl/frontend/config/webpack.dev.js
Comment thread packages/autorag/frontend/config/webpack.dev.js
Comment thread packages/eval-hub/frontend/config/webpack.dev.js
Comment thread packages/gen-ai/frontend/config/webpack.dev.js
Comment thread packages/maas/frontend/config/webpack.dev.js
Comment thread packages/mlflow/frontend/config/webpack.dev.js
@github-actions
Copy link
Copy Markdown
Contributor

This PR is stale because it has been open 21 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@github-actions github-actions Bot added the Stale Issue was created a long time ago and nothing has happened label Apr 25, 2026
@lucferbux lucferbux removed the Stale Issue was created a long time ago and nothing has happened label Apr 27, 2026
@lucferbux
Copy link
Copy Markdown
Contributor

/retest-required

Copy link
Copy Markdown
Contributor

@lucferbux lucferbux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm
/approve

After reviewing, this is doing more than it should but it does no harm, we can approve it and if anyone wants to tweak it later they can.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Apr 27, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: lucferbux

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@lucferbux
Copy link
Copy Markdown
Contributor

/retest-required

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 64.97%. Comparing base (f309bee) to head (2312cc3).
⚠️ Report is 256 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #7065      +/-   ##
==========================================
+ Coverage   64.93%   64.97%   +0.03%     
==========================================
  Files        2355     2349       -6     
  Lines       74532    74477      -55     
  Branches    18280    18267      -13     
==========================================
- Hits        48400    48389      -11     
+ Misses      26132    26088      -44     

see 8 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f309bee...2312cc3. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@openshift-merge-bot openshift-merge-bot Bot merged commit d73dabc into opendatahub-io:main Apr 27, 2026
73 of 76 checks passed
@mturley mturley deleted the fix/dev-proxy-forward-auth-headers branch April 28, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants