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

  1. Call the backup read-auth endpoint without account credentials (no Authorization header).
  2. Configure the HTTP client to omit the auth header for backup/anonymous endpoints.
  3. Use a dedicated unauthenticated client instance for backup archive operations.
  4. 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

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

Related errors


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)