Re[3]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 22.01.14 13:35
Оценка: 72 (4)
Здравствуйте, WSA, Вы писали:

WSA>У меня STS успешно выдаёт токен, я его получаю клиентом, расшифровываю и назначаю Thread.CurrentPrincipal на клиенте....

WSA>А вот дальше... прям стена... ни как не удаётся настроить WCF сервисы, чтобы принимали этот токен.

И не будет, на сколько я понимаю, в меру своих знаний — базовый WCF не позволяет такие манипуляции.

Вы можете сделать один из следующих варианов:

1. Использовать только "чистый" WCF.
Тогда вам понадобится ws2007FederationHttpBinding. Там примерно следующая схема:

2. Использовать Windows Identity Foundation.
Именно она позволит сделать сценарий именно как у вас. Только обратите внимание, что начиная с .Net 4.5 классы WIF вроде как с некоторыми небольшими изменениями перекочевали в основной фремворк. Хотя "старую" WIF (которая поставляется отдельной библиотекой) по-прежнему можно использовать.


Для более предметной беседы, ответьте, плиз:
Re: Re: Вариант №2 "Ручное" получение токена
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 02.02.14 09:25
Оценка: 98 (3)
Сергей, добрый день

Прошу прощения, что приходится писать вот так "в неделю по чайной ложке" — увы, быстрее не получается: нужно все вспомнить и проверить + разобраться с особенностями Thinktecture IdentityServer (мы сами используем самописное решение) + выбрать время для собственно статьи.

К счастью, как я вижу, у вас и так все движется не плохо. Однако, раз уж обещал — закончу всю серию (надеюсь, пойдет уже шибче).

Итак, в первом варианте мы все переложили на WCF и только передавали ему credentials. В этот раз мы по сути полностью повторим весь процесс работы с токенами, который реализует WCF, но сделаем это "вручную": будем получать сертификат явно, а затем явно же передавать его сервисам.

Собственно, у нас уже почти все для этого готово, нужно только указать явно 1 endpoint в настройках клиента — по нему мы будем обращаться к STS.

Я все же немного перепишу app.confug для клиента (удалю все лишнее):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <ws2007FederationHttpBinding>
        <binding name="Service">
          <!--    Здесь мы не указываем параметров для Issuer, т.к. обращаться будем сами явно 
                однако, если оставить просто такие настройки, WCF начнет показывать окно выбора токена
                чтобы это предотвратить нужно или указать специальный параметр в <endpointBehaivor>
                или аналогичный параметр в коде (мы сделаем так)
          -->
          <security mode="TransportWithMessageCredential" />
        </binding>
      </ws2007FederationHttpBinding>
      <ws2007HttpBinding>
        <binding name="ForIssue">
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="UserName" establishSecurityContext="false" />
          </security>
        </binding>
      </ws2007HttpBinding>
    </bindings>
    <client>
      <endpoint address="https://мой_WCF_Service/WcfService/Service.svc"
          binding="ws2007FederationHttpBinding" bindingConfiguration="Service"
          contract="ServiceReference.Service" name="Service" />
      <!-- Мы добавляем новый endpoint -->
      <endpoint name="STS"
                address="https://мой_STS/IdSrv/issue/wstrust/mixed/username"
                binding="ws2007HttpBinding" bindingConfiguration="ForIssue"
                contract="System.ServiceModel.Security.IWSTrustChannelContract" />
    </client>
  </system.serviceModel>
</configuration>


Сам код клиента довольно прост:
// Формируем канал до STS - именно здесь мы указываем credentials пользователя
var stsChannelFactory = new WSTrustChannelFactory("STS");
stsChannelFactory.Credentials.UserName.UserName = "User1";
stsChannelFactory.Credentials.UserName.Password = "123456";
var stsClient = stsChannelFactory.CreateChannel();

// Запрашиваем токен
var token = stsClient.Issue(
    new RequestSecurityToken(RequestTypes.Issue)
    {
        // Указываем для какого RP нам нужен токен
        AppliesTo = new EndpointReference("https://мой_WCF_Service/WcfService/Service.svc")
    });

// Теперь канал уже до самого сервиса
var wcfServiceFactory = new ChannelFactory<ServiceReference.Service>("Service");
wcfServiceFactory.Credentials.UseIdentityConfiguration = true;
// Подавляем показ окошка выбора токена
wcfServiceFactory.Credentials.SupportInteractive = false;

// Собственно получение канала, у которого в качестве Credentials указан полученный ранее токен.
var channel = wcfServiceFactory.CreateChannelWithIssuedToken(token);
Console.WriteLine(channel.GetData());


Все довольно просто, однако, как обычно, есть несколько подводных камней.

1. Можно ли теперь забыть credentials пользователя и хранить в клиенте только токен?
Вопрос, как мне кажется, очень спорный. Вот мои соображения...
Токен — это тоже своеобразная форма credentials. Ведь по сути как работает токен (часто для аналогии приводят выдачу, например, водительских прав):

В чем же здесь подвох?
Во-первых, токен — это snapshot информации аутентифицирующего центра на момент вашего обращения к нему. А ситуация с момента выдачи может уже поменяться. Например, у пользователя поменялись права доступа или его вообще заблокировали в системе, а в токене все еще старая информация (это при случае, что права/роли вы тоже передаете в токене — иногда это реально удобно).
Во-вторых, токен, как и любые другие credentials может быть перехвачен. Это не значит, что токен плох! Это значит, хранение и передача токена ничуть не безопаснее хранения и передачи любого другого вида credentials.
Наконец, имеется, пусть и теоретическая, вероятность дискредитации сертификата, которым подписываются токены. Т.е. в какой-то момент мы уже не можем доверять токенам, подписанным сертификатом X, т.к. помимо нашего аутентификационного центра есть еще кто-то, кто может генерировать и подписывать токены.

Как с этим бороться?
Собственно, на сегодня используют простой подход — каждый токен имеет ограниченный срок действия. Какой конкретно — определяется администраторами системы. Я встречал токены сроком действия от нескольких минут до полумесяца. Срок действия указывается в самом токене, т.е. он тоже защищен подписью от подделки.
На самом деле, время действия это рекомендация для RP, которой они могут и не следовать и продолжать принимать просроченные токены. Но тогда они сами себе вредители.

2. Можно ли с одним токеном обращаться к разным RP?
Если вы посмотрите на структуру типичного SAML-токена, то увидите примерно следующее (нужная строчка выделена):
<Assertion ID="_311184a5-4c69-4c49-aa9f-a6b3bee96e32" IssueInstant="2014-02-02T08:16:37.209Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
  <Issuer>STS</Issuer>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <!-- Тут информация о подписи STS - это не интересно -->
  </Signature>
  <Subject>
    <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"></SubjectConfirmation>
  </Subject>
  <Conditions NotBefore="2014-02-02T08:16:36.933Z" NotOnOrAfter="2014-02-02T08:36:36.933Z">
    <AudienceRestriction>
      <Audience>https://мой_WCF_Service/WcfService/Service.svc</Audience>
    </AudienceRestriction>
  </Conditions>
  <AttributeStatement>
    <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name">
      <AttributeValue>User1</AttributeValue>
    </Attribute>
    <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress">
      <AttributeValue>user1@companydomain.com</AttributeValue>
    </Attribute>
    <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/webpage">
      <AttributeValue>http://myhome.home</AttributeValue>
    </Attribute>
  </AttributeStatement>
  <AuthnStatement AuthnInstant="2014-02-02T08:16:36.450Z">
    <AuthnContext>
      <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef>
    </AuthnContext>
  </AuthnStatement>
</Assertion>

Т.е. привязка к конкретному RP идет всего по одному параметру.

Этот параметр можно игнорировать, указав в настройках WIF на стороне RP опцию <audienceUris mode="Never" /> (я о ней писал еще в первом варианте)

Помимо самого SAML, зависимость от RP может выражаться в том, какими ключами шифруется токен при передаче на RP. Здесь мы можем: или совсем отключить шифрование при передаче токена, или указать что для всех RP используется один набор ключей и шифровать им (ну и на каждый сервер RP установить нужный сертификат).

Это что касается технических ограничений, однако, есть еще и соображения предметного плана.
Вообще говоря, STS не обязан выдавать на все запросы одни и те же токены. В самом простом случае для каждого RP он может формировать собственный набор claims (например, условно говоря, ФИО и дату рождения вы будуту передавать только нескольким доверенным RP, а остальным — только логин). В этом случае все одназначно: набор токенов для каждого сервиса — свой.

3. Можно ли, раз уж мы получили токен на клиенте, воспользоваться информацией из него?
Об этом я напишу немного погодя, в отдельном сообщении — а это и так большим получилось
Re: Вариант №1 "Чистый" WCF
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 25.01.14 14:27
Оценка: 45 (3)
Сергей, добрый день.

Что ж, я в принципе разобрался со всеми сложностями и напастями, что мне встречались и теперь могу рассказать, что же у меня получилось.
На текущий момент, у меня готов первый вариант, из тех, что я описывал, т.е. практически всю работу на себя берет WCF (правда в .Net 4.5 работа так или иначе идет с использованием WIF, но от вас лично не потребует ни строчки кода для WIF — разве что вы начнете делать какие-то кастомные расширения).

Для начала, чтобы у нас было единое понимание того, что происходит, позволю себе небольшое описание процесса (см. картинку):

0. Пользователь вводит свои UserName и Password (U/P)
1. Клиент пытается подключиться к WCF Service используя переданные ему U/P. Однако, инфраструктура WCF, анализирует указанные ей настройки и выясняет, что от нее требуется работать по Federation протоколу, а это значит, что вместо прямого обращения WCF Service, она формирует специальный Security Token Request (STR), который отсылает на STS. При этом в качестве credentials в SRT используются наши U/P.
2. STS получает STR и первым делом анализирует credentials (надо сказать, что в общем случае это не обязательно должны быть именно UserName/Password, а любой допустимый способ аутентификации Windows, Certificate, … — вплоть до полученного ранее сертификата! Но мы рассматриваем простейший случай).
Получив и аутентифицировав STR, наш STS начинает его анализировать и обрабатывать.
Делает он это следующим образом:
3. Клиент, получивший STRes проверяет подпись STS, достает свои данные и зашифрованный пакет для RP и уже с этим пакетом делает запрос на установление соединения с WCF Service
4. WCF Service получает запрос, достает оттуда зашифрованный пакет, расшифровывает его своим закрытым ключом и проверяет подпись STS. Если все в порядке, то устанавливается сессия с клиентом, а данные из токена используются как данные аутентифицированного пользователя.

Примерно так выглядит общий процесс (я тоже владею далеко не всем предметом в совершенстве, так что местами мог слегка и наврать).
Если мы его внимательно просмотрим, то получится, что для успешного решения нам желательно выполнить следующее:

Теперь перейдем к настройке STS.
Тут нужно не забыть сделать следующее:

Все, на этом настройки STS закончены.

Однако, прежде чем перейти к настройкам WSF Service, сделаем следующее: выйдем на страницу Home (она в корне нашего STS или можно через закладки), а оттуда на Application integration. На этой странице есть 2 важных для нас адреса: "WS-Trust metadata" и "WS-Trust mixed mode security (user name)" — сами мы будем использовать только 1-ый, а второй чисто для проверки, что все метаданные верно подхватились.

Переходим к нашему WCF Service!
Здесь все делается в настройке, поэтому я просто приведу config с комментариями:
<?xml version="1.0"?>
<configuration>
  <configSections>
    <!-- Обязательно указать эту секцию!!! Почему ее нет по умолчанию в machine.confug - ума не приложу -->
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
  </configSections>

  <system.serviceModel>
    <bindings>
      <ws2007FederationHttpBinding>
        <binding name="Default">
          <!--    Мы используем TransportWithMessageCredential безопасность, чтобы не заморачиваться с настройками,
                так как SSL мы уже настроили.
                В качестве issuerMetadata нужно указать адрес, который содержится на странице Application integration
                и называется "WS-Trust metadata"
          -->
          <security mode="TransportWithMessageCredential">
            <message>
              <issuerMetadata address="https://мой_STS/IdSrv/issue/wstrust/mex" />
            </message>
          </security>
        </binding>
      </ws2007FederationHttpBinding>
    </bindings>
    
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- Это чтобы не возиться с настройкой клиента... -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
          <!-- Здесь мы указываем, что в pipeline WCF будет работать WIF, конфигурация которой (DefaultIConf) описывается ниже-->
          <serviceCredentials identityConfiguration="DefaultIConf" useIdentityConfiguration="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    
    <services>
      <!-- Ну и сам сервис. Т.к. хостим под IIS, адрес не указываем, binding и contract - обязательно -->
      <service name="WcfService.Service">
        <endpoint binding="ws2007FederationHttpBinding" bindingConfiguration="Default" contract="WcfService.Service" />
      </service>
    </services>
  </system.serviceModel>

  <!-- Здесь указываются все настройки WIF, т.е., собственно, все настройки обработки токенов -->
  <system.identityModel>

    <!-- Это требование MS в WIF - создавать и использовать отдельные конфигурации -->
    <identityConfiguration name="DefaultIConf">
      <securityTokenHandlers>

        <securityTokenHandlerConfiguration>
          <!-- Здесь мы указываем, что не будем проверять какому именно серверу предназначен пришедший токен
                если все же хочется проверять, то нужно будет добавить к узлу audienceUris дочерний.
                Что-то типа <add value="https://мой_хост/WcfService/Service.svc"/> - главное следить, чтобы он совпадал 
                с тем, что указано в поле Realm/Scope Name на STS
          -->
          <audienceUris mode="Never" />

          <!--    Здесь указывается класс, который будет для пришедшего токена искать закрытый ключ для расшифровки.
                К сожалению, я не нашел как для этого параметра указать настройку (т.е. в каком хранилище искать сертификаты)
                это можно сделать, если задавть настройку программно (например, делать свой наследник текущего класса и там 
                в конструкторе заполнять нужные параметры, а здесь указывать наш класс).
                Однако по умолчанию этот класс ищет сертификаты в Local Machine / My так что просто положим его туда
          -->
          <serviceTokenResolver
              type="System.IdentityModel.Tokens.X509CertificateStoreTokenResolver, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>

          <!--    Этот тэг задает список доверенных STS, т.е. тех от которых мы готовы получать токены.
                Опознаем мы их довольно просто - по thumbprint сертификата, которым они ставят подпись
                Информация о сертификате подписи добавляется в саму подпись, поэтому ничего регистрировать 
                дополнительно не надо, надо только указать строку thumbprint (имя произвольное)
          -->
          <issuerNameRegistry>
            <trustedIssuers>
              <add name="STS" thumbprint="9B14A55B4A435YYY8C88D14779F57193905C2"/>
            </trustedIssuers>
          </issuerNameRegistry>

        </securityTokenHandlerConfiguration>

      </securityTokenHandlers>
    </identityConfiguration>
  </system.identityModel>

</configuration>


Теперь переходим к клиенту.
Здесь тоже ничего специально делать не надо, просто создать ServiceReference для нашего WCF Service.

После создания перепроверьте, что все настройки соединений подхватились правильно.
Я привожу свой с комментариями, на что обратить внимание.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.serviceModel>
    <bindings>
      <!-- !!! Должно быть 2 биндинга: ws2007FederationHttpBinding и ws2007HttpBinding  !!! -->
      <ws2007FederationHttpBinding>
        <binding name="WS2007FederationHttpBinding_Service">
          <security mode="TransportWithMessageCredential">
            <message>
              <!-- !!! Обратите внимание на адрес. Он должен совпадать с "WS-Trust mixed mode security (user name)" 
                  на странице Application integration  !!! 
              -->
              <issuer address="https://мой_STS/IdSrv/issue/wstrust/mixed/username"
                  binding="ws2007HttpBinding" bindingConfiguration="https://мой_STS/IdSrv/issue/wstrust/mixed/username" />
              <issuerMetadata address="https://мой_STS/IdSrv/issue/wstrust/mex" />
              <tokenRequestParameters>
                <trust:SecondaryParameters xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
                  <!-- Тут я выпущу кусок -->
                </trust:SecondaryParameters>
              </tokenRequestParameters>
            </message>
          </security>
        </binding>
      </ws2007FederationHttpBinding>

      <ws2007HttpBinding>
        <!--  !!! Модель безопасности должна быть TransportWithMessageCredential
              а  clientCredentialType должен стоять в UserName  !!! -->
        <binding name="https://мой_STS/IdSrv/issue/wstrust/mixed/username">
          <security mode="TransportWithMessageCredential">
            <transport clientCredentialType="None" />
            <message clientCredentialType="UserName" establishSecurityContext="false" />
          </security>
        </binding>
      </ws2007HttpBinding>
    </bindings>

    <client>
      <!-- Ну а тут просто адрес наших WCF Service и указание биндинга с контрактом -->
      <endpoint address="https://мой_WCF_Service/WcfService/Service.svc"
          binding="ws2007FederationHttpBinding" bindingConfiguration="WS2007FederationHttpBinding_Service"
          contract="ServiceReference.Service" name="WS2007FederationHttpBinding_Service" />
    </client>
  </system.serviceModel>
</configuration>


Ну все, настройка закончена

Теперь пишем пробный код клиента
(создаем proxy и указываем User/Password в качестве credentials)
using (var client = new ServiceReference.ServiceClient())
{
    client.ClientCredentials.UserName.UserName = "User1";
    client.ClientCredentials.UserName.Password = "123456";

    Console.WriteLine(client.GetData());
}


Ну и осталось привести код сервиса
Там ничего сложного, просто показываю как добраться до переданных Claims. В отличие от WIF 3.5, WIF 4.5 не меняет Principal текущего потока (хотя может где-то и есть такая опция), а предлагает как и в целом в WCF все получать через OperationContext

namespace WcfService
{
    [ServiceContract]
    public class Service
    {
        [OperationContract]
        public string GetData()
        {
            var princinpal = OperationContext.Current.ClaimsPrincipal;
            var name = princinpal.Identity.Name;
            var email = princinpal.FindFirst(ClaimTypes.Email);
            var webPage = princinpal.FindFirst(ClaimTypes.Webpage);

            return string.Format("User: {0}, mail: {1}, web page: {2}", name, 
                email == null ? "" : email.Value,
                webPage == null ? "" : webPage.Value);
        }
    }
}



Надеюсь, все получилось достаточно доходчиво.
Если останутся вопросы — спрашивайте без смущения (если я не буду долго отвечать — спросите в личку, я не постоянно читаю RSDN, а так должно прийти уведомление).

По поводу второго варианта (отдельное получение токена и затем вручную добавление его в вызовы сервисов в качестве Credentials) — он тоже должен быть возможен (мы именно так и работаем), но с ходу накидать пример на WCF/WIF 4.5 я не смогу, а больше пока времени на исследования, увы, не осталось.
Re[2]: Вариант №1 "Чистый" WCF
От: WSA  
Дата: 20.02.14 11:10
Оценка: 12 (1)
Михаил, здравтсвуйте.
Нашёл тут одну полезную фичу, возможно вам будет полезно тоже.

МР>Ну и осталось привести код сервиса

МР>Там ничего сложного, просто показываю как добраться до переданных Claims. В отличие от WIF 3.5, WIF 4.5 не меняет Principal текущего потока (хотя может где-то и есть такая опция), а предлагает как и в целом в WCF все получать через OperationContext

МР>
МР>namespace WcfService
МР>{
МР>    [ServiceContract]
МР>    public class Service
МР>    {
МР>        [OperationContract]
МР>        public string GetData()
МР>        {
МР>            var princinpal = OperationContext.Current.ClaimsPrincipal;
МР>            var name = princinpal.Identity.Name;
МР>            var email = princinpal.FindFirst(ClaimTypes.Email);
МР>            var webPage = princinpal.FindFirst(ClaimTypes.Webpage);

МР>            return string.Format("User: {0}, mail: {1}, web page: {2}", name, 
МР>                email == null ? "" : email.Value,
МР>                webPage == null ? "" : webPage.Value);
МР>        }
МР>    }
МР>}
МР>




Когда в настройках WCF-сервиса мы укажем

<behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- ... -->
          <serviceCredentials  useIdentityConfiguration="true"/>
<serviceAuthorization principalPermissionMode="Always"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>


в результате появляется возможность авторизировать доступ пользователя к операции WCF-сервиса через аттрибуты операции:


        [PrincipalPermission(SecurityAction.Demand, Role = @"admin")]
        public List<Items> GetItems()



В добавок System.Threading.Thread.CurrentPrincipal становится вместо WindowsPrincipal — ClaimsPrincipal'ом


С уважением, Сергей
Re[6]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 23.01.14 11:29
Оценка: 3 (1)
Добрый день, Сергей

Давайте так — я попробую вечерком посмотреть на:

Возможно, смогу подсказать что-то более конкретное (если это будет еще актуально на тот момент).

Сразу оговорюсь — ваша схема вполне работоспособна, т.к. мы сами работаем по схожей. Разница только в том, что: а) токенов мы получаем несколько — в них разная информация для разных случаев, у нас Web, а не Desktop клиент (поэтому часть токенов мы получаем по HttpRedirect, а что-то по SOAP).
Но нюансов мы насобирали там просто невозможное количество, поэтому давать советы типа "посмотрите тут, это должно работать", я воздержусь, прежде чем не перепроверю.

Идет?
Re[2]: Вариант №2.1 Получение информации из токена на клиенте
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 02.02.14 10:36
Оценка: 3 (1)
Продолжим.

Еще раз продублирую вопрос:
3. Можно ли, раз уж мы получили токен на клиенте, воспользоваться информацией из него?

Можно, но "в лоб" это сделать не получится.
Дело в том, что настройки по умолчанию для Thinktecture IdentityServer (а точнее, для WIF), таковы, что все токены выданные по WS-Trust по умолчанию шифруются. Т.е. вместо узла с тоеном вы получите в ответе вот такой XML:
<EncryptedAssertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
  <xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" 
                      xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <!-- Информация о ключе шифрования  -->
    </KeyInfo>
    <xenc:CipherData>
      <xenc:CipherValue>OQFpRRqqa5z8Rkgfs ... e9ltVY=</xenc:CipherValue>
    </xenc:CipherData>
  </xenc:EncryptedData>
</EncryptedAssertion>


Какие есть варианты?

Я предлагаю пойти по 3-у варианту!

Для начала создадим отдельную RP для клиента. Из всех настроек нам нужен только URI (Realm), ну и, конечно, сертификата шифрования мы не указываем, т.к. шифровать и не планируем.


Будет ли этого достаточно? Увы нет. Если мы попытаемся запросить сертификат для этой RP — STS упадет с внутренней ошибкой, что не указаны Ecripting credetials (ну или что-то в этом ключе). Почему так происходит?
Для этого надо понять как STS от Thinktecture решает когда токен надо шифровать (на самом деле, они это поведение не меняли — оно тянется с самой WIF) и, соответсвенно, начинает искать сертификат шифрования, коий благополоучно не находит и валится. Упрощенный алгоритм выглядит так:

Чтобы решить первое, мы пойдем в настройке Thinktecture сервера и снимем галочку принудительного шифрования:


Для того, чтобы реализовать второе, нужно вспомнить, что в токене, помимо информации о пользователе могут передавать ключи шифрования. Всего есть три варианта настройки, касающейся этих ключей:

Думаю, тип ключей можно как-то задать и настройкой на STS, но мы пойдем по пути явного указания их в запросе токена.
Таким образом, наш запрос токена будет выглядить так:
var client = stsChannelFactory.CreateChannel();
var token = client.Issue(
    new RequestSecurityToken(RequestTypes.Issue)
    {
        AppliesTo = new EndpointReference("urn:Client"),
        KeyType = KeyTypes.Bearer
    });



Итак, теперь к нам пришел сертификат, правда лежит он там пока что в виде XML. Конечно, можно разобрать её руками, но, раз WIF все умеет и так, то сложим все на неё.
Нужно только иметь в виду, что не позволяет просто "прочитать токен", она может только "проверить валидность и прочитать токен", а для проверки валидности ей нужно указать каким сертификатом будем подписываться токен.

Для этого дополним наш app.config:
<configuration>
  <configSections>
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
  </configSections>
  
  <system.identityModel>
    <identityConfiguration name="ClientConf">
      <securityTokenHandlers>
        <securityTokenHandlerConfiguration>
          <audienceUris mode="Never" />
          <issuerNameRegistry>
            <trustedIssuers>
              <add name="STS" thumbprint="9B14A55B4AB96F5D7188C88D14779F57193905C2"/>
            </trustedIssuers>
          </issuerNameRegistry>
        </securityTokenHandlerConfiguration>
      </securityTokenHandlers>
    </identityConfiguration>
  </system.identityModel>
  
  <system.serviceModel>
   <!-- А здесь без изменений -->
  </system.serviceModel>
</configuration>


Полный пример на запрос и чтение токена выглядит так:
var stsChannelFactory = new WSTrustChannelFactory("STS");
stsChannelFactory.Credentials.UserName.UserName = "User1";
stsChannelFactory.Credentials.UserName.Password = "123456";

var client = stsChannelFactory.CreateChannel();
var token = client.Issue(
    new RequestSecurityToken(RequestTypes.Issue)
    {
        AppliesTo = new EndpointReference("urn:Client"),
        KeyType = KeyTypes.Bearer
    }
    );

var identityConfiguration = new IdentityConfiguration("ClientConf");
// Нужный нам SAML токен лежит в token.TokenXml (ну и берем от него OuterXml, т.к. будем все парсить)
var xmlReader = XmlReader.Create(new StringReader(((GenericXmlSecurityToken)token).TokenXml.OuterXml));

var samlToken = identityConfiguration.SecurityTokenHandlers.ReadToken(xmlReader);
var claimsCollection = identityConfiguration.SecurityTokenHandlers.ValidateToken(samlToken)[0];

// В принципе получать именно principal не обязательно, но т.к. он используется везде, то и здесь сделаем так же
var principal = new ClaimsPrincipal(new ClaimsIdentity(claimsCollection));


Ну и под занавес — можно ли этот токен передавать на RP?
В принципе, если мы не будем использовать шифрование на уровне сообщений (а будем на уровне транспорта), и не будем проверять какой именно RP предназначался токен — то почему-бы и нет.
Нужно только внести ряд небольших исправлений в настройки биндингом на сервере и клиенте (чтобы они стали допускать токены с BearerKey).

web.config на серевере RP (изменения полужирным):
<system.serviceModel>
    <bindings>
      <ws2007FederationHttpBinding>
        <binding name="Default">
                   <security mode="TransportWithMessageCredential">
            <message issuedKeyType="BearerKey">
              <issuerMetadata address="https://мой_STS/IdSrv/issue/wstrust/mex" />
            </message>
          </security>
        </binding>
      </ws2007FederationHttpBinding>
    </bindings>
 </system.identityModel>


Аналогично в app.config на клиенте (изменения полужирным)
<ws2007FederationHttpBinding>
  <binding name="Service">
    <security mode="TransportWithMessageCredential" >
      <message issuedKeyType="BearerKey"/>
    </security>
  </binding>
</ws2007FederationHttpBinding>


Все, теперь можно везде использовать оди и тот же токен.

Другой вопрос — на сколько это оправдано. Но тут уже к архитекторам конкретной системы.
Re[2]: Вариант №2 "Ручное" получение токена
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 02.02.14 10:46
Оценка: 3 (1)
Есть еще и 3-ий вариант, так называемое делегирование — когда вы получаете токен нужного пользователя на клиенте, но не передаете его явно на каждый RP, а используете для получения токенов от STS и уже с теми токенами образаетесь к RP.

Я не описал его потому что:

Я все же попытаюсь описать и этот вариант. Надеюсь, другие участники форума не будут против периодической активности в данной ветке..
Re: Вариант №3 Делегирование (имперсонация) на базе токенов
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 08.02.14 06:21
Оценка: 3 (1)
Еще раз добрый день, Сергей

Продолжаем изучать WIF и Thinktecture IdentityServer (кстати, отдельное спасибо за наводку на этот продукт — случись сейчас мне делать заново SSO, я бы, наверняка, не раздумывая выбрал его, а не стал бы разрабатывать полностью свое решение на "голом" WIF).

Итак, в чем идея третьего подхода? Она примерно такова:

Сразу же стоит оговориться, что в качестве "Пользователя B" не обязательно должен выступать человек. Это может быть, например, приложение, т.е. пользователь проходит аутентификацию, а затем приложение обращается от его имени к сервисам.
Понятно, что для Desktop приложений это схема выглядит несколько притянутой за уши, просто потому, что credentials клиентского ПО вечь практически эфемерная (их получить можно практически всегда), а вот для серверных приложений (например, когда пользователь работает с Web-фронтендом, который может обращаться к разным сервисам от его имени) уже приобретает смысл.

Итак, попробуем реализовать эту схему. В качестве "Пользователя B" у нас будет приложение.

Сразу же возникает вопрос — какие credentials использовать для аутентификации приложения. В принципе, никто не мешает делать это с помощью стандартных User/Password, однако как-то так сложилось, что для этой задачи чаще используются сертификаты (может просто мне такие решения чаще попадались, не знаю, принципиальной разницы, имхо, тут нет).

Однако, в случае использования Thinktecture мы натыкаемся на один тонкий момент: у него единое пространство пользователей, части из которых можно дать право имперсонации. Нам же нужен вариант с 2-я непересекающимися группами:

К сожалению, я не нашел варианта по-простому сделать это на базе Thinktecture. Логика обработки запросов на делегирование там жестко прописана в классе Thinktecture.IdentityServer.TokenService.TokenService, а способа поменять его через конфигурацию, к сожалению, нет (хотя в целом, точек расширения ребята оставили довольно много).
Т.е. сделать это можно даже не спускаясь до уровня WIF, но придется немного покопаться в коде Thinktecture

Второй момент состоит в том, что UI связывания пользователей и сертификатов почему-то не вынесен на главную страницу, хотя все для его работы есть. Но это не проблема, нужно просто знать что найти его можно по пути https://сервер STS/путь на сервере/Admin/ClientCertificate

В общем, делаем следующее:
1. Создаем пользователя (напримен с именем Desktop Client)

2. Регистрируем для него сертификат (напоминаю, что путь до страницы с этой формой придется ввести руками)

3. И говорим, что данный пользователь имеет право имперсонации (это делается на странице Identity Delegation)


Теперь займемся клиентом. В app.config нам нужно будет указать новую настройку биндинга для аутентификации по сертификату (я привожу только секцию <system.serviceModel>, важные места — полужирным)
<system.serviceModel>
  <bindings>
    <ws2007FederationHttpBinding>
      <binding name="Service">
        <security mode="TransportWithMessageCredential" >
          <message issuedKeyType="BearerKey">
            <issuer address="https://мой_STS/IdSrv/issue/wstrust/mixed/certificate"
                    binding="ws2007HttpBinding" bindingConfiguration="ForActAs" />
          </message>
        </security>
      </binding>
    </ws2007FederationHttpBinding>
    <ws2007HttpBinding>
      <binding name="ForIssue">
        <security mode="TransportWithMessageCredential">
          <message clientCredentialType="UserName" establishSecurityContext="false" />
        </security>
      </binding>
      <binding name="ForActAs">
        <security mode="TransportWithMessageCredential">
          <message clientCredentialType="Certificate" establishSecurityContext="false" />
        </security>
      </binding>
    </ws2007HttpBinding>
  </bindings>
  <client>
    <endpoint address="https://мой_WCF_Service/WcfService/Service.svc"
        binding="ws2007FederationHttpBinding" bindingConfiguration="Service"
        contract="ServiceReference.Service" name="Service" />
    <endpoint name="STS"
              address="https://мой_STS/IdSrv/issue/wstrust/mixed/username"
              binding="ws2007HttpBinding" bindingConfiguration="ForIssue"
              contract="System.ServiceModel.Security.IWSTrustChannelContract" />
  </client>
</system.serviceModel>


А это код клиента:
var stsChannelFactory = new WSTrustChannelFactory("STS");
stsChannelFactory.Credentials.UserName.UserName = "User1";
stsChannelFactory.Credentials.UserName.Password = "123456";

var client = stsChannelFactory.CreateChannel();
var token = client.Issue(
    new RequestSecurityToken(RequestTypes.Issue)
    {
        AppliesTo = new EndpointReference("urn:Client"),
        KeyType = KeyTypes.Bearer
    }
    );

// Здесь можно токен распаковать и получить данные на клиенте

var wcfServiceFactory = new ChannelFactory<ServiceReference.Service>("Service");
wcfServiceFactory.Credentials.UseIdentityConfiguration = true;
var cert = GetClientCertificate();
wcfServiceFactory.Credentials.ClientCertificate.Certificate = cert;

var channel = wcfServiceFactory.CreateChannelWithActAsToken(token);
Console.WriteLine(channel.GetData());


Процедура получения сертификата GetClientCertificate() может брать его хоть из хранилища, хоть из ресурсов приложения.
Например, самый простой вариант (с поиском по захордкоженному thumbprint в хранилище):
public X509Certificate2 GetClientCertificate()
{
    X509Store store = null;
    try
    {
        store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadOnly);
        return
            store.Certificates.Find(X509FindType.FindByThumbprint, "19B14A554EDHYF5D7188C88014779F57193905C2", false)
                .OfType<X509Certificate2>()
                .Single();
    }
    finally
    {
        if (store != null)
            store.Close();
    }
}


Вот, что получилось. Увы, вариант не идеальный — одна из проблем, что исходные токены можно получать прямо на имя пользователя Desktop Client (для серверного решения ключ был бы закрыт от посторонних, а на клиенте это не получится).
Если покопать в коде сервера, все это решить можно, но работы на порядок больше, конечно.
Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 16.01.14 10:11
Оценка:
Здравствуйте!

1. Загружается основный модуль программы, который содержит логику аутентификации на STS.
2. После успешной аутентификации подключаются другие модули.
3. В каждом модуле находится proxy-client для доступа к WCF — сервисам.





Необходимо реализовать механизм, чтобы после единого ввода логина и пароля и получения Токена безопасности от STS, можно было аутентифицироваться на прочих WCF-сервисах, на которые идёт обращение из модулей без передачи
туда логина-пароля, а при помощи ранее полученного токена.


С уважением,
Сергей
Re: Аутентификация rich-клиента на нескольких WCF-сервисах
От: Аноним  
Дата: 19.01.14 05:41
Оценка:
Здравствуйте, WSA, Вы писали:

Угу, схема нормальная. А вопрос какой?
Re[2]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 22.01.14 11:00
Оценка:
А>Угу, схема нормальная. А вопрос какой?

Да проблнма в том, что далее схемы дело не идёт )))
Ни как не удаётся заставить работать схему.


У меня STS успешно выдаёт токен, я его получаю клиентом, расшифровываю и назначаю Thread.CurrentPrincipal на клиенте....
А вот дальше... прям стена... ни как не удаётся настроить WCF сервисы, чтобы принимали этот токен.
Re[4]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 22.01.14 13:40
Оценка:
МР>Здравствуйте, WSA, Вы писали:

Немного поправлюсь... Почему-то подумал, что у вас сайт и обращение к STS идет через HTTP Redirect.

Собственно, раз у вас rich клиент, то с большой вероятностью обращение к STS идет поверх SOAP. Осталось понять по какому именно протоколу: WS-Federation или SAML (это не только язык разметки токенов, но и протокол передачи из, чтобы не путать его еще часто называют SAML-P) у вас идет обращение, а также возвращает ли токен закрытые ключи для шифрования канала (без этого ws2007FederationHttpBinding не будет работать).
Re[5]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 23.01.14 07:41
Оценка:
Михаил, здравствуйте. Спасибо за интерес к теме и информативные комментарии.

>> Какой .Net используете

.Net 4.5. И это вносит дополнительную головную боль. Как вы заметили, в 4.5 WIF перекочевал во вреймворк, да и к томуже в другой неймспейс. в 4.5 это System.IdentityModel

>> Что используете в качестве STS (AD, свое решение — тогда на чем, сторонний продукт)

Как STS использую Thinktecture Identity Server v2

>> Используете ли WIF

Да, использую, но пока что не очень осознанно... Ничего не работает, я не знаю поэтому что на что влияет.

>> WS-Federation или SAML

В качестве формата утверждений SAML 2.0. Как протокол обмена как с WIF.... пока ничего не работает пробую все варианты из инета... в котором блин все примеры не для 4.5


С уважением,
Сергей
Re[7]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 23.01.14 11:43
Оценка:
Здравствуйте, Михаил Романов, Вы писали:

МР>Идет?


Будет очень здорово!
Re[8]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 23.01.14 11:52
Оценка:
Кстати, Михаил, может проще если я вам дам адрес этого STS и админские права, смодете поковыряться в уже работающем тестовом STS thinktecture? Мой email: zergy@list.ru
Re: Аутентификация rich-клиента на нескольких WCF-сервисах
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 23.01.14 12:44
Оценка:
Здравствуйте, WSA, Вы писали:

WSA>Необходимо реализовать механизм, чтобы после единого ввода логина и пароля и получения Токена безопасности от STS, можно было аутентифицироваться на прочих WCF-сервисах, на которые идёт обращение из модулей без передачи

WSA>туда логина-пароля, а при помощи ранее полученного токена.

http://msdn.microsoft.com/en-us/library/aa355045(v=vs.110).aspx уже изучал?
... << RSDN@Home 1.2.0 alpha 5 rev. 100 on Windows 8 6.2.9200.0>>
AVK Blog
Re[2]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 23.01.14 12:51
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>http://msdn.microsoft.com/en-us/library/aa355045(v=vs.110).aspx уже изучал?


Да, изучал очень много статей про федерацию, но и там куча нюансов... результат пока не достиг.
Re: Аутентификация rich-клиента на нескольких WCF-сервисах
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 24.01.14 09:28
Оценка:
Сергей, добрый день.

Я смог установить и настроить Thinktecture Server (кстати, если я не путаю, один из его авторов достаточно активный участник форумов MSDN по WIF и Identity, я даже пользовался как-то его советами).
А также мне удалось разобраться с большей частью нововведений (точнее изменений) в WIF 4.5 и построить, хоть и с некоторыми "костылями", работающий пример по первому описанному мною сценарию (когда вы сами руками токен не получаете, а всем занимается только инфраструктура WCF+WIF — сейчас они почти неотделимы).

К сожалению, у меня катастрофически отсутсвует время, чтобы написать подробный ответ и пример (а за одно разобраться, почему же потребовались костыли — по идее все должно работать и без них...)

Сергей, на сколько вас жмет время? Можете ли вы подождать до понедельника с этой задачей?
Если да, то я постараюсь за выходные все еще раз перепроверить и сделать детальное описание того, что я узнал.
Re[2]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 24.01.14 09:55
Оценка:
Здравствуйте Михаил.
МР>Сергей, на сколько вас жмет время? Можете ли вы подождать до понедельника с этой задачей?

Мне даже не удобно отвечать )) Ну естественно подожду. Я всё ещё эксперементирую, но пример от вас, воплотившего подобный сценарий в жизнь, это очень круто. Буду ждать с нетерпением. Я подготовлю к понедельнику солюшен с клиентом, который получает и дешефрует токен, может быть вам будет интересно взглянуть. Куда бы отправить? Если вы напишите мне на zergy@list.ru я отправлю мылом.
Re[2]: Вариант №1 "Чистый" WCF
От: WSA  
Дата: 25.01.14 15:00
Оценка:
Приветствую Михаил.

С ума сойти! Сейчас бум курить...
Re[3]: Вариант №1 "Чистый" WCF
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 27.01.14 07:52
Оценка:
Здравствуйте, WSA, Вы писали:

WSA>Сейчас бум курить...


Потом отпишите о результатах, по возможности.
Я постараюсь в течение недели выделить время и сделать еще один пример — с явным получением токена и дальнешей работой через него. Но обещать не буду — времени не хватает ни на что, как обычно.
Re[4]: Вариант №1 "Чистый" WCF
От: WSA  
Дата: 27.01.14 08:30
Оценка:
Михаил, отправил вам личное сообщение, но не понял тут как его судьбу просдедить. Вы получили что-то от меня?
Re[2]: Вариант №1 "Чистый" WCF
От: WSA  
Дата: 27.01.14 11:39
Оценка:
Приветствую Михаил,

Проект с сервисом создаю по шаблону VS2012: WCF Service Application.
После редактирования конфига с данными, приведёнными в статье для WCF сервиса и попытки обратиться к сервису через браузер, возникает ошибка
"Could not find a base address that matches scheme https for the endpoint with binding WS2007FederationHttpBinding. Registered base address schemes are [http]."

Что я делаю не так?
Re[3]: Вариант №1 "Чистый" WCF
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 27.01.14 12:22
Оценка:
Здравствуйте, WSA, Вы писали:

WSA>Что я делаю не так?

Обычно такая ошибка возникает, когда ост сервиса не может найти https среди доступных ему биндингов сайта (не биндингов WCF, а биндингов IIS-сайта!!!).
На сколько я помню, по умолчанию проект WCF Application хостится под IIS Express, у которого по умолчанию же SSL не настроен.

К сожалению, при всей удобстве работы с IIS Express, в данном конкретном случае я рекомендую переключиться на полноценный IIS — как минимум там проще и нагляднее настраивать SSL.
Re[4]: Вариант №1 "Чистый" WCF
От: WSA  
Дата: 27.01.14 12:26
Оценка:
МР>К сожалению, при всей удобстве работы с IIS Express, в данном конкретном случае я рекомендую переключиться на полноценный IIS — как минимум там проще и нагляднее настраивать SSL.

К сожалению в моём случае не получится перейти на IIS, т.к. используются self-hosted WCF — сервисы, которые крутятся в windows — службе.
В данном контекстк, видимо нужно будет укказать более явно адрес конечной точки... А сертификат руками привязать к порту?
Re[2]: Вариант №1 "Чистый" WCF
От: WSA  
Дата: 30.01.14 07:58
Оценка:
Михаил, здравствуйте.

Кажется получается.

2 Сервиса, натсроенные с той же конфигурацией, что приведена в вашем описании вполне себе аутентифицируют клиента по переданному токену, без указания пароля.
Обращаюсь из клиента примерно так:


public void RequestSecuredWCFOne(GenericXmlSecurityToken token){
            using (var cf = new ChannelFactory<IService2>("WS2007FederationHttpBinding_IService2"))
            {
                cf.Credentials.UseIdentityConfiguration = true;
                var proxy = cf.CreateChannelWithIssuedToken(token);

                var data = proxy.GetData(1);
                Console.WriteLine(data);

                ((IDisposable)proxy).Dispose();
            }
        }



Токен получается заранее на клиенте прямо с STS.
Я пока не знаю как поведёт себя эта схема если будут разные сертификаты или адреса RP... тут много вариантов. Сейчас займусь этим исследованием. Но самое главное, что есть сильная подвижка.
Благодарю Вас за помощь. Но всё равно с нетерпением буду ждать второй части статьи. У вас здорово получается, уверен, можно будет многое из неё почерпнуть!

С уважением,
Сергей
Re[3]: Вариант №1 "Чистый" WCF
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 30.01.14 11:03
Оценка:
Здравствуйте, WSA, Вы писали:

WSA>Кажется получается.


Все, верно.
Просто у этого варианта нужно понимать, что:
1. В идеале, разным сервисам нужен разный токен.
Это не обязательное требование. По большому счету, чтобы воспользоваться токеном сервис должен уметь его расшифровать (т.е. у него должен быть сертификат с закрытым ключом) и проверить подпись STS. Еще WIF позволяет проверять кому назначен токен (там указывается URL сервиса), но эту проверку можно и отключить (<audienceUris mode="Never" />)
Когда все сервисы принадлежат вам, можно просто сделать везде единые настройки и забыть об этом.

Схема начинает плыть, например, когда сервисы, скоторыми вам надо общаться, принадлежат другим компаниям.
В этом случае врятли кто-то согласится использовать единый закрытый ключ, да и в принципе — claims которые получает каждый сервис, могут существенно различаться.

2. Токен имеет ограничение по времени использования и время от времени его надо обновлять.

3. Смотря конечно для чего использовать токены...
Но вообще — это не только механизм аутентификации, но и способ для передачи разной полезной информации о пользователе: права, роли, личные данные (ФИО, должность, email, ...) которая хранится и обновляется централизованно. Поэтому логично хотябы часть этой информации получать, например, на клиенте.
Re[3]: Вариант №1 "Чистый" WCF
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 30.01.14 11:06
Оценка:
Здравствуйте, WSA, Вы писали:

WSA>Но всё равно с нетерпением буду ждать второй части статьи. У вас здорово получается, уверен, можно будет многое из неё почерпнуть!

Да не так уж и много — вы уже почти все нашли сами.
Re[2]: Вариант №2 "Ручное" получение токена
От: WSA  
Дата: 03.02.14 07:35
Оценка:
Здравствуйте Михаил.

Вы снова порадовали дельной статьёй!
Если позволите, мои комментарии..

> В чем же здесь подвох?

> Во-первых, токен — это snapshot информации аутентифицирующего центра на момент вашего обращения к нему. А ситуация с момента выдачи может уже поменяться. Например, у пользователя поменялись права доступа или его вообще заблокировали в системе, а в токене все еще старая информация (это при случае, что права/роли вы тоже передаете в токене — иногда это реально удобно).
Что касается внезапного запрета пользователю входа. Нверное или полагаться на относительно небольшой срок действия токена, или реализовывать синхронизацию(но это серьёзный подход, который не всегда оправдан, если не критично)
Синхронизацию изменения клеймов в пользу пользователя, реальнее всего решить рекомеднацией пользователю, совершить выход и повторную аутентификацию. При этом(выходе) старый токен будет удалён и затребован новый.


> Во-вторых, токен, как и любые другие credentials может быть перехвачен. Это не значит, что токен плох! Это значит, хранение и передача токена ничуть не безопаснее хранения и передачи любого другого вида credentials.

Вот именно, только токен тут отличает, что его полезность ограничена по времени в отличии от пароля (что вы заметили далее в статье).

> вероятность дискредитации сертификата, которым подписываются токены

Это да, Но со временем можно усовершенствовать эту систему, через методы борьбы с "человек по середине"... Я пока не вникал в эту тему.

> Дело в том, что настройки по умолчанию для Thinktecture IdentityServer (а точнее, для WIF), таковы, что все токены выданные по WS-Trust по умолчанию шифруются. Т.е. вместо узла с тоеном вы получите в ответе вот такой

> Какие есть варианты?
А что если расшифровывать токен на клиенте, для целей клиента, но передавать на RP токен зашифрованный, который было получен от STS??


И вообще по поводу шифрования токенов. Я планирую шифровать токены. И код работает на моей машине, но я подозреваю, работает потому, что на моей машине установлены
закрытые ключи для сертификтов и клиент шифрует токен прежде чем отправить на RP и поэтому всё работает...


>Есть еще и 3-ий вариант, так называемое делегирование — когда вы получаете токен нужного пользователя на клиенте, но не передаете его явно на каждый RP, а используете для получения токенов от STS и уже с теми токенами образаетесь к RP.

Вот этот кажется как раз именно мой. Он покрывает такие требования:
— На клиенте в памяти не зранится логин и пароль. Хранится ограниченый по времени токен.
— Все токены шифруются по умолчанию, без лишних приседаний

