Skip to content

fix(ui): forward incoming auth headers in webpack dev proxy#2544

Merged
google-oss-prow[bot] merged 1 commit intokubeflow:mainfrom
mturley:fix/dev-proxy-forward-auth-header
Apr 27, 2026
Merged

fix(ui): forward incoming auth headers in webpack dev proxy#2544
google-oss-prow[bot] merged 1 commit intokubeflow:mainfrom
mturley:fix/dev-proxy-forward-auth-header

Conversation

@mturley
Copy link
Copy Markdown
Contributor

@mturley mturley commented Apr 3, 2026

Description

The webpack dev server's proxy was capturing the kubeconfig token at startup and setting it as a static headers config, which overwrote any auth headers forwarded by a host backend proxy (e.g. ODH Dashboard with dev impersonation). This meant that when running in federated mode with AUTH_METHOD=user_token, the BFF always operated with admin-level permissions regardless of which user the host backend was impersonating.

This PR switches to an onProxyReq callback that dynamically forwards incoming authorization headers when present, and falls back to the kubeconfig token captured at startup for standalone dev mode. This preserves the existing standalone behavior while enabling proper auth forwarding for federated setups.

Before

  • getProxyHeaders() ran kubectl config view at startup and returned static Authorization and x-forwarded-access-token headers
  • These static headers overwrote any auth headers set by an upstream proxy

After

  • getKubeconfigToken() captures the kubeconfig token at startup as a fallback
  • getProxyHeaders() returns {} for user_token mode (no static auth headers)
  • onProxyReq callback dynamically:
    • Forwards incoming authorization header if present (federated mode with impersonation)
    • Falls back to the kubeconfig token if no incoming auth header (standalone mode)

How Has This Been Tested?

Manual testing with ODH Dashboard federated dev mode:

  1. Started ODH Dashboard backend with DEV_IMPERSONATE_USER configured
  2. Started model-registry frontend + BFF in federated mode
  3. Verified that BFF operations now use the impersonated user's token instead of the admin token

Merge criteria:

  • All the commits have been signed-off (To pass the DCO check)
  • The commits have meaningful messages
  • Automated tests are provided as part of the PR for major new functionalities; testing instructions have been added in the PR body (for PRs involving changes that are not immediately obvious).
  • The developer has manually tested the changes and verified that the changes work.
  • Code changes follow the kubeflow contribution guidelines.
  • For first time contributors: Please reach out to the Reviewers to ensure all tests are being run, ensuring the label ok-to-test has been added to the PR.

If you have UI changes

  • The developer has added tests or explained why testing cannot be added.
  • Included any necessary screenshots or gifs if it was a UI change.
  • Verify that UI/UX changes conform the UX guidelines for Kubeflow.

The webpack dev server's proxy was capturing the kubeconfig token at
startup and setting it as a static header, which overwrote any auth
headers forwarded by a host backend proxy (e.g. ODH Dashboard with dev
impersonation). Switch to an onProxyReq callback that dynamically
forwards incoming authorization headers when present and falls back to
the kubeconfig token for standalone dev mode.

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

mturley commented Apr 3, 2026

/retest-required

@lucferbux
Copy link
Copy Markdown
Contributor

lucferbux commented Apr 23, 2026

Hi @mturley! I've been tackling the same issue across all BFF-backed modules in the ODH Dashboard monorepo (see opendatahub-io/odh-dashboard#7372) after I saw your thread. I tested the fix on a live cluster with both maas and model-registry BFFs and confirmed impersonation token forwarding works end-to-end.

I wanted to share our approach since we applied the same pattern to 6 other modules (maas, gen-ai, automl, autorag, mlflow, eval-hub) and it'd be great to keep consistency across all of them. Our approach is a bit simpler — here's the key difference:

Your current approach

  • Refactors getProxyHeaders() (removes the user_token branch)
  • Introduces getKubeconfigToken() + fallbackToken cached variable
  • Creates a standalone onProxyReq function
  • Keeps both headers: getProxyHeaders() and onProxyReq

Our approach (applied to 6 modules)

  • getProxyHeaders() stays exactly as-is (no refactoring)
  • Replace headers: getProxyHeaders() with onProxyReq (not both)
  • onProxyReq either forwards the upstream token or falls back to getProxyHeaders()
proxy: [
  {
    context: ['/api', '/model-registry/api'],
    target: { host: PROXY_HOST, protocol: PROXY_PROTOCOL, port: PROXY_PORT },
    changeOrigin: true,
    onProxyReq: (proxyReq, req) => {
      const upstreamAuth = req.headers.authorization;
      if (upstreamAuth && AUTH_METHOD === 'user_token') {
        // Federated mode: forward the upstream token (may be impersonated) as x-forwarded-access-token
        const token = upstreamAuth.replace(/^Bearer\s+/i, '');
        proxyReq.setHeader('x-forwarded-access-token', token);
      } else {
        // Standalone / internal auth: use the static headers from getProxyHeaders()
        const headers = getProxyHeaders();
        Object.entries(headers).forEach(([key, value]) => {
          proxyReq.setHeader(key, value);
        });
      }
    },
  },
],

Why this is simpler

  1. No getProxyHeaders() refactoring — the function keeps working exactly as before for standalone mode
  2. No cached fallbackToken variablegetProxyHeaders() already handles the kubectl token capture
  3. Single code pathonProxyReq handles everything instead of splitting logic between headers: and onProxyReq
  4. Consistency — the same pattern is used across all 6 other modules in ODH Dashboard

Also: pre-existing lint issues

When we ran our lint hooks against the downstream copy of this file, we hit pre-existing errors that needed fixing:

  • Missing /* eslint-disable @typescript-eslint/no-require-imports */ at the top (all other modules have this for CJS webpack configs)
  • Missing // eslint-disable-next-line import/order on the dotenv import (it must come before webpack.common.js for functional reasons)

You might want to fix those here too since they'll come up once the downstream sync happens.

Happy to discuss if you have questions about the approach!

lucferbux added a commit to lucferbux/odh-dashboard that referenced this pull request Apr 23, 2026
Replace static `headers: getProxyHeaders()` with `onProxyReq` callback
in all module webpack dev proxies. In federated mode, the main dashboard
backend sets the authorization header (which may carry an impersonated
token). The new callback extracts it and forwards as
x-forwarded-access-token for the BFF, matching production convention.
In standalone/internal auth modes, falls back to getProxyHeaders().

model-registry is excluded — handled upstream via
kubeflow/hub#2544.
@mturley
Copy link
Copy Markdown
Contributor Author

mturley commented Apr 23, 2026

Thanks @lucferbux ! This approach does make more sense. I'm on PTO this week, I can replace this PR when I get back or if you want us to merge sooner for consistency someone else on our scrum could do it.

@lucferbux
Copy link
Copy Markdown
Contributor

@mturley after re-evaluating, your solution is fine, it's doing an extra step, I misread your thread and I thought you didn't get the full solution for all modules, we can keep your solution as it's already stablished, approving and merging

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

@google-oss-prow
Copy link
Copy Markdown
Contributor

[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

@ppadti
Copy link
Copy Markdown
Contributor

ppadti commented Apr 27, 2026

/retest

@google-oss-prow google-oss-prow Bot merged commit 653ff51 into kubeflow:main Apr 27, 2026
40 of 42 checks passed
@mturley mturley deleted the fix/dev-proxy-forward-auth-header branch April 28, 2026 14:07
@mturley
Copy link
Copy Markdown
Contributor Author

mturley commented Apr 28, 2026

Thanks @lucferbux !

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.

3 participants