Как использовать аналогичные функции saml2aws в приложении Nodejs

#node.js #amazon-web-services #aws-sdk #saml #adfs2.0

#node.js #amazon-веб-сервисы #aws-sdk #saml #adfs2.0

Вопрос:

Я ищу предложения о том, как аутентифицировать сервер NodeJS в моей учетной записи AWS, используя ADFS с именем пользователя и паролем, за которым следует аутентификация MFA, чтобы получить временный ключ доступа и секретный ключ для дальнейших действий.

Для справки, я пытаюсь добиться того, что saml2aws делает в интерфейсе командной строки. Здесь вместо интерфейса командной строки я хотел бы сделать это на моем сервере NodeJS.

Любые предложения будут высоко оценены.

Пока у меня есть приведенный ниже код, он выполняет за меня половину работы, например, вводит учетные данные ADFS и отправляет запрос, а затем запускает уведомление для подтверждения в моем приложении Microsoft authenticator. Но мой код не ожидает ответа этого аутентификатора. Он просто получает тело веб-страницы, где он ожидает подтверждения аутентификатора. Мне нужен какой-то способ заставить его ждать ответа аутентификатора, который даст мне ответ утверждения SAML после отправки кредита ADFS.

 require("dotenv").config();
const AWS = require("aws-sdk");
const sts = new AWS.STS();
const request = require("request").defaults({ jar: true });
const url = require("url");
const JSSoup = require("jssoup").default;
const os = require("os");
const path = require("path");
const fs = require("fs");
const ini = require("ini");

const HOME = os.homedir();
const CONFIG_FILE = path.join(HOME, ".aws", "credentials");

let IDP_URL = process.env.IDP_URL;
let IDP_USER = process.env.IDP_USER;
let IDP_PASS = process.env.IDP_PASS;
let AWS_PROFILE = process.env.AWS_PROFILE;
console.log(IDP_USER);
function base64encode(data) {
  return Buffer.from(data, "utf8").toString("base64");
}

function base64decode(data) {
  return Buffer.from(data, "base64").toString("utf8");
}

function httpGet(url) {
  const options = {
    url,
  };
  return new Promise((resolve, reject) => {
    request.get(options, (error, response, body) => {
      if (error) {
        reject(error);
      } else {
        resolve({ response, body });
      }
    });
  });
}

function httpPost(url, form) {
  const options = {
    url,
    form,
    followAllRedirects: true,
  };
  return new Promise((resolve, reject) => {
    request.post(options, (error, response, body) => {
      if (error) {
        reject(error);
      } else {
        resolve({ response, body });
      }
    });
  });
}

function getLoginData(body) {
  const soup = new JSSoup(body);
  const forms = soup.findAll("form");
  const form = forms.find((form) => form.attrs.id === "loginForm");

  if (!form) {
    throw new Error("LOGIN_FORM_NOT_FOUND");
  }

  const action = url.resolve(IDP_URL, form.attrs.action);
  const inputs = {};

  for (const input of form.findAll("input")) {
    const name = input.attrs.name || "";
    const value = input.attrs.value || "";
    const namelc = name.toLowerCase();

    if (namelc.includes("user")) {
      inputs[name] = IDP_USER;
    } else if (namelc.includes("email")) {
      inputs[name] = IDP_USER;
    } else if (namelc.includes("pass")) {
      inputs[name] = IDP_PASS;
    } else {
      inputs[name] = value;
    }
  }

  return { action, inputs };
}

function getSAMLAssertion(body) {
  const soup = new JSSoup(body);
  const inputs = soup.findAll("input");
  const saml = inputs.find((input) => input.attrs.name === "SAMLResponse");

  if (!saml) {
    throw new Error("SAML_ASSERTION_NOT_FOUND");
  }

  return base64decode(saml.attrs.value);
}

function getSAMLRoles(saml) {
  const soup = new JSSoup(saml);
  const roles = soup
    .findAll("AttributeValue")
    .filter((value) => {
      return (
        value.parent amp;amp;
        value.parent.name === "Attribute" amp;amp;
        value.parent.attrs amp;amp;
        value.parent.attrs.Name ===
          "https://aws.amazon.com/SAML/Attributes/Role"
      );
    })
    .map((value) => {
      const [provider, role] = (value.text || "").split(",");
      return { provider, role };
    });

  if (!roles[0]) {
    throw new Error("SAML_ROLE_NOT_FOUND");
  }

  return roles;
}

async function getSTSToken(provider, role, assertion) {
  const params = {
    DurationSeconds: 3600,
    PrincipalArn: provider,
    RoleArn: role,
    SAMLAssertion: base64encode(assertion),
  };
  return new Promise((resolve, reject) => {
    sts.assumeRoleWithSAML(params, (error, data) => {
      if (error) {
        reject(error);
      } else {
        resolve(data);
      }
    });
  });
}

function saveSTSToken(filename, profile, sts) {
  const readFile = () => {
    try {
      return ini.decode(fs.readFileSync(filename, "utf-8"));
    } catch (e) {
      return {};
    }
  };

  const writeFile = (config) => {
    fs.writeFileSync(filename, ini.encode(config, { whitespace: true }));
  };

  const config = readFile();
  const section = config[profile] || {};
  const credentials = sts.Credentials || {};

  section.aws_access_key_id = credentials.AccessKeyId;
  section.aws_secret_access_key = credentials.SecretAccessKey;
  section.aws_session_token = credentials.SessionToken;

  config[profile] = section;
  writeFile(config);
}

function checkUsage() {
  if (!IDP_URL) {
    throw new Error("IDP_URL not set!");
  }
  if (!IDP_USER) {
    throw new Error("IDP_USER not set!");
  }
  if (!IDP_PASS) {
    throw new Error("IDP_PASS not set!");
  }
  if (!AWS_PROFILE) {
    throw new Error("AWS_PROFILE not set!");
  }
}

(async function main() {
  try {
    console.log(`aws-saml-session started.`);
    checkUsage();

    console.log(`Logging into SAML provider...`);
    const resp1 = await httpGet(IDP_URL);
    const data = getLoginData(resp1.body);
    // This is the place its not waiting and getting me body of the page 
    //where it needs to wait and get me SAML response
    ```const resp2 = await httpPost(data.action, data.inputs);
    const saml = getSAMLAssertion(resp2.body);```

    const roles = getSAMLRoles(saml);

    const { provider, role } = roles[0];

    console.log(`Assuming role: ${role}...`);
    const sts = await getSTSToken(provider, role, saml);

    console.log(`Saving credentials: ${AWS_PROFILE}...`);
    saveSTSToken(CONFIG_FILE, AWS_PROFILE, sts);

    console.log("Done.");
  } catch (e) {
    console.log("ERROR:", e.message);
  }
})();
  

Комментарии:

1. Что вы уже пробовали? В конце концов, это серия запросов, перенаправлений и синтаксического анализа HTML. С чем-нибудь конкретным у вас возникли проблемы?

2. @AleksanderWons Я отредактировал свой вопрос с учетом проделанной до сих пор работы и места, где я застрял. Мне действительно нужно это, чтобы сделать это. Пожалуйста, помогите

3. Этот запрос завершен.

4. Не могли бы вы, пожалуйста, поделиться @PrashantGupta, как это сработало в итоге?