Repository: envoyproxy/envoy
File: source/extensions/filters/http/basic_auth/basic_auth_filter.cc (C++)

Code before:
    return onDenied("User authentication failed. Expected 'Basic' authentication scheme.",
                    "invalid_scheme_for_basic_auth");
  }

  // Extract and decode the Base64 part of the header.
  absl::string_view base64_token = auth_value.substr(6);
  const std::string decoded = Base64::decodeWithoutPadding(base64_token);

  // The decoded string is in the format "username:password".
  const size_t colon_pos = decoded.find(':');
  if (colon_pos == std::string::npos) {
    return onDenied("User authentication failed. Invalid basic credential format.",
                    "invalid_format_for_basic_auth");
  }

  absl::string_view decoded_view = decoded;
  absl::string_view username = decoded_view.substr(0, colon_pos);
  absl::string_view password = decoded_view.substr(colon_pos + 1);

  if (!config_->validateUser(username, password)) {
    return onDenied("User authentication failed. Invalid username/password combination.",
                    "invalid_credential_for_basic_auth");
  }

  if (!config_->forwardUsernameHeader().empty()) {
    headers.addCopy(Http::LowerCaseString(config_->forwardUsernameHeader()), username);
  }

  config_->stats().allowed_.inc();
  return Http::FilterHeadersStatus::Continue;
}

Http::FilterHeadersStatus BasicAuthFilter::onDenied(absl::string_view body,
                                                    absl::string_view response_code_details) {
  config_->stats().denied_.inc();
  decoder_callbacks_->sendLocalReply(Http::Code::Unauthorized, body, nullptr, absl::nullopt,
                                     response_code_details);
  return Http::FilterHeadersStatus::StopIteration;
}

} // namespace BasicAuth
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy


Review comment (security):
Should this replace the existing header rather than append? What I worry about is an untrusted client setting this header and confusing backends that only look at the first header value when multiple values are present.

Code after:
    return onDenied("User authentication failed. Expected 'Basic' authentication scheme.",
                    "invalid_scheme_for_basic_auth");
  }

  // Extract and decode the Base64 part of the header.
  absl::string_view base64_token = auth_value.substr(6);
  const std::string decoded = Base64::decodeWithoutPadding(base64_token);

  // The decoded string is in the format "username:password".
  const size_t colon_pos = decoded.find(':');
  if (colon_pos == std::string::npos) {
    return onDenied("User authentication failed. Invalid basic credential format.",
                    "invalid_format_for_basic_auth");
  }

  absl::string_view decoded_view = decoded;
  absl::string_view username = decoded_view.substr(0, colon_pos);
  absl::string_view password = decoded_view.substr(colon_pos + 1);

  if (!config_->validateUser(username, password)) {
    return onDenied("User authentication failed. Invalid username/password combination.",
                    "invalid_credential_for_basic_auth");
  }

  if (!config_->forwardUsernameHeader().empty()) {
    headers.setCopy(Http::LowerCaseString(config_->forwardUsernameHeader()), username);
  }

  config_->stats().allowed_.inc();
  return Http::FilterHeadersStatus::Continue;
}

Http::FilterHeadersStatus BasicAuthFilter::onDenied(absl::string_view body,
                                                    absl::string_view response_code_details) {
  config_->stats().denied_.inc();
  decoder_callbacks_->sendLocalReply(Http::Code::Unauthorized, body, nullptr, absl::nullopt,
                                     response_code_details);
  return Http::FilterHeadersStatus::StopIteration;
}

} // namespace BasicAuth
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy