Skip to main content

File Service

AuthProxy provides file upload and download transport for Chat attachments. AuthProxy stores bytes and enforces transport-level limits; Chat owns file metadata, access checks, and attachment lifecycle.

There are two upload flows:

  • auth/v1/file/* — buffered chunk upload used by the current Chat PWA.
  • auth/v2/file/* — draft + streaming upload, plus code-based downloads for anonymous or public-link scenarios.

Both flows support two storage backends:

  • local disk via LocalFileStoragePath
  • external HTTP file servers via FileServers

v1 Chunked Upload​

The v1 flow buffers chunks in AuthProxy memory, validates CRC32 per chunk, validates the full file CRC32 after all chunks arrive, creates a pending Chat file message, writes bytes to storage, and then completes the pending Chat message. There is no separate complete_upload endpoint.

Initialize Upload​

Start a new upload session. The returned result is the numeric upload ID. In the current implementation it is the declared full-file CRC32 value and is passed back as uploadId for chunk calls.

POST /auth/v1/file/init_upload
Content-Type: application/json
Cookie: sid=...

{
"FileName": "document.pdf",
"FileSize": 1048576,
"CRC32": 305419896,
"ChatId": "638412345678901234",
"Message": "optional message text",
"LinkedMessageId": "0"
}

Response

{
"result": 305419896
}

ChatId and Personal Storage fallback​

Every uploaded file becomes a Chat message (chat_mess), so each upload must reference a real chat where the caller is a member.

  • ChatId > 0 — a normal conversation. The caller must be a member; Chat enforces this.
  • ChatId <= 0 (or omitted) — a "no-conversation" file such as an OAuth app logo, a user avatar, etc. AuthProxy lazily resolves a per-user Personal Storage chat (get_or_create_personal_storage_chat) and substitutes its timetick before sending the message. This applies to both init_upload (v1) and create_draft (v2). On a resolve failure the upload returns a gateway/file error instead of an opaque ChatNotFound.

This removes the need for clients to invent a fake chatId for sessionless attachments.

Upload Part​

Upload one binary chunk. Parameters are query-string values; the request body is raw application/octet-stream, not multipart form data.

POST /auth/v1/file/upload_part?uploadId=305419896&chunkNumber=0&chunkCrc32=2598427311
Content-Type: application/octet-stream
Cookie: sid=...

[binary chunk bytes]

Chunks are zero-based. The fixed chunk size is 256 KB except for the final chunk.

For non-final chunks AuthProxy returns the accepted chunk number:

{
"result": {
"chunkNumber": 0,
"fileId": null,
"fileCode": null
}
}

When the last missing chunk is accepted, AuthProxy finalizes the upload and returns the canonical file ID. For Chat attachments, fileId is the Chat message timetick.

{
"result": {
"chunkNumber": 3,
"fileId": "638412345678901234",
"fileCode": "encoded-file-code"
}
}

Cancel Upload​

Cancel an active buffered upload session and release the rented memory buffer.

POST /auth/v1/file/cancel_upload?uploadId=305419896
Cookie: sid=...

v2 Streaming Upload​

The v2 flow creates a pending Chat file message first, then streams the request body directly to local disk or the configured HTTP file server. It is intended for clients that do not want AuthProxy to buffer the whole file in memory.

The actual request body is capped by the dynamic FileMaxSizeBytes setting. AuthProxy rejects oversized streaming uploads with HTTP 413 Payload Too Large.

Create Draft​

POST /auth/v2/file/create_draft?fileName=document.pdf&fileSize=1048576&chatId=638412345678901234&message=optional%20text&linkedMessageId=0&crc32=305419896
Cookie: sid=...

Response

{
"result": "638412345678901234:encoded-file-code"
}

The value before : is fileId; the value after : is the code that can be used with /auth/v2/file/{id_or_code}.

Stream Upload​

POST /auth/v2/file/stream_upload/638412345678901234
Content-Type: application/octet-stream
Range: bytes=0-
Cookie: sid=...

[binary file bytes]

On success the endpoint completes the pending Chat message and returns HTTP 200 with an empty body. If the upload fails or the client disconnects, AuthProxy attempts to delete the stored bytes and the pending Chat message.

Download API​

v1 Session Download​

GET /auth/v1/file/{fileId}
Cookie: sid=...

The v1 download endpoint always requires a valid session and validates access through Chat file metadata before serving bytes.

Optional chunk download:

GET /auth/v1/file/{fileId}?part_number=0
Cookie: sid=...

part_number uses 256 KB chunks and returns these headers:

  • X-Chunk-CRC32 — full-file CRC32 from Chat metadata
  • X-File-Part — whole-file or part-N/total
  • Cache-Control — public immutable-style browser cache hint

HEAD /auth/v1/file/{fileId} is also supported.

v2 Streaming Download​

GET /auth/v2/file/{id_or_code}

id_or_code can be:

  • an obfuscated file code, always accepted by the route
  • a raw numeric file ID, always accepted for authenticated users and accepted anonymously only when FileDownloadByIdEnabled is true

By default code-based v2 downloads do not require a session. Set FileDownloadAuthRequired to require authentication for every v2 download. Authenticated raw-ID downloads always validate access through Chat metadata with the current user's CRM ID.

The v2 download endpoint supports:

  • HEAD
  • Range / 206 Partial Content
  • Accept-Ranges: bytes
  • ETag and If-None-Match
  • If-Match
  • bandwidth limiting through AuthProxy runtime limits

Storage Backend​

For a mono-proxy or other single-instance deployment, use local disk storage:

{
"LocalFileStoragePath": "/var/lib/authproxy/files"
}

AuthProxy stores file bytes under:

{LocalFileStoragePath}/{owner}/{fileId}

Notes:

  • The directory must be writable by the AuthProxy process.
  • This is deploy-time configuration in appsettings.json or environment variables.
  • This is not managed through ProxyAdmin.

External HTTP Storage Backend​

If you need storage outside the AuthProxy process, configure HTTP file servers:

{
"FileServers": {
"0": {
"Host": "http://files:80"
}
}
}

FileServers uses numeric thresholds as keys. AuthProxy selects the first configured backend whose threshold is lower than the generated fileId.

Use this mode when you intentionally want file bytes outside the local proxy instance. For single-proxy deployments, LocalFileStoragePath is usually simpler and is the preferred setup.

Dynamic Settings​

These values are runtime settings in ProxyAdmin → Settings, not static appsettings.json fields.

Setting typeDefaultEffect
FileMaxSizeBytes20971520Project-level upload size cap. Values are clamped by the AuthProxy hard cap of 100 MiB.
FileBlockedExtensionsemptySemicolon-separated upload extension denylist in setting_str, for example exe;bat;cmd;ps1.
FileDownloadBytesPerSecond5242880Shared v2 download quota in bytes per second (setting_value). Set it to 0 to disable byte throttling.
FileUploadBytesPerSecond2097152Shared v2 upload quota in bytes per second (setting_value). Set it to 0 to disable byte throttling.
FileDownloadByIdEnabled0Allows anonymous raw numeric IDs on /auth/v2/file/{id}. Authenticated raw-ID downloads and code-based v2 downloads are still available when this is false.
FileDownloadAuthRequired0Requires a browser session for every v2 download, including code-based links and anonymous raw-ID links.

Static storage settings remain in deploy-time configuration:

SettingDefaultEffect
LocalFileStoragePathnot setIf set, AuthProxy stores bytes under {LocalFileStoragePath}/{owner}/{fileId} instead of using HTTP file servers.
FileServersnot setHTTP storage backend map. Keys are numeric thresholds; AuthProxy selects the first configured backend whose threshold is lower than fileId.

Integrity Verification​

LevelCheckWhen
v1 per-chunkCRC32Each upload_part call
v1 full fileCRC32After the last missing chunk is accepted
v2 metadataOptional CRC32 stringStored on the pending Chat message when supplied to create_draft

For v1, CRC32 checksums are computed client-side and verified server-side. Any mismatch rejects the call.

Memory Management​

The v1 buffered flow uses ArrayPool<byte> for upload buffers:

  • Buffers are rented from the pool during upload
  • Returned to the pool after the file is written to the selected storage backend
  • Maximum 60 concurrent upload sessions prevent memory exhaustion
  • Each user can have only one active v1 upload session
  • Inactive upload sessions expire quickly and are cleaned up before new uploads are accepted

The v2 streaming upload flow does not buffer the whole file in AuthProxy memory.

Download Limits​

Keep v1 chunked behavior in place. The v1 download path is useful for compatibility and small files, but it is guarded mostly by request/session limits and does not share a bandwidth quota between clients. Turning v1 into a large unthrottled single-response path can let one large file transfer consume proxy memory/IO.

Prefer v2 for new preview and download flows. The v2 stream path uses the AuthProxy bandwidth limiter with dynamic FileDownloadBytesPerSecond and FileUploadBytesPerSecond settings, so configured capacity is shared across clients and a single transfer cannot monopolize the service.

Integration with Chat Module​

File uploads are integrated with the Chat module for message attachments:

  1. AuthProxy creates a pending Chat message with file metadata.
  2. AuthProxy writes bytes to local disk or the configured file server.
  3. AuthProxy marks the Chat file message as complete.
  4. Clients download the attachment through AuthProxy using the Chat message timetick as fileId, or a v2 file code where applicable.

File metadata (name, size, hash) is stored in the Chat module's message records.

Even when LocalFileStoragePath is enabled, AuthProxy still uses the Chat module for file metadata and attachment lifecycle operations.

Error Codes​

CodeConstantDescription
1101SessionNotFoundNo valid session was found where the endpoint requires one.
1301MaxFileSizeFile size is zero or exceeds the 20 MB limit.
1302AlreadyDownloadThe current user already has an active v1 upload session.
1303TooManyDownloadsThe server already has 60 active v1 upload sessions.
1304CRC32AlreadyUsedThe declared CRC32 is already used by an active v1 upload session.
1305InvalidIDUpload ID is invalid or no longer active.
1306InvalidPartChunk number is outside the expected range.
1307WrongPartUploaded chunk body is larger than the expected chunk size.
1308ChecksumErrorChunk CRC32 does not match.
1309InvalidFileNameFile name failed validation.
1310ChartApiFileErrorChat service failed to create or complete the file message.