CWMP Auth Not Working with External API Validation

Hi everyone, I am fairly new to GenieACS. I am trying to set up cwmp.auth in GenieACS to authenticate CPEs via an external API call to a server. The goal is to have the username validated by the external server and optionally I can skip the password for CPE → ACS communication. I want the external server to determine whether the authentication succeeds with eather or username and password.

I’ve configured cwmp.auth in the GenieACS settings by the docs CPE Authentication — GenieACS Documentation 1.2.13 documentation.

The hardcoded username and password work, but my external validation setup fails. Here’s what I’ve tried:

CWMP Auth Configurations Attempted:

for the key I tried:

  • cwmp.auth
  • cwmp.connectionRequestAuth

for value:

AUTH(Device.ManagementServer.Username, EXT("auth", "validateCredentials", Device.ManagementServer.Username, Device.ManagementServer.Password)) 
OR 
AUTH(InternetGatewayDevice.ManagementServer.Username, EXT("auth", "validateCredentials", InternetGatewayDevice.ManagementServer.Username, InternetGatewayDevice.ManagementServer.Password))

// Another variation just for username and many more combinations:
AUTH(EXT("auth", "validateCredentials", Device.ManagementServer.Username, Device.ManagementServer.Username), "")

I’ve experimented with multiple variations, but none worked for the external validation.

Provision Script Test:

When I run the external script in a provision file, it seems to execute correctly, and i get the response data. However in cwmp.auth, I suspect the issue is with GenieACS not properly loading the EXT file, even after restarting the services using sudo systemctl restart genieacs-cwmp etc…

My Environment:

  • Host machine OS: Ubuntu 24
  • Virtual machine OS: Ubuntu 22 - running genieacs and i have set the ports as follows
  • GenieACS Versions: 1.2.12 and 1.2.13 (tested both)

Virtual Machine settings

Name Protocol Host Port Guest Port Description
ssh TCP 2222 22 SSH access to the Ubuntu VM from the host machine.
api (nbi) TCP 7557 7557 Access the GenieACS API running inside the VM.
tr069 (cwmp) TCP 7547 7547 TR-069 communication port for devices and GenieACS server.
ui TCP 4000 3000 Access the GenieACS UI via a different host machine port.

GenieACS ENV file

GENIEACS_CWMP_ACCESS_LOG_FILE=/var/log/genieacs/genieacs-cwmp-access.log
GENIEACS_NBI_ACCESS_LOG_FILE=/var/log/genieacs/genieacs-nbi-access.log
GENIEACS_FS_ACCESS_LOG_FILE=/var/log/genieacs/genieacs-fs-access.log
GENIEACS_UI_ACCESS_LOG_FILE=/var/log/genieacs/genieacs-ui-access.log
GENIEACS_DEBUG_FILE=/var/log/genieacs/genieacs-debug.yaml
NODE_OPTIONS=--enable-source-maps
GENIEACS_EXT_DIR=/opt/genieacs/ext
GENIEACS_UI_JWT_SECRET=2da7ad9de2b340630aee9cc0526df793526569b814021e47aaedd2bbef400b199f1dd5d1e52b02d814a9d9>
GENIEACS_DEBUG_FILE=/var/log/genieacs/cpe.debug

External Script variation:

my external script is located at /opt/genieacs/ext/auth.js:

"use strict";

const http = require("http"); // tried http and https

function validateCredentials(args, callback) {
  const [username, password] = args;

  const options = {
    hostname: "localhost", 
    port: 3000, // or without
    path: `/validate?username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`,
    method: "GET"
  };

  const req = http.request(options, (res) => {
    let data = "";

    res.on("data", (chunk) => {
      data += chunk;
    });

    res.on("end", () => {
      if (res.statusCode === 200) {
        const response = JSON.parse(data);
        callback(null, response.valid); // API returns { "valid": true/false } when running from provision script
      } else {
        callback(new Error(`API returned status code ${res.statusCode}`));
      }
    });
  });

  req.on("error", (err) => {
    callback(err);
  });

  req.end();
}

exports.validateCredentials = validateCredentials;

External Server Implementation:

Here’s the corresponding endpoint on my external server:

ruby code / tried with express.js to test as well

class Api::V2::Tr069AcsAuthController < Api::V2::ApplicationController
  def validate_acs_credentials
    username = params[:username]

    if username.blank?
      return render json: { valid: false }, status: :unauthorized
    end

    usr = User.find_by(username: username)

    render json: { valid: usr.present? }, status: :ok
  end
end

TL;DR

cwmp.auth is not working with ext script. I want to let user enter his username and password to connect to the server, I would like first to check with external server can he, and then allow or reject the cpe on the server. Need advice.

What I’ve Tried:

  1. Verified the external script by running it in a provision file – seems to work fine.
  2. Restarted GenieACS services multiple times to ensure cwmp.auth configuration is reloaded.
  3. Tested different filenames, function names, and response formats in the external script (e.g., returning a boolean or a string that matches expected auth values).

I would appreciate any advice on why cwmp.auth might not be working with the external script or what else I can try to troubleshoot, and fix this issue. I would not like to use NGINX, XMPP or other reverse proxies / middleware if possible.

Thank you in advance for your help!