static initialize (options) {
    // Make sure all options we need below are set
    options ??= {};
    options.mode ??= ArkimeConfig.get('authMode');
    options.userNameHeader ??= ArkimeConfig.get('userNameHeader');
    options.passwordSecret ??= ArkimeConfig.getFull(options.passwordSecretSection ?? undefined, 'passwordSecret');
    options.serverSecret ??= ArkimeConfig.get('serverSecret');
    options.requiredAuthHeader ??= ArkimeConfig.get('requiredAuthHeader');
    options.requiredAuthHeaderVal ??= ArkimeConfig.get('requiredAuthHeaderVal');
    options.userAutoCreateTmpl ??= ArkimeConfig.get('userAutoCreateTmpl');
    options.userAuthIps = ArkimeConfig.getArray('userAuthIps');
    options.caTrustFile ??= ArkimeConfig.get('caTrustFile');

    options.authConfig ??= {};
    options.authConfig.httpRealm ??= ArkimeConfig.get('httpRealm', 'Moloch');
    options.authConfig.userIdField ??= ArkimeConfig.get('authUserIdField');
    options.authConfig.discoverURL ??= ArkimeConfig.get('authDiscoverURL');
    options.authConfig.clientId ??= ArkimeConfig.get('authClientId');
    options.authConfig.clientSecret ??= ArkimeConfig.get('authClientSecret');
    options.authConfig.redirectURIs ??= ArkimeConfig.get('authRedirectURIs');
    options.authConfig.trustProxy ??= ArkimeConfig.get('authTrustProxy');
    options.authConfig.cookieSameSite ??= ArkimeConfig.get('authCookieSameSite');
    options.authConfig.cookieSecure ??= ArkimeConfig.get('authCookieSecure', true);
    options.authConfig.oidcScope ??= ArkimeConfig.get('authOIDCScope', 'openid');

    if (ArkimeConfig.debug > 1) {
      console.log('Auth.initialize', options);
    }

    if (options.mode === undefined) {
      if (options.userNameHeader) {
        if (options.userNameHeader.match(/^(digest|basic|anonymous|oidc|basic\+oidc|form|basic\+form)$/)) {
          console.log(`WARNING - Using authMode=${options.userNameHeader} setting since userNameHeader set, add to config file to silence this warning.`);
          options.mode = options.userNameHeader;
          delete options.userNameHeader;
        } else {
          console.log('WARNING - Using authMode=header since not set, add to config file to silence this warning.');
          options.mode = 'header';
        }
      } else {
        console.log('WARNING - Using authMode=digest since not set, add to config file to silence this warning.');
        options.mode = 'digest';
      }
    }

    Auth.mode = options.mode;
    Auth.#userNameHeader = options.userNameHeader;
    Auth.#appAdminRole = options.appAdminRole;
    Auth.#basePath = options.basePath ?? '/';
    Auth.#passwordSecretSection = options.passwordSecretSection ?? 'default';
    Auth.passwordSecret = options.passwordSecret ?? 'password';
    Auth.passwordSecret256 = crypto.createHash('sha256').update(Auth.passwordSecret).digest();
    if (options.serverSecret) {
      Auth.#serverSecret = options.serverSecret;
      Auth.#serverSecret256 = crypto.createHash('sha256').update(options.serverSecret).digest();
    } else {
      Auth.#serverSecret = Auth.passwordSecret;
      Auth.#serverSecret256 = Auth.passwordSecret256;
    }
    Auth.#requiredAuthHeader = options.requiredAuthHeader;
    Auth.#requiredAuthHeaderVal = options.requiredAuthHeaderVal?.split(',').map(s => s.trim()).filter(s => s !== '');
    Auth.#userAutoCreateTmpl = options.userAutoCreateTmpl;
    Auth.#userAuthIps = new iptrie.IPTrie();
    Auth.#s2sRegressionTests = options.s2sRegressionTests;
    Auth.#authConfig = options.authConfig;
    Auth.#caTrustCerts = ArkimeUtil.certificateFileToArray(options.caTrustFile);

    if (Auth.#app && Auth.#authConfig?.trustProxy !== undefined) {
      Auth.#doTrustProxy();
    }

    if (options.userAuthIps) {
      for (const cidr of options.userAuthIps) {
        const parts = cidr.split('/');
        if (parts[0].includes(':')) {
          Auth.#userAuthIps.add(parts[0], +(parts[1] ?? 128), 1);
        } else {
          Auth.#userAuthIps.add(`::ffff:${parts[0]}`, 96 + +(parts[1] ?? 32), 1);
        }
      }
    } else if (Auth.mode === 'header') {
      Auth.#userAuthIps.add('::ffff:127.0.0.0', 96 + 8, 1);
      Auth.#userAuthIps.add('::1', 128, 1);
    } else {
      Auth.#userAuthIps.add('::', 0, 1);
    }

    function check (field, str) {
      if (!ArkimeUtil.isString(Auth.#authConfig[field])) {
        console.log(`ERROR - ${str} missing from config file`);
        process.exit();
      }
    }

    Auth.#logoutUrl = ArkimeConfig.get('logoutUrl');
    const addBasic = Auth.mode.startsWith('basic+');
    if (addBasic) {
      Auth.mode = Auth.mode.slice(6);
    }

    let sessionAuth = false;
    switch (Auth.mode) {
    case 'anonymous':
      Auth.#strategies = ['anonymousWithDB'];
      break;
    case 'basic':
      check('httpRealm');
      Auth.#strategies = ['basic'];
      break;
    case 'digest':
      check('httpRealm');
      Auth.#strategies = ['digest'];
      break;
    case 'oidc':
      check('userIdField', 'authUserIdField');
      check('discoverURL', 'authDiscoverURL');
      check('clientId', 'authClientId');
      check('clientSecret', 'authClientSecret');
      Auth.#strategies = ['oidc'];
      Auth.#passportAuthOptions = { session: true, failureRedirect: `${Auth.#basePath}api/login`, scope: Auth.#authConfig.oidcScope };
      sessionAuth = true;
      break;
    case 'form':
      Auth.#strategies = ['form'];
      Auth.#passportAuthOptions = { session: true, failureRedirect: `${Auth.#basePath}auth` };
      sessionAuth = true;
      Auth.#logoutUrl ??= `${Auth.#basePath}logout`;
      break;
    case 'headerOnly':
      Auth.#strategies = ['header'];
      break;
    case 'header':
    case 'header+digest':
      Auth.#strategies = ['header', 'digest'];
      break;
    case 'header+basic':
      Auth.#strategies = ['header', 'basic'];
      break;
    case 's2s':
      Auth.#strategies = ['s2s'];
      break;
    case 'regressionTests':
      Auth.#strategies = ['regressionTests'];
      Auth.regressionTests = true;
      break;
    default:
      console.log('ERROR - unknown authMode', ArkimeUtil.sanitizeStr(Auth.mode));
      process.exit(1);
    }

    // Add basic to beginning
    if (addBasic) {
      Auth.#strategies.unshift('basic');
    }

    // Add s2s to beginning
    if (options.s2s && !Auth.#strategies.includes('s2s')) {
      Auth.#strategies.unshift('s2s');
    }

    if (ArkimeConfig.debug > 0) {
      console.log('AUTH strategies', Auth.#strategies);
    }

    Auth.#registerStrategies();

    // If sessionAuth is required enable the express and passport sessions
    if (sessionAuth) {
      Auth.#authRouter.use(expressSession({
        name: 'ARKIME-SID',
        secret: Auth.passwordSecret + Auth.#serverSecret,
        resave: false,
        saveUninitialized: true,
        cookie: { path: Auth.#basePath, secure: Auth.#authConfig.cookieSecure, sameSite: Auth.#authConfig.cookieSameSite ?? 'Lax', maxAge: 24 * 60 * 60 * 1000 },
        store: new ESStore({ })
      }));
      Auth.#authRouter.use(passport.initialize());
      Auth.#authRouter.use(passport.session());

      if (Auth.mode === 'form') {
        const fs = require('fs');
        const path = require('path');

        Auth.#authRouter.get('/logo.png', (req, res) => {
          res.sendFile(path.join(__dirname, '../assets/Arkime_Logo_Mark_FullGradient.png'));
        });

        Auth.#authRouter.get('/auth', (req, res) => {
          // User is not authenticated, show the login form
          let html = fs.readFileSync(path.join(__dirname, '/vueapp/formAuth.html'), 'utf-8');
          html = html.toString().replace(/@@BASEHREF@@/g, Auth.#basePath)
            .replace(/@@MESSAGE@@/g, ArkimeConfig.get('loginMessage', ''))
            .replace(/@@OGURL@@/g, req.session.ogurl ?? Auth.#basePath);
          return res.send(html);
        });

        Auth.#authRouter.post('/logout', (req, res, next) => {
          req.logout((err) => {
            if (err) { return next(err); }
            return res.redirect(`${Auth.#basePath}auth`);
          });
        });
      }

      // only save the userId to passport session
      passport.serializeUser(function (user, done) {
        done(null, user.userId);
      });

      // load the user using the userid in the passport session
      passport.deserializeUser(function (userId, done) {
        User.getUserCache(userId, async (err, user) => {
          if (err) { return done('ERROR - passport-session getUser - user: ' + userId + ' err:' + err); }
          if (!user) { return done(userId + " doesn't exist"); }
          if (!user.enabled) { return done(userId + ' not enabled'); }

          user.setLastUsed();
          return done(null, user);
        });
      });
    } else {
      Auth.#authRouter.use(passport.initialize());
    }
  }
Initialize the Auth subsystem
@param {string} options.mode=digest What auth mode to run in
@param {string} options.basePath=/ What the web base path is for the app
@param {string} options.userNameHeader In header auth mode, which http header has the user id
@param {string} options.passwordSecret=password For basic/digest mode, what password to use to encrypt the password hash
@param {string} options.serverSecret=passwordSecret What password is used to encrypt S2S auth
@param {string} options.requiredAuthHeader In header auth mode, another header can be required
@param {string} options.requiredAuthHeaderVal In header auth mode, a comma separated list of values for requiredAuthHeader, if none are matched the user will not be authorized
@param {string} options.userAutoCreateTmpl A javascript string function that is used to create users that don't exist
@param {boolean} options.s2s Support s2s auth also
@param {object} options.authConfig options specific to each auth mode
@param {object} options.caTrustFile Optional path to CA certificate file to use for external authentication