signalapp/Signal-Server · error · BadRequestException
must not use authenticated connection for anonymous…
Error message
must not use authenticated connection for anonymous operations
What it means
ArchiveController.readAuth (used by endpoints like get-credentials) is anonymous by design: the caller must present ZK backup credentials, not an authenticated account. If an account is present on the request, the server throws BadRequestException('must not use authenticated connection for anonymous operations').
Solutions
- Call the backup read-auth endpoint without account credentials (no Authorization header).
- Configure the HTTP client to omit the auth header for backup/anonymous endpoints.
- Use a dedicated unauthenticated client instance for backup archive operations.
- Check middleware/proxies that might be attaching authentication automatically.
Example fix
// before backupClient.getCredentials(presentation, cdn, withAuthHeader(accountToken)); // after anonymousBackupClient.getCredentials(presentation, signature, cdn); // no account auth
Defensive patterns
Strategy: validation
Validate before calling
if (client.defaults.headers['Authorization']) throw new Error('backup endpoints must be called without account auth'); Type guard
const isCredentialFree = (c) => !c.defaults || !c.defaults.headers || !c.defaults.headers['Authorization'];
Try / catch
try { await backupApi.getReadAuth(presentation, cdn); } catch (e) { if (e.status === 400 && e.message.includes('anonymous operations')) { return anonBackupApi.getReadAuth(presentation, cdn); } throw e; } Prevention
- Keep a dedicated unauthenticated client for backup archive calls
- Check for interceptors/proxies injecting Authorization headers
- Treat backup ZK credentials as the only auth for these endpoints
When it happens
Trigger: Calling the backup read-auth / get-credentials endpoint while the request carries account authentication (Authorization header with an authenticated device), i.e. account Optional is present.
Common situations: Shared HTTP clients that attach the Signal account auth token to every request, proxy layers that inject credentials, or clients mistakenly calling backup endpoints through an authenticated API session.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Operation requires unauthenticated access
- may not provide both group send token and unidentified…
- Only primary device can set backup-id
- Must set at least one of message/media credential requests
- receipt credential presentation verification failed
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/1792e1f91f3a48af.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/ArchiveController.java:423
@ApiResponse(responseCode = "429", description = "Rate limited.")
@ApiResponseZkAuth
@ManagedAsync
public ReadAuthResponse readAuth(
@Auth final Optional<AuthenticatedDevice> account,
@HeaderParam(HttpHeaders.USER_AGENT) final String userAgent,
@Parameter(description = BackupAuthCredentialPresentationHeader.DESCRIPTION, schema = @Schema(implementation = String.class))
@NotNull
@HeaderParam(X_SIGNAL_ZK_AUTH) final ArchiveController.BackupAuthCredentialPresentationHeader presentation,
@Parameter(description = BackupAuthCredentialPresentationSignature.DESCRIPTION, schema = @Schema(implementation = String.class))
@NotNull
@HeaderParam(X_SIGNAL_ZK_AUTH_SIGNATURE) final BackupAuthCredentialPresentationSignature signature,
@NotNull @Parameter(description = "The number of the CDN to get credentials for") @QueryParam("cdn") final Integer cdn)
throws BackupFailedZkAuthenticationException, BackupInvalidArgumentException, BackupPermissionException {
if (account.isPresent()) {
throw new BadRequestException("must not use authenticated connection for anonymous operations");
}
final AuthenticatedBackupUser backupUser =
backupManager.authenticateBackupUser(presentation.presentation, signature.signature, userAgent);
return new ReadAuthResponse(backupManager.generateReadAuth(backupUser, cdn));
}
@GET
@Path("/auth/svrb")
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Generate credentials for SVRB",
description = """
Generate SVRB service credentials. Generated credentials have an expiration time of 1 day (subject to change)
""")
@ApiResponse(responseCode = "200", description = "`JSON` with generated credentials.", useReturnTypeSchema = true)
@ApiResponseZkAuth
@ManagedAsync
public ExternalServiceCredentials svrbAuth(View on GitHub (pinned to 100ab61c82)