{"record":{"id":"d564e51fe2adacb0","repo":"signalapp/Signal-Server","slug":"endpoint-requires-unauthenticated-access","errorCode":null,"errorMessage":"Endpoint requires unauthenticated access","messagePattern":"Endpoint requires unauthenticated access","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/KeyTransparencyController.java","lineNumber":252,"sourceCode":"  }\n\n  private void handleKeyTransparencyServiceError(final StatusRuntimeException exception) {\n    final Status.Code code = exception.getStatus().getCode();\n    final String description = exception.getStatus().getDescription();\n    switch (code) {\n      case NOT_FOUND -> throw new NotFoundException(description);\n      case PERMISSION_DENIED -> throw new ForbiddenException(description);\n      case INVALID_ARGUMENT -> throw new WebApplicationException(description, 422);\n      default -> {\n        LOGGER.error(\"Unexpected error calling key transparency service\", exception);\n        throw new ServerErrorException(Response.Status.INTERNAL_SERVER_ERROR, exception);\n      }\n    }\n  }\n\n  private void requireNotAuthenticated(final Optional<AuthenticatedDevice> authenticatedAccount) {\n    if (authenticatedAccount.isPresent()) {\n      throw new BadRequestException(\"Endpoint requires unauthenticated access\");\n    }\n  }\n\n}\n","sourceCodeStart":234,"sourceCodeEnd":257,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/KeyTransparencyController.java#L234-L257","documentation":"KeyTransparencyController.requireNotAuthenticated enforces that certain key-transparency endpoints (search, monitor, getDistinguishedKey) are called WITHOUT account credentials, mirroring the privacy design of the protocol. If an AuthenticatedDevice is present in the request context it throws BadRequestException('Endpoint requires unauthenticated access').","triggerScenarios":"Calling GET/POST on the key-transparency search, monitor, or distinguished-key endpoints while sending an Authorization header / authenticated session that resolves to an AuthenticatedDevice.","commonSituations":"A shared HTTP client automatically attaches auth headers to all requests; developers testing with their logged-in session; proxy/SDK that injects credentials globally.","solutions":["Remove the Authorization header (and any credentials) from requests to key-transparency search, monitor, and distinguished-key endpoints","Use a dedicated unauthenticated HTTP client for these endpoints","If using an interceptor that adds auth globally, exclude these paths","Call the endpoints from an unauthenticated (e.g. new, credential-less) connection"],"exampleFix":"// before\nauthenticatedClient.get(\"/v1/key-transparency/search?...\"); // 400 BadRequestException\n// after\nRequest req = new Request.Builder().url(searchUrl).build(); // no Authorization header\nunauthenticatedClient.newCall(req).execute();","handlingStrategy":"validation","validationCode":"if (request.getHeader(\"Authorization\") != null) {\n  throw new IllegalStateException(\"key-transparency endpoints must be called without credentials\");\n}","typeGuard":"boolean isUnauthenticated(Request r) { return r.header(\"Authorization\") == null; }","tryCatchPattern":"try {\n  return client.search(...);\n} catch (WebApplicationException e) {\n  if (e.getResponse().getStatus() == 400 && e.getMessage().contains(\"unauthenticated\")) {\n    // drop Authorization header and retry once\n  } else throw e;\n}","preventionTips":["Use a separate unauthenticated HTTP client for key-transparency calls","Exclude these paths from global auth interceptors","Never reuse logged-in sessions for anonymous endpoints"],"tags":["authentication","bad-request-400","key-transparency"],"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"}