{"record":{"id":"1792e1f91f3a48af","repo":"signalapp/Signal-Server","slug":"must-not-use-authenticated-connection-for-anonymou","errorCode":null,"errorMessage":"must not use authenticated connection for anonymous operations","messagePattern":"must not use authenticated connection for anonymous operations","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/ArchiveController.java","lineNumber":423,"sourceCode":"  @ApiResponse(responseCode = \"429\", description = \"Rate limited.\")\n  @ApiResponseZkAuth\n  @ManagedAsync\n  public ReadAuthResponse readAuth(\n      @Auth final Optional<AuthenticatedDevice> account,\n      @HeaderParam(HttpHeaders.USER_AGENT) final String userAgent,\n\n      @Parameter(description = BackupAuthCredentialPresentationHeader.DESCRIPTION, schema = @Schema(implementation = String.class))\n      @NotNull\n      @HeaderParam(X_SIGNAL_ZK_AUTH) final ArchiveController.BackupAuthCredentialPresentationHeader presentation,\n\n      @Parameter(description = BackupAuthCredentialPresentationSignature.DESCRIPTION, schema = @Schema(implementation = String.class))\n      @NotNull\n      @HeaderParam(X_SIGNAL_ZK_AUTH_SIGNATURE) final BackupAuthCredentialPresentationSignature signature,\n\n      @NotNull @Parameter(description = \"The number of the CDN to get credentials for\") @QueryParam(\"cdn\") final Integer cdn)\n      throws BackupFailedZkAuthenticationException, BackupInvalidArgumentException, BackupPermissionException {\n    if (account.isPresent()) {\n      throw new BadRequestException(\"must not use authenticated connection for anonymous operations\");\n    }\n    final AuthenticatedBackupUser backupUser =\n        backupManager.authenticateBackupUser(presentation.presentation, signature.signature, userAgent);\n    return new ReadAuthResponse(backupManager.generateReadAuth(backupUser, cdn));\n  }\n\n  @GET\n  @Path(\"/auth/svrb\")\n  @Produces(MediaType.APPLICATION_JSON)\n  @Operation(\n      summary = \"Generate credentials for SVRB\",\n      description = \"\"\"\n          Generate SVRB service credentials. Generated credentials have an expiration time of 1 day (subject to change)\n          \"\"\")\n  @ApiResponse(responseCode = \"200\", description = \"`JSON` with generated credentials.\", useReturnTypeSchema = true)\n  @ApiResponseZkAuth\n  @ManagedAsync\n  public ExternalServiceCredentials svrbAuth(","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ArchiveController.java#L405-L441","documentation":"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').","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nbackupClient.getCredentials(presentation, cdn, withAuthHeader(accountToken));\n// after\nanonymousBackupClient.getCredentials(presentation, signature, cdn); // no account auth","handlingStrategy":"validation","validationCode":"if (client.defaults.headers['Authorization']) throw new Error('backup endpoints must be called without account auth');","typeGuard":"const isCredentialFree = (c) => !c.defaults || !c.defaults.headers || !c.defaults.headers['Authorization'];","tryCatchPattern":"try { await backupApi.getReadAuth(presentation, cdn); } catch (e) { if (e.status === 400 && e.message.includes('anonymous operations')) { return anonBackupApi.getReadAuth(presentation, cdn); } throw e; }","preventionTips":["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"],"tags":["backup","authentication","bad-request"],"backgroundTag":"authentication-required","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}