Вступление

Язык разметки утверждения безопасности (SAML, произносится SAM-el) - это открытый стандарт для обмена данными аутентификации и авторизации между сторонами, в частности, между поставщиком удостоверений и поставщиком услуг.

Он используется для входа пользователей в приложения на основе их сеансов в другом контексте. Этот стандарт единого входа (SSO) имеет значительные преимущества перед входом в систему с использованием имени пользователя и пароля:

  • Нет необходимости в учетных данных
  • Не нужно запоминать и менять пароли
  • Нет слабых паролей

Большинство организаций уже знают личности пользователей, поскольку они вошли в свой домен Active Directory или интрасеть. Имеет смысл использовать эту информацию для входа пользователей в другие приложения, такие как веб-приложения, и один из наиболее элегантных способов сделать это - использовать SAML.

SAML очень мощный и гибкий, но спецификации может быть довольно мало.

Как работает SAML?

SAML SSO работает путем проверки удостоверения пользователя от поставщика удостоверений, например, из Azure Active Directory, путем проверки удостоверения пользователя из Azure Active Directory (поставщик удостоверений) в веб-приложение (поставщик услуг). Это осуществляется посредством обмена XML-документами с цифровой подписью.

Шаги:

  • Пользователь обращается к веб-приложению (поставщику услуг).
  • Пользователь вводит адрес электронной почты, приложение идентифицирует происхождение пользователя по домену электронной почты, на основе веб-сервера домена (NodeJS) создает запрос SAML и отправляет его поставщику удостоверений (Active Directory Azure), запрашивая аутентификацию.
  • Пользователь либо имеет существующий активный сеанс браузера с поставщиком удостоверений, либо устанавливает его, войдя в систему поставщика удостоверений.
  • Поставщик удостоверений формирует ответ аутентификации в форме XML-документа, содержащего адрес электронной почты пользователя, подписывает его с помощью сертификата X.509 и отправляет эту информацию поставщику услуг.
  • Поставщик услуг, который уже знает поставщика удостоверений и имеет отпечаток сертификата, получает ответ аутентификации и проверяет его с помощью отпечатка сертификата (используя библиотеку паспортов, доступную для NodeJs).
  • После установления личности поставщик услуг выполняет обратный вызов URL-адреса возврата, извлекая адрес электронной почты пользователя и другие сведения.
  • Приложение проверяет личность пользователя, и пользователю предоставляется доступ к приложению.

Диаграмма потока

Реализация

1. Приложение для доступа пользователей

<!--  entryPoint=https://login.microsoftonline.com/123456-xxxx-xxxx-xxxx-123456/saml2 -->
<button class="btn-submit">
  <a :href="config.entryPoint">SSO</a>
</button>

2. Создайте запрос SAML.

const moment = require('moment')
// for deflating SAML request
const pako = require('pako')
const uuid = require('uuid')
.....
const config = {
   // domain of email address for identifying on client side if SAML is enable for same or not
  "domain": "outlook.com",
   // unique entityId while setting up SAML server
  "entityURI": "https://example.com",
  // SAML server login url, can be found in SAML XML file
  "entryPoint": "https://login.microsoftonline.com/123456-xxxx-xxxx-xxxx-123456/saml2",
  // X 509 signing certificate, can be found in SAML XML file
  "certificate": "MIIC8DCCAdigAwIBAgIQaKWqA3vVf7ZPHf5h52SkkjANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDEylNaWNyb3NvZnQgQXMIIC8DCCAdigAwIBAgIQaKWqA3vVf7ZPHf5h52SkkjANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDEylNaWNyb3NvZnQgQX8MIIC8DCCAdigAwIBAgIQaKWqA3vVf7ZPHf5h52SkkjANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDEylNaWNyb3NvZnQgQX",
  // callback url after successfully authentication
  "entityReplyUrl": "https://example.com/api/auth/saml"
}
.....
generateSAMLRequest (config) {
  const SAMLReq = `<samlp:AuthnRequest
    xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
    ID="${config.domain}${uuid.v4()}"
    Version="2.0"
    IssueInstant="${moment().utc().format()}"
    IsPassive="false"
    AssertionConsumerServiceURL="${config.entityReplyUrl}" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
    ForceAuthn="false">
      <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
        ${config.entityURI}
      </Issuer>
      <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">
      </samlp:NameIDPolicy>
    </samlp:AuthnRequest>`
  const deflatedSAMLReq = pako.deflateRaw(SAMLReq)
  const deflatedBase64SAMLReq = Buffer(deflatedSAMLReq).toString('base64')
  const encodedDeflatedBase64SAMLReq = encodeURIComponent(deflatedBase64SAMLReq)
  return ~config.entryPoint.indexOf('?')
    ? `${config.entryPoint}&SAMLRequest=${encodedDeflatedBase64SAMLReq}`
    : `${config.entryPoint}?SAMLRequest=${encodedDeflatedBase64SAMLReq}`
}

3. Идентификация установлена, ответ SAML возвращен.

4. Приложение проверяет ответ SAML и авторизует пользователя.

  • "Документация"

Настроить стратегию для нескольких поставщиков. Вы можете передать параметр getSamlOptions в MultiSamlStrategy, который будет вызываться перед потоками SAML. Passport-SAML передаст объект запроса, чтобы вы могли решить, какая конфигурация подходит.

var MultiSamlStrategy = require('passport-saml/multiSamlStrategy');
.....
passport.use(new MultiSamlStrategy(
  {
    getSamlOptions: function(request, done) {
      findProvider(request, function(err, provider) {
        if (err) {
          return done(err);
        }
        return done(null, provider.configuration);
      });
    }
  },
  function(profile, done) {
    findByEmail(profile.email, function(err, user) {
      if (err) {
        return done(err);
      }
      return done(null, user);
    });
  })
);