languagetool-org/languagetool · error · AuthException

Expected Basic Authentication

Error message

Expected Basic Authentication

What it means

GET /v2/users/me authenticates the caller via the HTTP 'Authorization' header (Basic Authentication). handleGetUserInfoRequest checks the header first and throws an AuthException when it is absent, because user information can never be resolved without credentials.

Source

Thrown at languagetool-server/src/main/java/org/languagetool/server/ApiV2.java:360

  /*
   * Provide information on user that requests this, e.g. for add-on to acquire token + other information
   * Expects user + password via HTTP Basic Auth
   */
  private void handleGetUserInfoRequest(HttpExchange httpExchange, Map<String, String> parameters, HTTPServerConfig config) throws Exception {
    if (httpExchange.getRequestMethod().equalsIgnoreCase("options")) {
      ServerTools.setAllowOrigin(httpExchange, allowOriginUrl);
      httpExchange.getResponseHeaders().put("Access-Control-Allow-Methods", Collections.singletonList("GET, OPTIONS"));
      List<String> requestHeaders = httpExchange.getRequestHeaders().get("Access-Control-Request-Headers");
      if (requestHeaders != null) {
        httpExchange.getResponseHeaders().put("Access-Control-Allow-Headers", Collections.singletonList(String.join(", ", requestHeaders)));
      }
      httpExchange.sendResponseHeaders(HttpURLConnection.HTTP_NO_CONTENT, -1);
      ServerMetricsCollector.getInstance().logResponse(HttpURLConnection.HTTP_NO_CONTENT);
    } else {
      ensureGetMethod(httpExchange, "/users/me");
      if (!httpExchange.getRequestHeaders().containsKey("Authorization")) {
        throw new AuthException("Expected Basic Authentication");
      }
      String authParameter = parameters.getOrDefault("authMethod", "password");
      if (!(authParameter.equals("password") || 
            authParameter.equals("apiKey") || 
            authParameter.equals("addonToken"))) {
        throw new IllegalArgumentException("Unknown authMethod: " + authParameter);
      }

      String authHeader = httpExchange.getRequestHeaders().getFirst("Authorization");
      BasicAuthentication basicAuthentication = new BasicAuthentication(authHeader);
      String user = basicAuthentication.getUser();
      String password = basicAuthentication.getPassword();
      UserInfoEntry userInfo = null;

      if (authParameter.equals("password")) {
        userInfo = DatabaseAccess.getInstance().getUserInfoWithPassword(user, password);
      } else if (authParameter.equals("addonToken")) {
        userInfo = DatabaseAccess.getInstance().getUserInfoWithAddonToken(user, password);

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Add an Authorization: Basic base64(user:password) header to the request.
  2. Prefer the header over query-parameter credentials for /users/me, as the header is mandatory here.
  3. Check that redirects (302) preserve the Authorization header or re-attach it manually.
  4. Verify no proxy or CORS preflight configuration strips the header.

Example fix

// before
curl https://server/v2/users/me?username=jane&token=abc
// after
curl -u jane:abc https://server/v2/users/me
Defensive patterns

Strategy: validation

Validate before calling

const headers = { Authorization: 'Basic ' + btoa(user + ':' + token) };
if (!headers.Authorization) throw new Error('users/me requires a Basic Authorization header');

Prevention

When it happens

Trigger: GET /v2/users/me with username/token only as query parameters and no Authorization header; HTTP clients stripping the header after redirects; API explorers that do not send Basic auth headers.

Common situations: Browsers or fetch calls omitting the header; reverse proxies removing Authorization headers; tests built against an anonymous server setup then pointed at the authenticated endpoint.

Understand the failure class

Related errors


AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06). Data as JSON: /api/errors/315d0fea8ca5305e. Report an issue: GitHub.