Repository navigation
Conversation
The fileAccess middleware decided inline whether a user may access a file: same tenant for tenant-scoped files, then ownership, then access through the agents the file is attached to. The decision now lives in canAccessFile(user, file), which the middleware calls and exports, so a route that already holds a file record can apply the same rules without going through req.params.file_id. Behaviour is unchanged; the cross-tenant denial is still logged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With fileStrategy s3, browsers load images, previews and downloads straight from presigned URLs. Those links expire after S3_URL_EXPIRY_SECONDS and are stored in messages and file records, so images break once they expire, and the bucket has to answer requests from the internet, so it cannot be kept private to the deployment's network (for instance with a bucket policy on aws:SourceVpce). STORAGE_PROXY_FILES=true makes storage strategies link files to LibreChat instead: /api/stored-files/<source>/<key>, which never expires. The route reads the object through the strategy's StoredFileSource adapter and streams it to viewers who may read it. This commit adds the storage-agnostic route and links, and the S3 adapter. With the proxy on, getS3URL returns stored file links, extractKeyFromS3Url reads them back to keys, presigned links are replaced where the file list and avatars already renew links, and getS3DownloadURL returns no direct link, so the download and shared-link routes stream through their own checks. With it off, stored links go back to presigned ones the same way. Off by default. The route signs the viewer in from the session cookie, as /images does, since an image tag sends no bearer token; authenticateViewer carries the image route's account checks (retired tokens, required 2FA enrollment). Within the viewer's tenant it serves the owner named in the key, avatars to any signed-in user (as CloudFront avatar cookies do), and anyone the stored file's own rules admit through canAccessFile, such as a file on an agent shared with the viewer; everyone else gets a 404. The file lookup is scoped to the key's owner and source. Uploads are now served from the app's own origin, so only raster images are sent inline; every response carries a sandboxing CSP and nosniff. Related to LibreChat-AI#16912 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With fileStrategy azure_blob, LibreChat stores each blob's plain URL, so browsers can only display images from a container with public blob access. A private container (AZURE_STORAGE_PUBLIC_ACCESS=false, or an account that disallows public access) breaks every image and preview. This adds the Azure adapter for the stored-file route. With STORAGE_PROXY_FILES=true, uploads, saved buffers and avatars in the configured container store /api/stored-files/azure_blob/<blob path> instead of the blob URL, getAzureFileStream and deleteFileFromAzure read those links back to blob paths, and the route streams blobs to viewers who may read them under the same rules as S3. Azure keeps avatars beside other images, so the adapter marks avatar-<ts> and agent-<id>-avatar-<ts> files as avatars. Blobs in any other container keep their URLs, since a stored link names no container. Off by default. Related to LibreChat-AI#16912 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 9, 2026
Vidminas
marked this pull request as ready for review
October 9, 2026 11:00
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
👍
Summary
With
fileStrategy: "azure_blob", LibreChat stores plain blob URLs, so browsers can only show images from a public container. A private container breaks every image and preview (#13340, #14955, discussion #6704).This adds the Azure adapter for the stored-file route. With
STORAGE_PROXY_FILES=true:/api/stored-files/azure_blob/<blob path>.getAzureFileStreamanddeleteFileFromAzureresolve those links to blob paths.Unlike SAS URLs (#11137, #13245, #14956), these links don't expire, so nothing needs refreshing. Off by default.
Addresses #16628 for files saved with the setting on: uploads, generated images and outputs, avatars and downloads. Links already stored in conversation history are rewritten by #16916.
Related to #16912 and #14955. Depends on #16914
How it works
avatar-<ts>andagent-<id>-avatar-<ts>files as avatars, which any signed-in user may see.Type of change
Testing
fileStrategy: "azure_blob",AZURE_STORAGE_PUBLIC_ACCESS=falseandSTORAGE_PROXY_FILES=true, with a private container./api/stored-files/azure_blob/....Tested environments/configuration:
This is a proof-of-concept adapter for Azure, I'm focusing on the S3 side. These changes are not yet tested with a real Azure account. Feedback from Azure users is welcome (cc @mihidumh, @JorgeCosta87, @adamfisher)
Automated tests:
npm run static-checks -- --against upstream/dev --fullpasses.New tests:
storage/azure/__tests__/source.test.ts:services/Files/Azure/crud.spec.js: uploads store stored links, and stream and delete resolve them in the configured container. The existing blob-URL cases pass unchanged.Caveat:
@azure/storage-blobwith a nativeimport(), which Jest can't mock. The adapter's unit tests cover reading instead.Screenshots / recordings
No user-facing change. Images and files look the same; only their URLs change.
Risk / compatibility
/files/downloadas ablob:URL. Here the stored link already points at LibreChat, so there is no failing first request and no client change.Checklist