В данный момент мне кажутся оптимальными 2 варианта. Какой из них удастся реализовать, такой и буду использовать.
1) Получение токена клиентом заранее. Передача на RP версию токена, которая не была расшифрована, чтобы небыло необходимости шифровать клиентом, читай иметь на клиенте закрытый ключ STS....
2) Получение Credentials в виде токена, и использование этих Credentials для доступа к остальным сервисам. Очень красивый вариант.
Re[3]: Вариант №2 "Ручное" получение токена
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 03.02.14 08:11
Оценка:
Здравствуйте, WSA, Вы писали:

WSA>Что касается внезапного запрета пользователю входа. Нверное или полагаться на относительно небольшой срок действия токена, или реализовывать синхронизацию(но это серьёзный подход, который не всегда оправдан, если не критично)

Ну или какой-то промежуточный вариант — типа оповещения всех сервисов о том, что токены до даты XXX более не действительны — тогда пользователю нужно будет явно перезапросить токен снова.

WSA>Синхронизацию изменения клеймов в пользу пользователя, реальнее всего решить рекомеднацией пользователю, совершить выход и повторную аутентификацию. При этом(выходе) старый токен будет удалён и затребован новый.

Да, но чисто в теории — если пользователю будут урезать права, ему выгоднее никогда не выходить из системы (или пока не кончится время жизни токена).

WSA>А что если расшифровывать токен на клиенте, для целей клиента, но передавать на RP токен зашифрованный, который было получен от STS??

На самом деле, а надо ли шифровать токены при передаче на RP? Если там нет критически важной информации, то может ну его?
Я в продолжении второй части статьи
Автор: Михаил Романов
Дата: 02.02.14
именно такой вариант показывал — токен без сессионных ключей уже не требует обязательного шифрования и, как следствие, мы можем его читать где угодно, а сама защита осуществляется с помощью защиты канала.

>>Есть еще и 3-ий вариант, так называемое делегирование — когда вы получаете токен нужного пользователя на клиенте, но не передаете его явно на каждый RP, а используете для получения токенов от STS и уже с теми токенами образаетесь к RP.

WSA>Вот этот кажется как раз именно мой. Он покрывает такие требования:
WSA>- На клиенте в памяти не зранится логин и пароль. Хранится ограниченый по времени токен.
WSA>- Все токены шифруются по умолчанию, без лишних приседаний
Хм... Ок, постараюсь выкроить время и таки добить этот вариант.
Но все же вопрос — а реально, нужно ли шифровать токены при передаче? Не достаочно ли просто получать их по защищенным каналам (SSL)?
Ведь чтобы работать с токеном для клиента, вам нужно будет сертификаты держать где-то поблизости от клиента (ставить в хранилище или зашивать в сборку/исполнимый файл, ...) — а значит проблем с доставанием этого сертификата у того, кто имеет доступ к клиенту — не будет.
Зато вам — лишние хлопоты.

WSA>2) Получение Credentials в виде токена, и использование этих Credentials для доступа к остальным сервисам. Очень красивый вариант.

Хорошо. Я не буду ничего обещать — но постараюсь-таки выкроить время и описать этот вариант (у меня остались некоторые сложности с самим Thinktecture — он не рассчитан на вариант делегирования без авторизации или с авторизацией от имени приложения, а не пользователя.
Т.е. в его сценариях за токеном всегда приходят от имени какого-то пользователя (т.е. передают credentials этого пользователя), а уже потом, в зависимости от типа запроса или выдают токен на этого пользователя или на того, чей токен мы вложили в запрос.

Нам же нужен вариант:
1. Пришел за 1-ым токеном с credentials пользователя
2. Пришел за 2-м токеном с первым, при этом или совсем без credentials или с токеном в их качестве.
Re[4]: Вариант №2 "Ручное" получение токена
От: WSA  
Дата: 04.02.14 06:52
Оценка:
Здравствуйте, Михаил.

WSA>>Что касается внезапного запрета пользователю входа. Нверное или полагаться на относительно небольшой срок действия токена, или реализовывать синхронизацию(но это серьёзный подход, который не всегда оправдан, если не критично)


WSA>>А что если расшифровывать токен на клиенте, для целей клиента, но передавать на RP токен зашифрованный, который было получен от STS??

МР>На самом деле, а надо ли шифровать токены при передаче на RP? Если там нет критически важной информации, то может ну его?
МР>Я в продолжении второй части статьи
Автор: Михаил Романов
Дата: 02.02.14
именно такой вариант показывал — токен без сессионных ключей уже не требует обязательного шифрования и, как следствие, мы можем его читать где угодно, а сама защита осуществляется с помощью защиты канала.



Наверное в данный момент мне придётся пойти по предложеному вами пути и отказаться от шифрования токенов на уровне сообщений, а пользоваться только шифрованием на уровне транспорта.
Потому что схема перестаёт работать, если на клиенте не установлен сертификат с закрытым ключём, для шифрования токена, при передаче на RP... Этот сертификат задаётся на клиенте в разделе:

<system.identityModel.services>
    <federationConfiguration>
      <!--<serviceCertificate>
        <certificateReference x509FindType="FindByThumbprint" findValue="..." storeLocation="LocalMachine" storeName="My" />
      </serviceCertificate>-->
    </federationConfiguration>
  </system.identityModel.services>



И когда клиента запускается на машине где нет данного сертификата, схема рассыпается.... Сейчас попробую использовать токен клиента, или токен общий для всех RP.. без шифрования уровня сообщений.
Но также буду изучать тему токена в роли Credentials... ну и естественно ждать продолжения вашей статьи. Спасибо!
Re[3]: Вариант №1 "Чистый" WCF
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 20.02.14 11:32
Оценка:
Сергей, добрый день

Да, существенное дополнение. Спасибо!

WSA>В добавок System.Threading.Thread.CurrentPrincipal становится вместо WindowsPrincipal — ClaimsPrincipal'ом

Меня это (что ребята отказались от выставления System.Threading.Thread.CurrentPrincipal, как это было в WIF 3.5 и поломали тем самым совместимость) — очень смущало. А оказалось, что я просто не досмотрел настройки.

WSA>в результате появляется возможность авторизировать доступ пользователя к операции WCF-сервиса через аттрибуты операции:

Конкретно у нас это оказалось не актуальным, но по причине того, что мы не смогли использовать стандартный claim для ролей — у нас чуть более сложная система прав и только плоским спиком ролей её не опишешь, пришлось использовать свои типы Claims, свои атрибуты разметки и писать свой менедежер авторизации. Впрочем, это оказалось совсем не сложно, зато на порядок гибче (что для нас было существеннее всего).

Но в любом случа — спасибо!

P.S. Есть не нулевая вероятность, что я таки доберусь (с подачи нашего учебного центра) до полноценного цикла статей/внутреннего курса по WIF (и ряду других тем), поэтому любые новые знания и находки очень к месту.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.