Skip to content

🔐 feat: Mint Code API Auth Tokens - #13028

Merged
danny-avila merged 12 commits into
devfrom
danny-avila/ai-905-codeapi-auth
May 9, 2026
Merged

danny-avila merged 12 commits into
devfrom
danny-avila/ai-905-codeapi-auth

Conversation

@danny-avila

Copy link
Copy Markdown
Collaborator

Summary

I implemented LibreChat-side CodeAPI JWT minting for AI-905 so managed CodeAPI calls receive a short-lived bearer token from canonical request context.

  • Added CodeAPI auth helpers that mint scoped JWT claims from authenticated LibreChat request state, including tenant, principal source, and auth context hash.
  • Cached minted tokens briefly while respecting token expiry and managed-auth requirements.
  • Injected CodeAPI Authorization headers into code upload, batch upload, download, execution, programmatic tool calling, and bash PTC paths.
  • Hardened minting against missing tenant context and caller-supplied tenant/body spoofing without forwarding OpenID or refresh credentials.

Change Type

  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)

Testing

  • Ran npx jest --runTestsByPath src/auth/codeapi.spec.ts --runInBand --coverage=false --watch=false from packages/api.
  • Ran npm run build:api from packages/api; the build completed and emitted existing package-only workspace warnings.

Test Configuration:

  • macOS local worktree
  • Node/npm workspace tooling from the repository

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • I have commented in any complex areas of my code
  • I have written tests demonstrating that my changes are effective or that my feature works
  • Local unit tests pass with my changes

Copilot AI review requested due to automatic review settings May 8, 2026 19:52

Copy link
Copy Markdown
Collaborator Author

@codex review

@danny-avila danny-avila changed the title 🔐 feat: Mint CodeAPI Auth Tokens 🔐 feat: Mint Code API Auth Tokens May 8, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3c85665a5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/auth/codeapi.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds first-party (LibreChat-minted) short-lived JWT bearer authentication for managed CodeAPI requests, propagating authenticated request context (tenant + principal/source metadata) into CodeAPI-bound operations.

Changes:

  • Added CodeAPI JWT minting + Authorization header helper (mintCodeApiToken, getCodeApiAuthHeaders) with brief caching and scoped claims.
  • Threaded authHeaders through tool construction paths (programmatic tool calling, bash/code execution) so remote execution can attach CodeAPI auth.
  • Injected CodeAPI Authorization headers into CodeAPI file upload/batch upload/download flows and plumbed request auth strategy through JWT middleware.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/api/src/types/http.ts Extends ServerRequest to carry the auth strategy used to populate req.user.
packages/api/src/tools/classification.ts Adds optional authHeaders provider to tool classification and forwards it into PTC tool creation.
packages/api/src/auth/index.ts Exposes the new CodeAPI auth helpers via the auth barrel export.
packages/api/src/auth/codeapi.ts Implements JWT minting, claim shaping, signing, and header generation for CodeAPI.
packages/api/src/auth/codeapi.spec.ts Adds unit tests covering minting behavior, spoofing resistance, and caching window behavior.
api/server/services/ToolService.js Passes authHeaders into agent tool classification and into PTC + bash tool creation for execution-time calls.
api/server/services/Files/Code/crud.js Adds CodeAPI Authorization headers to CodeAPI download/upload/batch-upload axios requests.
api/server/routes/files/files.js Passes req into the code-output download stream strategy to enable per-request auth headers.
api/server/middleware/requireJwtAuth.js Records chosen passport strategy on req.authStrategy before tenant context middleware runs.
api/server/middleware/optionalJwtAuth.js Sets req.authStrategy consistently when optional auth succeeds (openidJwt vs jwt).
api/app/clients/tools/util/handleTools.js Provides CodeAPI auth headers to the code execution tool for managed remote execution.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread api/server/services/Files/Code/crud.js
Comment thread packages/api/src/auth/codeapi.ts
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: abc2daf3bb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/auth/codeapi.ts

Copy link
Copy Markdown
Collaborator Author

@codex review

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

GitNexus: 🚀 deployed

The LibreChat-pr-13028 index is now live on the MCP server.
Deploy run

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e27ab4356e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/auth/codeapi.ts
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

1 similar comment
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

GitNexus: 🚀 deployed

The LibreChat-pr-13028 index is now live on the MCP server.
Deploy run

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

1 similar comment
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

GitNexus: 🚀 deployed

The LibreChat-pr-13028 index is now live on the MCP server.
Deploy run

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7281fd5c28

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/server/middleware/requireJwtAuth.js Outdated
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a270f35b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/server/middleware/requireJwtAuth.js Outdated
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

GitNexus: 🚀 deployed

The LibreChat-pr-13028 index is now live on the MCP server.
Deploy run

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

GitNexus: 🚀 deployed

The LibreChat-pr-13028 index is now live on the MCP server.
Deploy run

@danny-avila
danny-avila force-pushed the danny-avila/ai-905-codeapi-auth branch from 72a882e to bfb5cb3 Compare May 9, 2026 15:52
@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

GitNexus: 🚀 deployed

The LibreChat-pr-13028 index is now live on the MCP server.
Deploy run

@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

GitNexus: 🚀 deployed

The LibreChat-pr-13028 index is now live on the MCP server.
Deploy run

@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

GitNexus: 🚀 deployed

The LibreChat-pr-13028 index is now live on the MCP server.
Deploy run

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c094b51baa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/server/services/ToolService.js Outdated
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

GitNexus: 🚀 deployed

The LibreChat-pr-13028 index is now live on the MCP server.
Deploy run

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danny-avila
danny-avila force-pushed the danny-avila/ai-905-codeapi-auth branch from 9879b68 to 24c0acf Compare May 9, 2026 20:03
@danny-avila
danny-avila marked this pull request as ready for review May 9, 2026 20:07
@danny-avila
danny-avila merged commit c67e2b5 into dev May 9, 2026
15 checks passed
@danny-avila
danny-avila deleted the danny-avila/ai-905-codeapi-auth branch May 9, 2026 20:09
fuuuzzy pushed a commit to fuuuzzy/LibreChat that referenced this pull request May 11, 2026
* feat: Mint CodeAPI auth tokens

* style: Format CodeAPI download route

* fix: Prune CodeAPI token cache

* fix: Propagate CodeAPI managed auth

* test: Mock CodeAPI auth in traversal suite

* fix: Pass auth context to invoked skill cache

* feat: Mint CodeAPI plan context

* chore: Refresh CodeAPI auth guidance

* fix: Guard OpenID JWT fallback

* fix: Default CodeAPI JWT tenant in single-tenant mode

* chore: Update @librechat/agents to version 3.1.84 in package-lock.json and package.json files

* chore: Standardize references to Code API in comments and tests
jcbartle pushed a commit to jcbartle/LibreChat that referenced this pull request May 11, 2026
* feat: Mint CodeAPI auth tokens

* style: Format CodeAPI download route

* fix: Prune CodeAPI token cache

* fix: Propagate CodeAPI managed auth

* test: Mock CodeAPI auth in traversal suite

* fix: Pass auth context to invoked skill cache

* feat: Mint CodeAPI plan context

* chore: Refresh CodeAPI auth guidance

* fix: Guard OpenID JWT fallback

* fix: Default CodeAPI JWT tenant in single-tenant mode

* chore: Update @librechat/agents to version 3.1.84 in package-lock.json and package.json files

* chore: Standardize references to Code API in comments and tests
aron-muon added a commit to aron-muon/KubeCodeRun that referenced this pull request May 20, 2026
…batch

LibreChat 0.8.5 (@librechat/agents >= 3.1.74) and the LC PRs around
the codeapi auth refactor (LibreChat-AI/LibreChat#13028, #12767) shifted
the upload/exec wire contract in ways that broke our clients:

  1. Responses are expected to carry ``storage_session_id`` (renamed
     from ``session_id``) on upload responses and per-file entries.
     packages/data-provider/src/codeEnvRef.ts and
     api/server/services/Files/Code/crud.js both read this field; a
     missing key was making LC throw "Unexpected batch upload response".

  2. File references gained ``resource_id``, ``kind`` ('skill'|'agent'|
     'user'), and ``version`` discriminator fields (CodeEnvFile type).
     LC fans them through to skill priming, sandbox file routing, and
     tool-output references.

  3. ``POST /upload/batch`` is the endpoint LC uses for skill bundles —
     uploading many files in one request. We didn't have it, so skill
     priming silently 404'd.

  4. ``GET /files/{session_id}`` is called with ``kind``/``id``/
     ``version`` query params by fetchSessionFiles. Without accepting
     them the request 422'd.

Changes:

  src/models/exec.py
    - FileRef:    populate_by_name; resource_id/kind/version added;
                  storage_session_id computed-field mirrors session_id.
    - RequestFile: storage_session_id accepted via AliasChoices alongside
                  legacy session_id; resource_id/kind/version added.

  src/api/files.py
    - POST /upload response: dual-emit storage_session_id + session_id.
    - POST /upload/batch: new endpoint. Returns per-file succeeded/failed
      counts; persists kind/resource_id on session.metadata when present.
    - GET /files/{session_id}: kind/id/version query params accepted
      (pass-through; no server-side filter today). Each list item gets
      both storage_session_id and session_id.

  tests/unit/test_librechat_contract.py
    - 16 new tests pinning every contract point above so a future model
      refactor cannot silently regress LC compatibility.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aron-muon added a commit to aron-muon/KubeCodeRun that referenced this pull request May 20, 2026
LibreChat LibreChat-AI/LibreChat#13028 introduced signed JWT auth for
the code-interpreter API. Tokens are minted by LC's
packages/api/src/auth/codeapi.ts (EdDSA Ed25519 by default, RS256
supported) and sent as `Authorization: Bearer <jwt>` with claims
{iss, aud, sub, iat, nbf, exp, jti, tenant_id, role,
principal_source, auth_context_hash}.

This commit adds the verifier side as a new service module —
src/services/codeapi_jwt.py — and the env knobs to configure it. The
middleware wiring lands in a follow-up commit so the verifier can be
unit-tested in isolation first.

Settings (env names match LC's signer 1:1):
  - CODEAPI_JWT_ENABLED      (default false; opt-in)
  - CODEAPI_JWT_PUBLIC_KEY   (PEM, raw JWK JSON, or file path)
  - CODEAPI_JWT_ALGORITHM    (EdDSA | RS256; default EdDSA)
  - CODEAPI_JWT_ISSUER       (default 'librechat')
  - CODEAPI_JWT_AUDIENCE     (default 'codeapi')
  - CODEAPI_JWT_LEEWAY_SECONDS (default 10; clock-skew tolerance)
  - CODEAPI_JWT_TRUST_TENANT_ID (default false; observability-only)

Security:
  - Algorithm is pinned to the operator-configured value — never the
    JWT header's `alg`. Standard alg-confusion defence; an attacker
    hand-crafting an HS256 token signed with the public key as the
    HMAC secret is rejected.
  - iss/aud are checked exactly; exp/nbf get the configured leeway.
  - require=[iss, aud, sub, exp, iat] so a token missing any of those
    is rejected without falling through to defaults.
  - Public key loaded once and process-cached; rotate by restart.

CodeApiJwtConfigurationError vs CodeApiJwtError distinction lets the
caller map config bugs to 500 and token problems to 401.

22 unit tests in tests/unit/test_codeapi_jwt.py cover: EdDSA + RS256
happy path, expired / wrong iss / wrong aud / wrong key / empty sub /
malformed / alg-confusion / missing-required-claim rejection, and
PEM / JWK / file-path key formats.

New dependency: pyjwt[crypto]>=2.10.0 (pulls in cryptography).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aron-muon added a commit to aron-muon/KubeCodeRun that referenced this pull request May 20, 2026
…ll-runtimes bash image (#66)

* fix(security): prevent cross-user file leak via session reuse

Sessions referenced by file_ref.session_id or entity_id are now reused
only when the existing session's metadata.user_id matches the request's
user_id. Before this change, a request from user-B carrying a file_ref
that pointed at user-A's upload session would execute *in* user-A's
session, where _mount_files would then re-hydrate every file ever
uploaded under that session into user-B's pod (issue surfaced in
multiple user reports of "users seeing other users files").

Defense-in-depth in _mount_files: even after the session-isolation
fix routes user-B to a fresh execution session, refuse to read file
content out of a session owned by a different user. Legacy sessions
without metadata.user_id are still readable for back-compat — the
window pre-dates the ownership concept.

license; semantics preserved, doc strings rewritten for our codebase).

Pre-existing TestMountFilesExtended tests were updated to set
ctx.session_id, which they had been implicitly relying on; the realistic
"upload-then-exec in same session" scenario short-circuits the new
authorization guard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(auth): accept HTTP Basic auth, add AUTH_ENABLED bypass

LibreChat 0.8.5 (@librechat/agents >= 3.1.74) removed the x-api-key
header and the body-spread LIBRECHAT_CODE_API_KEY field from its code-
interpreter client. Users with the legacy URL-credentials configuration
(LIBRECHAT_CODE_BASEURL=https://KEY@host/v1) now reach us with the key
in an Authorization: Basic header that axios derives from the URL —
which our middleware was ignoring, causing every request to 401.

Extractor now accepts:
  - x-api-key (preferred, unchanged)
  - Authorization: Bearer / ApiKey (unchanged)
  - Authorization: Basic base64(KEY:)  ← new; LC convention is user-half
  - Authorization: Basic base64(:KEY)  ← also accepted; falls through
  - Authorization: Basic base64(user:KEY) → returns user (documented)
Malformed Basic headers return None (no 500). x-api-key still wins so
reverse-proxy injection has deterministic behavior.

New AUTH_ENABLED setting (default true) lets operators with their own
trust boundary (mTLS sidecar, VPC ingress) globally bypass key auth on
user paths. Admin endpoints (/api/v1/admin) still require MASTER_API_KEY
regardless — the bypass is intentionally gated. Bypassed requests get
scope["state"] seeded with anonymous markers so downstream metrics code
keeps working without crashing on missing api_key_hash.

_should_skip_auth gained a `scope` parameter to perform that seeding.
Three legacy callers in test_trusted_networks.py updated to pass {}.

Same change applied to the older AuthenticationMiddleware in
middleware/auth.py for consistency; main.py uses SecurityMiddleware.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(api): wire LibreChat User-Id header into session ownership

LibreChat 0.8.5 sends the user identifier in the User-Id HTTP header on
every code-interpreter call (api/server/services/Files/Code/crud.js).
It is NOT in the request body. Without consuming the header, every
LibreChat request reaches us with request.user_id=None, which after the
session-isolation fix forces the orchestrator into "create new session"
on every call — breaking same-user file continuity (upload → exec → next
exec all land in separate sessions).

Changes:

  POST /exec  — exec.py copies the User-Id (or X-User-Id) header onto
  request.user_id when the body field is missing. Body wins when both
  are set, preserving direct-API compatibility.

  POST /upload — upload_file gained User-Id / X-User-Id Header params.
  When present, the value is persisted onto session.metadata.user_id at
  session-creation time, AND the entity_id reuse path now matches the
  requesting user (mirrors the orchestrator's same-user gate). Without
  this, the upload-side entity_id reuse was its own cross-user collapse
  vector: two users uploading via the same shared agent would converge
  on a single session.

Pre-existing tests in test_cross_session_files.py modeled the legacy
issue-#34 entity_id=null scenario. They now explicitly configure
session_service.get_session to return None so the orchestrator treats
the upload sessions as legacy/anonymous (allowed for back-compat). The
upload reuse test was split into same-user-reuse (must reuse) and
cross-user-must-not-reuse cases, and a third test pins that User-Id
is persisted onto session metadata.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(api): LibreChat 0.8.5 wire contract — storage_session_id, kind, batch

LibreChat 0.8.5 (@librechat/agents >= 3.1.74) and the LC PRs around
the codeapi auth refactor (LibreChat-AI/LibreChat#13028, #12767) shifted
the upload/exec wire contract in ways that broke our clients:

  1. Responses are expected to carry ``storage_session_id`` (renamed
     from ``session_id``) on upload responses and per-file entries.
     packages/data-provider/src/codeEnvRef.ts and
     api/server/services/Files/Code/crud.js both read this field; a
     missing key was making LC throw "Unexpected batch upload response".

  2. File references gained ``resource_id``, ``kind`` ('skill'|'agent'|
     'user'), and ``version`` discriminator fields (CodeEnvFile type).
     LC fans them through to skill priming, sandbox file routing, and
     tool-output references.

  3. ``POST /upload/batch`` is the endpoint LC uses for skill bundles —
     uploading many files in one request. We didn't have it, so skill
     priming silently 404'd.

  4. ``GET /files/{session_id}`` is called with ``kind``/``id``/
     ``version`` query params by fetchSessionFiles. Without accepting
     them the request 422'd.

Changes:

  src/models/exec.py
    - FileRef:    populate_by_name; resource_id/kind/version added;
                  storage_session_id computed-field mirrors session_id.
    - RequestFile: storage_session_id accepted via AliasChoices alongside
                  legacy session_id; resource_id/kind/version added.

  src/api/files.py
    - POST /upload response: dual-emit storage_session_id + session_id.
    - POST /upload/batch: new endpoint. Returns per-file succeeded/failed
      counts; persists kind/resource_id on session.metadata when present.
    - GET /files/{session_id}: kind/id/version query params accepted
      (pass-through; no server-side filter today). Each list item gets
      both storage_session_id and session_id.

  tests/unit/test_librechat_contract.py
    - 16 new tests pinning every contract point above so a future model
      refactor cannot silently regress LC compatibility.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(auth): CodeAPI JWT verifier (LibreChat 0.8.5 signer side)

LibreChat LibreChat-AI/LibreChat#13028 introduced signed JWT auth for
the code-interpreter API. Tokens are minted by LC's
packages/api/src/auth/codeapi.ts (EdDSA Ed25519 by default, RS256
supported) and sent as `Authorization: Bearer <jwt>` with claims
{iss, aud, sub, iat, nbf, exp, jti, tenant_id, role,
principal_source, auth_context_hash}.

This commit adds the verifier side as a new service module —
src/services/codeapi_jwt.py — and the env knobs to configure it. The
middleware wiring lands in a follow-up commit so the verifier can be
unit-tested in isolation first.

Settings (env names match LC's signer 1:1):
  - CODEAPI_JWT_ENABLED      (default false; opt-in)
  - CODEAPI_JWT_PUBLIC_KEY   (PEM, raw JWK JSON, or file path)
  - CODEAPI_JWT_ALGORITHM    (EdDSA | RS256; default EdDSA)
  - CODEAPI_JWT_ISSUER       (default 'librechat')
  - CODEAPI_JWT_AUDIENCE     (default 'codeapi')
  - CODEAPI_JWT_LEEWAY_SECONDS (default 10; clock-skew tolerance)
  - CODEAPI_JWT_TRUST_TENANT_ID (default false; observability-only)

Security:
  - Algorithm is pinned to the operator-configured value — never the
    JWT header's `alg`. Standard alg-confusion defence; an attacker
    hand-crafting an HS256 token signed with the public key as the
    HMAC secret is rejected.
  - iss/aud are checked exactly; exp/nbf get the configured leeway.
  - require=[iss, aud, sub, exp, iat] so a token missing any of those
    is rejected without falling through to defaults.
  - Public key loaded once and process-cached; rotate by restart.

CodeApiJwtConfigurationError vs CodeApiJwtError distinction lets the
caller map config bugs to 500 and token problems to 401.

22 unit tests in tests/unit/test_codeapi_jwt.py cover: EdDSA + RS256
happy path, expired / wrong iss / wrong aud / wrong key / empty sub /
malformed / alg-confusion / missing-required-claim rejection, and
PEM / JWK / file-path key formats.

New dependency: pyjwt[crypto]>=2.10.0 (pulls in cryptography).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(auth): wire CodeAPI JWT verification into SecurityMiddleware

The CodeAPI JWT verifier landed in the previous commit as a standalone
service. This commit makes the middleware actually use it.

Auth-path priority on user-facing endpoints:

  1. ``Authorization: Bearer <jwt>`` AND ``codeapi_jwt_enabled``
     AND the token structurally looks like a JWT (3 base64 segments)
     → run the JWT verifier.
       - Valid: seed scope["state"] with authenticated=True,
         user_id=<sub>, api_key_hash="jwt:<sub-hash-prefix>",
         auth_principal_source="codeapi_jwt", and (when
         codeapi_jwt_trust_tenant_id is true) tenant_id=<tenant>.
         The legacy API-key path is SKIPPED.
       - Invalid: 401 immediately. DO NOT fall back to API-key auth
         with the same Bearer string. This is the downgrade-attack
         defence — an attacker mustn't be able to bypass JWT auth by
         submitting a deliberately-bad JWT and having the server quietly
         try the same string as an API key.
       - Configuration error (JWT enabled but no public key): 500.
         The client did nothing wrong; we just can't verify.
  2. Otherwise: existing API-key path (x-api-key → Bearer api-key →
     ApiKey scheme → Basic → JSON body LIBRECHAT_CODE_API_KEY).

The "looks like a JWT" structural check (3 dot-separated segments,
each ≥4 chars) lets a plain API key submitted as ``Bearer`` keep
working — we only divert to the JWT path for tokens that could
plausibly be a JWT.

12 new unit tests in test_security_middleware.py cover:
- _extract_bearer_jwt: disabled / wrong scheme / non-JWT-shaped /
  happy path classification.
- _authenticate_jwt: valid token seeds expected state; tenant_id
  omitted when trust is off; CodeApiJwtError → 401; configuration
  error → 500.
- End-to-end downgrade test: bad JWT 401s and the inner app is
  NEVER reached with the same Bearer treated as an api-key.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(api): trust JWT.sub over User-Id header in exec + upload

When SecurityMiddleware verified a CodeAPI JWT, request.state.user_id
is set to the JWT's `sub` claim — cryptographically authenticated.

Both /exec and /upload now use a 3-tier resolution chain:

  1. JWT.sub (request.state.user_id) — wins over everything else.
  2. ExecRequest.user_id body field — direct-API integration.
  3. User-Id / X-User-Id HTTP header — LibreChat 0.8.5 convention.

The JWT override is the security-critical bit: without it, an
attacker holding a valid JWT for user-A could pass user_id=victim
in the body (or User-Id: victim in headers) and the orchestrator's
cross-user-isolation guard would happily route them into the
victim's sessions. The signed JWT is the source of truth.

upload_file and upload_files_batch gain a `request: Request`
parameter to access scope state; existing tests had to be updated
to inject an anonymous mock_http_request fixture (resolution falls
through to headers exactly as before).

3 new tests in test_api_exec.py pin the JWT precedence:
  - JWT.sub overrides body user_id (attacker submits user_id=victim)
  - JWT.sub overrides User-Id header (same threat via header)
  - (existing tests cover header-fallback when JWT absent)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: document AUTH_ENABLED and CODEAPI_JWT_* env vars

- .env.example: stub-out the new auth toggles (commented; off by default)
  with one-line rationale each.
- docs/CONFIGURATION.md: full Authentication section update including
  the auth source priority order, a new CodeAPI JWT Authentication
  subsection covering the LC 0.8.5 integration (env-var-by-env-var
  mapping to LC's signer side), the trust model (JWT.sub overrides
  body/header user_id), and key rotation guidance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: fmt

* feat(docker): bake every supported language into the bash image

LibreChat @librechat/agents >= 3.1.74 collapsed `execute_code` and all
per-language code-interpreter tools into a single `bash_tool`. The LC
client no longer sends `lang: py | js | go | ...` — every code-interpreter
call now arrives as `lang: bash`, and the model is expected to shell out
to `python3 -c "..."`, `node -e "..."`, `go run`, etc. from inside the
shell. Before this change, those calls would fail in the bash sandbox
because the interpreters / compilers weren't installed.

Bakes all 13 supported language runtimes into docker/bash.Dockerfile:

  bash, sh                    — bash + coreutils + jq
  python (+ data stack)       — python3 + python-is-python3 + numpy /
                                pandas / matplotlib / openpyxl / Pillow
  javascript, typescript      — nodejs + npm + typescript (global)
  go                          — golang-go
  java                        — default-jdk-headless (OpenJDK 21)
  c, cpp                      — gcc, g++
  php                         — php-cli
  rust                        — rustc, cargo
  r                           — r-base-core
  fortran                     — gfortran
  d                           — ldc (compiler) + gcc (linker)

Final image is ~865 MB vs ~100 MB for the bash-only image — the trade
made deliberately so the bash pod can serve every LC request rather
than erroring on a missing interpreter.

ENTRYPOINT pins HOME=/tmp and per-toolchain cache dirs (GOCACHE,
GOPATH, CARGO_HOME, RUSTUP_HOME, JAVA_TOOL_OPTIONS=-Duser.home=/tmp,
MPLCONFIGDIR) so compilers don't try to write build artefacts into
the read-only sandbox home or the user-code /mnt/data dir.

The dedicated per-language images (python, nodejs, go, etc.) still
ship and are still served by `/exec` when callers send an explicit
`lang: <code>`. Only the LC client path changed.

Validation: scripts/test-bash-megaimage.sh runs the runner's exact
compile-and-run command (from docker/runner/executor.go LangSpec.Args)
for each of the 13 languages, asserts stdout contains "Hello, World!",
and reports pass/fail per language. All 13 pass on first run against
the locally-built image.

Side effect: the new BASE_IMAGE build arg lets CI / local builds
swap dhi.io/debian-base for debian:trixie-slim without editing the
Dockerfile. Production default unchanged.

Drive-by: removed an internal-fork reference from a code comment in
src/services/orchestrator.py — the algorithm is documented inline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ThomasVuNguyen pushed a commit to ThomasVuNguyen/LibreChat that referenced this pull request Jul 15, 2026
* feat: Mint CodeAPI auth tokens

* style: Format CodeAPI download route

* fix: Prune CodeAPI token cache

* fix: Propagate CodeAPI managed auth

* test: Mock CodeAPI auth in traversal suite

* fix: Pass auth context to invoked skill cache

* feat: Mint CodeAPI plan context

* chore: Refresh CodeAPI auth guidance

* fix: Guard OpenID JWT fallback

* fix: Default CodeAPI JWT tenant in single-tenant mode

* chore: Update @librechat/agents to version 3.1.84 in package-lock.json and package.json files

* chore: Standardize references to Code API in comments and tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants