Skip to content

Bff - #6114

Closed
gcgoncalves wants to merge 5 commits into
epic/ui-rewritefrom
bff
Closed

Bff#6114
gcgoncalves wants to merge 5 commits into
epic/ui-rewritefrom
bff

Conversation

@gcgoncalves

@gcgoncalves gcgoncalves commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Running the BFF locally

1. FastAPI (terminal A, repo root)

cp .env.example .env   # if not already done
make install-dev        # first time only
make dev                 # :8000
  1. BFF (terminal B)
cd client/server
cp .env.example .env
# edit .env: FASTAPI_URL=http://127.0.0.1:8000, COOKIE_SECURE=false for local HTTP
npm install
npm run dev                 # :3000, tsx watch

REDIS_URL=memory:// by default — no Redis process needed for local dev (in-process session store; state resets on restart).

  1. Build the SPA for the BFF to serve
cd client
npm run build:bff        # outputs to client/server/public/, base "/"
Re-run after any client/src change — the BFF serves whatever's on disk, no rebuild-on-save.
  1. Use it

Visit http://127.0.0.1:3000/ — redirects to /app/login (unauthed) or /app/ (authed). Login form posts through the BFF, which holds the FastAPI JWT server-side and hands the browser an opaque session cookie only.

Default seeded admin: admin@example.com / changeme (first login forces a password change unless PASSWORD_CHANGE_ENFORCEMENT_ENABLED=false is set in the root .env).

Troubleshooting

  • EADDRINUSE on :3000 → stale tsx watch process: lsof -ti:3000 | xargs kill, restart pnpm dev.
  • 401 mid-session → normal, token hard-expires per TOKEN_EXPIRY (default 20 min); BFF auto-revokes and redirects to login.

@marekdano marekdano added the ui-rewrite Tasks for the isolated ui rewrite feature branch label Aug 7, 2026

@a-effort a-effort left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

A few things worth addressing before this merges up. Left them inline; overall this is really solid work and the security core is right. Filed as questions/options rather than blockers.

const sessionId = request.session!.sessionId;

return reply.from(upstreamPath, {
rewriteRequestHeaders: (_req, headers) => ({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Real client IP is lost upstream (audit accuracy). Since every request now originates server-to-server from the BFF, rewriteRequestHeaders here (and the login fetch) don't carry the caller's IP. Upstream email_auth.py derives the client IP from X-Forwarded-For, falling back to request.client.host, so audit logs will record the BFF's IP for every login and every action rather than the real client.

Worth setting x-forwarded-for / x-real-ip from request.ip here and in routes/auth/login.ts. Lockout itself isn't affected (it's keyed per-email, not per-IP), so this is about audit fidelity, not access control.


subscriber.psubscribe(REVOKED_PATTERN, (err) => {
if (err) {
subscriber.emit("error", err);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This can crash the process on a subscribe failure. subscriber.emit("error", err) is emitted, but nothing registers an 'error' listener on this connection. A Node EventEmitter throws when 'error' is emitted with no listener, so a Redis hiccup at startup could take the whole BFF down instead of degrading gracefully to the Option-A periodic recheck.

Adding a subscriber.on("error", (err) => { /* log */ }) before psubscribe covers both this path and ordinary connection blips over the connection's lifetime.

logLevel: optional("LOG_LEVEL", "info"),
} as const;

if (process.env.NODE_ENV === "production" && config.redisUrl.startsWith("memory://")) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This guard depends on NODE_ENV, which the start script never sets. "start": "node --env-file-if-exists=.env dist/index.js" doesn't set NODE_ENV, so a prod deploy that forgets to export NODE_ENV=production silently falls back to the in-process memory:// store, quietly losing cross-instance sessions and revocation.

One option is to fail closed on a more reliable signal, e.g. reject memory:// whenever COOKIE_SECURE=true (which prod sets anyway), in addition to the NODE_ENV check.

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>

@marekdano marekdano left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Potential Issues & Recommendations

Minor Issues

E2E Test Cleanup: Some tests still set sessionStorage.setItem("mcpgateway_token", ...)

  • Harmless (ignored by BFF auth), but could be cleaned up for consistency

Recommendations

  1. Documentation: Add BFF architecture diagram to docs/
  2. Monitoring: Add structured logging for session lifecycle events
  3. Rate Limiting: Consider adding rate limiting to /auth/login
  4. Session Cleanup: Add background job to clean expired Redis sessions
  5. CORS: Document CORS requirements if BFF and SPA are on different origins

@gcgoncalves

Copy link
Copy Markdown
Collaborator Author

Closed in favour of the new repository.

@gcgoncalves
gcgoncalves deleted the bff branch August 12, 2026 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ui-rewrite Tasks for the isolated ui rewrite feature branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants