Аутентификация пользователя в Asp.Net Ядро 3 с Saml2 и Okta в качестве поставщика услуг

#asp.net-core #saml #okta

#asp.net-ядро #saml #okta

Вопрос:

Я заменяю нашу собственную службу аутентификации на Okta и нуждаюсь в поддержке единого входа с помощью Saml2 в нашем приложении. Okta можно настроить как поставщика услуг, отправлять SAMLRequest , получать и проверять SAMLResponse . Пока все хорошо: я смог настроить поставщика удостоверений в Okta, получить утверждение, и, согласно журналам, утверждение было успешно проверено, и пользователь автоматически предоставил.

Но что происходит дальше?

  1. В потоке, инициированном Idp, как я могу перенаправить пользователя на URL-адрес приложения? RelayState Настроен ли Idp только для опции? Мне не нравится идея, что всем нашим клиентам придется вносить изменения, если мы изменим URL нашего приложения. Я бы ожидал возможности связать Idp с приложением Okta, поэтому URL-адрес перенаправления будет настроен этим приложением.
  2. После перенаправления на сайт нашего приложения, как мне аутентифицировать пользователя в Asp.Net Ядро? Я реализовал обычай AuthenticationHandler , который считывает sid cookie, установленный Okta при перенаправлении, и извлекает информацию о сеансе из Okta. Оттуда я получаю имя пользователя и создаю Principal . Этот подход работает, но мне кажется неправильным — он слишком ручной, я бы ожидал Okta.Sdk, чтобы сделать это для меня (если это правильный способ аутентификации).
  3. После успешной проверки утверждения возможно ли обменять токен saml на токен OAuth для аутентификации в приложении?

Ответ №1:

Ответы на ваши вопросы, основанные на чистом подходе SAML:

  1. Вы можете использовать единый вход, инициированный SP — вы достигнете той же цели — попасть на какую-либо страницу. Допустим, ваше приложение, действующее в качестве поставщика услуг SAML, находится по my.app.com адресу, и вы хотите, чтобы пользователи заходили на https://my.app.com/foo/bar сайт после их аутентификации. Публикация https://my.app.com/foo/bar для ваших клиентов / пользователей приведет к запуску потока SP-init, когда они в конечном итоге перейдут по этой ссылке. После некоторого обмена сообщениями между IdP и SP пользователь будет выбран в https://my.app.com/foo/bar качестве конечного пункта назначения.

Вы можете реализовать ту же идею с потоком, инициируемым IdP, с помощью дополнительного перенаправления в вашем приложении или выше по течению вашего приложения в вашем стеке. Это иногда называют URL-адресом тщеславия. Например, если URLадрес тщеславия https://my.app.com/home Таким образом, ваше приложение или другой вышестоящий компонент могут преобразовать этот URL-адрес в другой, целевой URL-адрес и перенаправить его на целевой. Целевой URL-адрес может запускать поток, инициированный IdP, с помощью RelayState или поток, инициированный SP, как описано выше. Ваше приложение или вышестоящий компонент должны поддерживать сопоставление между тщеславием и целевыми URL-адресами.

  1. Вы хотите, чтобы Okta был вашим поставщиком идентификационных данных SAML, а ваше приложение — поставщиком услуг SAML. Вы попробовали этот обходной путь на основе файлов cookie, потому что не понимали, как реализовать SAML в вашем .СЕТЕВОЕ приложение.

Okta не предоставляет инструментарий SAML или SDK для .NET apps, выступающие в качестве поставщиков услуг SAML, рекомендуют сторонние библиотеки. ASP.NET не поддерживает SAML из коробки … что является еще одной причиной, по которой Okta рекомендует сторонние приложения. (Microsoft не является одной из этих сторонних сторон). Два популярных, бесплатных и широко используемых варианта — это Sustainsys и ITFoxTec. Взгляните на их документы, выберите один и внедрите его. После этого вам не придется прибегать к обходному пути использования файлов cookie.

  1. После того, как ваше приложение обработает ответ SAML и сгенерирует ответ (если таковой имеется), выполняется этап аутентификации и становится известна личность участника. Теперь ваше приложение может делать все, что вы хотите, в качестве следующего шага, включая получение токена OAuth. Этот следующий шаг не является частью протокола SAML … если только это не новый / новый поток SAML, то есть.

Ответы на ваши вопросы основаны на подходе, при котором вы используете Okta в качестве поставщика услуг SAML и хранилища идентификационных данных с другим поставщиком идентификационных данных SAML. Поток составляет 2 перехода: SAML IdP -> Okta как SAML SP -> ваше приложение.

Ваше приложение должно интегрироваться с Okta через обратный вызов переднего канала (SAML-подобный) или обратный вызов API (OAuth-подобный). Последнее более или менее требует первого, хотя на этом пути есть много вариантов. Приведенные ниже ответы предполагают интерфейс обратного вызова стиль интеграции.

  1. Ваше приложение будет «вызвано» через RelayState, если Okta перенаправит на URL-адрес в RelayState после обработки ответа SAML с первого перехода. Если вы сделаете свой RelayState равным https://my.app.com/foo/bar , то после некоторого обмена данными между IdP и SP и вашим приложением пользователь попадет в https://my.app.com/foo/bar качестве конечного пункта назначения. Ваше приложение должно будет запустить эту последовательность, активировав 1-й переход через поток SAML, инициированный IdP или SP.

  2. В интерфейсном стиле интеграции с обратным вызовом вы будете использовать Microsoft interfaces библиотеки Okta, как показано в примере Okta.

  3. Используя Okta в качестве хранилища идентификационных данных и аутентифицированного участника, вы можете получать токены от Okta с помощью дополнительных шагов. Если ваша цель — включить вызовы API в ваше приложение от третьей стороны через Okta, изучите стиль интеграции обратного вызова API.

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

1. Спасибо за исчерпывающий ответ! Единственное, чего я не могу понять, это какова цель конфигурации IdP Okta? Я имею в виду, что Okta можно настроить как SP, но в чем преимущество этого подхода?

2. Преимущества Okta как SP в настоящее время невелики. Раньше, когда корпоративные архитектуры не были такими зрелыми, а приложения или вышестоящие компоненты не реализовывали все эти модные стандарты, такие как SAML, SCIM или OAuth, Okta действовала как SP, чтобы приложения могли использовать ее в качестве хранилища / службы идентификации через свои API. Вы входите в свое приложение с помощью Google через Okta, аналогично тому, как вы это делали. В настоящее время это устаревший подход.

3. Итак, согласно ссылке, похоже, что должно быть возможно войти в систему с помощью Okta без добавления поддержки Saml в нашем приложении? По крайней мере, в потоке, инициированном SP. И, похоже, должно быть возможно получить токен OAuth после утверждения saml. Я бы предпочел придерживаться этого подхода, поскольку он упрощает разработку. Поскольку мы используем Api Gateway на основе Ocelot framework, использовать эти библиотеки Saml не так просто.

4. В архитектуре, о которой вы думаете, вашему приложению все равно придется реализовывать интерфейс поставщика услуг. За исключением того, что теперь это больше не SAML или другой стандарт идентификации, это комбинация вызовов библиотек Okta с аспектами, связанными с идентификацией, организованными через интерфейсы Microsoft. Конечный результат будет работать в соответствии с вашими пожеланиями; объем работы, который вам придется выполнить в этом случае по сравнению с SAML, будет аналогичным. Ваше приложение будет тесно связано с Okta. Если минусы приемлемы, и вы считаете, что плюсы перевешивают минусы, то этот вариант реализации подойдет вам.

Ответ №2:

Наконец-то я смог заставить его работать так, как я хотел. В настоящее время у меня проблемы с потоком, инициированным Idp, я обновлю ответ, если найду решение.

  1. Сначала настройте Okta вашей организации как Saml Sp для внешнего Saml IdP. Документация по Api находится здесь. Инструкции пользовательского интерфейса здесь. Вы должны получить информацию для SAML PROTOCOL SETTINGS (URI IdP-эмитента, URL-адрес единого входа IdP и сертификат подписи IdP) от вашего внешнего IdP.

  2. Следующий шаг — создание веб-приложения в Okta. Вы Login redirect URIs должны закончить с /authorization-code/callback , иначе вам придется настроить его в CallbackPath свойстве в коде (см. Ниже). Вам не нужно реализовывать эту конечную точку, фреймворк сделает это за вас.

  3. Теперь установите Okta.AspNetCore nuget и добавьте этот код в ConfigureServices метод

      services.AddAuthentication(options =>
     {
         options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
         options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
         options.DefaultChallengeScheme = OktaDefaults.MvcAuthenticationScheme;
     })
     .AddCookie()
     .AddOktaMvc(new OktaMvcOptions
     {
         OktaDomain = "<okta_domain>",
         ClientId = "<app_client_id>",
         ClientSecret = "<app_client_secret>",
         Scope = OktaDefaults.Scope,
         //CallbackPath = "/authorization-code/callback" <= it's default value, change it if required
     });
      

Вы найдете app_client_id и app_client_secret внизу вкладки «Общие» веб-приложения, созданного на шаге 2.

  1. Теперь самая интересная часть. Если вы хотите запустить процесс аутентификации для внутренних пользователей (а не для внешних пользователей Saml Idp), вы вызываете ChallengeAsync метод. Что-то вроде этого:

      [ApiController]
     [Route("[controller]")]
     public class AuthController : ControllerBase
     {
         [HttpGet("login")]
         public async Task Login()
         {
             var properties = new AuthenticationProperties
             {
                 RedirectUri = "/"
             };
    
             await HttpContext.ChallengeAsync(properties);
         }
     }
      

Всякий раз, когда вы нажимаете /auth/login url, вы будете перенаправлены на Okta для аутентификации.

Если вы хотите запустить процесс аутентификации для внешних пользователей Idp, вы должны добавить idp параметр в AuthenticationProperties class.

 [ApiController]
[Route("[controller]")]
public class SsoController : ControllerBase
{
    private readonly ILogger<SsoController> _logger;

    private readonly IOktaClient _oktaClient;

    public SsoController(ILogger<SsoController> logger, IOktaClient oktaClient)
    {
        _logger = logger;
        _oktaClient = oktaClient;
    }

    [HttpGet]
    public async Task Sso([FromQuery] string name)
    {
        var idpProviders = await _oktaClient.IdentityProviders.ListIdentityProviders(q: name, limit: 1).ToListAsync();

        // search is case insensitive and "starts with", and not "exact match"
        var idp = idpProviders.FirstOrDefault(p => p.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));

        if (idp == null)
        {
            _logger.LogWarning($"idp name '{name}' doesn't exist");

            var authenticationProperties = new AuthenticationProperties
            {
                RedirectUri = "/"
            };

            await HttpContext.ForbidAsync(authenticationProperties);
        }
        else
        {
            var properties = new AuthenticationProperties
            {
                RedirectUri = "/",
                Items = { ["idp"] = idp.Id }
            };
            await HttpContext.ChallengeAsync(properties);
        }
    }
}
  

Когда вы нажмете /sso?name=MySamlIdp url, вы будете искать MySamlIdp поставщика удостоверений, определенного на шаге 1, и использовать его идентификатор, чтобы сообщить Okta, куда перенаправить пользователя для аутентификации. IOktaClient Определено в Okta.Sdk nuget.