Федеративная аутентификация в Sharepoint 2013: получение файлов cookie rtFa и FedAuth

Сценарий следующий: мне нужно выполнить федеративную аутентификацию пользователя (использующего его университетскую учетную запись) на сайте Sharepoint его университета и получить файлы cookie FedAuth и rtFa (которые у меня есть). для перехода к веб-службам SharePoint REST для доступа к ресурсам).

Я сделал несколько попыток, но в каждой из них есть как минимум проблема:

1) Использование библиотеки Microsoft.SharePoint.Client

ClientContext context = new ClientContext(host);
SharePointOnlineCredentials creds = new SharePointOnlineCredentials(user, passw);
context.Credentials = creds;

Uri sharepointuri = new Uri(host);
string authCookie = creds.GetAuthenticationCookie(sharepointuri);

Web web = context.Web;
context.Load(web, w=>w.Lists);
context.ExecuteQuery();

fedAuthString = authCookie.Replace("SPOIDCRL=", string.Empty);

Таким образом мне удается получить файл cookie FedAuth, но я не могу получить файл cookie rtFa.

Как я могу получить файл cookie rtFa в этот момент? Могу ли я перехватить HTTP-запрос, связанный с такой операцией (например, context.ExecuteQuery()), который предположительно содержит файл cookie rtFa в заголовках? Или я могу получить файл cookie rtFa, используя только файл cookie FedAuth?

2) Использование MsOnlineClaimsHelper

Это вспомогательный класс, который можно найти в Интернете (например, здесь http://blog.kloud.com.au/tag/msonlineclaimshelper/ ).

Этот класс работает с обычной аутентификацией, но не работает с федеративной аутентификацией.

Поэтому я отрегулировал его, чтобы заставить его работать в этом случае. Насколько я понимаю, шаги следующие:

  1. Выполните аутентификацию, используя имя пользователя и пароль, в службе STS ADFS университета («федеративная сторона» или ISSUER) — здесь проверяющей стороной является Sharepoint O365 STS («https://login.microsoftonline.com/extSTS.srf")
  2. Если аутентификация прошла успешно, я получаю подтверждение SAML, содержащее утверждения и токен безопасности.
  3. Теперь я аутентифицируюсь на сайте SharePoint, передавая токен безопасности
  4. Если токен распознан, я получаю ответ, содержащий два файла cookie (FedAuth и rtFa).

Я не эксперт в этом вопросе, и у меня вышел следующий код:

Это код, который вызывает описанный выше метод и пытается получить FedAuth и rtFa из учетных данных в два этапа (шаг 1: получить токен SAML от федеративной стороны; шаг 2: передать токен от федеративной стороны в Sharepoint):

     private List<string> GetCookies(){
            // 1: GET SAML XML FROM FEDERATED PARTY THE USER BELONGS TO
            string samlToken = getResponse_Federation(sts: "https://sts.FEDERATEDDOMAIN.com/adfs/services/trust/13/usernamemixed/",
                realm: "https://login.microsoftonline.com/extSTS.srf");

            // 2: PARSE THE SAML ASSERTION INTO A TOKEN 
            var handlers = FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers;
            SecurityToken token = handlers.ReadToken(new XmlTextReader(new StringReader(samlToken )));

            // 3: REQUEST A NEW TOKEN BASED ON THE ISSUED TOKEN
            GenericXmlSecurityToken secToken = GetO365BinaryTokenFromToken(token);

            // 4: NOW, EASY: I PARSE THE TOKEN AND EXTRACT FEDAUTH and RTFA
            ...............
    }


    private string getResponse_Federation(string stsUrl, string relyingPartyAddress)
    {
        var binding = new Microsoft.IdentityModel.Protocols.WSTrust.Bindings.UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential);
        binding.ClientCredentialType = HttpClientCredentialType.None;

        var factory = new WSTrustChannelFactory(binding,  stsUrl);

        factory.Credentials.UserName.UserName = "username";
        factory.Credentials.UserName.Password = "password";
        factory.Credentials.SupportInteractive = false;
        factory.TrustVersion = TrustVersion.WSTrust13;

        IWSTrustChannelContract channel = null;
        try
        {
            var rst = new RequestSecurityToken
            {
                RequestType = WSTrust13Constants.RequestTypes.Issue,
                AppliesTo = new EndpointAddress(relyingPartyAddress), //("urn:sharepoint:MYFEDERATEDPARTY"),
                ReplyTo = relyingPartyAddress,
                KeyType = WSTrust13Constants.KeyTypes.Bearer,
                TokenType =  "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0",
                RequestDisplayToken = true,
            };
            channel = (WSTrustChannel)factory.CreateChannel();

            RequestSecurityTokenResponse response = null;
            SecurityToken st = channel.Issue(rst, out response);
            var genericToken = st as GenericXmlSecurityToken;
            return genericToken.TokenXml.OuterXml;
        }
        catch (Exception e)
        {
            return null;
        }
    }

    private GenericXmlSecurityToken GetO365BinaryTokenFromToken(SecurityToken issuedToken)
    {
        Uri u = new Uri("https://login.microsoftonline.com/extSTS.srf");

        WSHttpBinding binding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential);
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
        binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
        binding.Security.Message.ClientCredentialType = MessageCredentialType.IssuedToken;

        Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory channel =
        new Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory(
            binding, new EndpointAddress("https://login.microsoftonline.com/extSTS.srf"));

        channel.TrustVersion = TrustVersion.WSTrust13;
        channel.Credentials.SupportInteractive = false;

        GenericXmlSecurityToken token = null;

        try
        {
            RequestSecurityToken rst = new RequestSecurityToken(WSTrust13Constants.RequestTypes.Issue, WSTrust13Constants.KeyTypes.Bearer)
            {
            };
            rst.AppliesTo = new EndpointAddress("urn:sharepoint:MYFEDERATEDPARTY");
            channel.ConfigureChannelFactory();
            var chan = (Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel)channel.CreateChannelWithIssuedToken(issuedToken);

            RequestSecurityTokenResponse rstr = null;

            token = chan.Issue(rst, out rstr) as GenericXmlSecurityToken;

            return token;
        }
        catch (Exception ex){
            Trace.TraceWarning("WebException in getO365BinaryTokenFromADFS: " + ex.ToString());
            throw;
        }
    }

Мне удалось вернуть токен SAML от STS университета. Однако при синтаксическом анализе полученный SecurityToken не имеет ключей безопасности (т. е. коллекция SecurityKeys пуста).

Без ключей я получаю GetO365BinaryTokenFromToken(), но когда я пытаюсь отправить токен в службу проверки подлинности SharePoint, я получаю следующую ошибку: «Токен подписи Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken не имеет ключей. токен используется в контексте, требующем выполнения криптографических операций, но токен не содержит криптографических ключей. Либо тип токена не поддерживает криптографические операции, либо конкретный экземпляр токена не содержит криптографических ключей. Проверьте свою конфигурацию, чтобы убедиться, что криптографические отключенные типы токенов (например, UserNameSecurityToken) не указываются в контексте, требующем криптографических операций (например, одобряющий поддерживающий токен)».

Я думаю, что есть также некоторые проблемы с конфигурацией, которые я не могу контролировать напрямую с обеих сторон (университетская STS ADFS и Sharepoint STS).

Я надеюсь, что более опытные люди внесут ясность в этот процесс и даже дадут совет, как заставить этот сценарий работать.

Функция загрузки файлов

С помощью следующей функции я могу загрузить файл (с учетом URL-адреса, такого как https://myfederatedparty.sharepoint.com/sites/MYSITE/path/myfile.pdf), создав ОБИ файлы cookie FedAuth и rtFa. Если я не передам файл cookie rtFa, я получу ответ «Неавторизованный».

    public static async Task<byte[]> TryRawWsCall(String url, string fedauth, string rtfa, CancellationToken ct, TimeSpan? timeout = null) {
        try {
            HttpClientHandler handler = new HttpClientHandler();
            handler.CookieContainer = new System.Net.CookieContainer();
            CookieCollection cc = new CookieCollection();
            cc.Add(new Cookie("FedAuth", fedauth));
            cc.Add(new Cookie("rtFa", rtfa));
            handler.CookieContainer.Add(new Uri(url), cc);

            HttpClient _client = new HttpClient(handler);
            if (timeout.HasValue)
                _client.Timeout = timeout.Value;
            ct.ThrowIfCancellationRequested();

            var resp = await _client.GetAsync(url);
            var result = await resp.Content.ReadAsByteArrayAsync();
            if (!resp.IsSuccessStatusCode)
                return null;
            return result;
        }
        catch (Exception) { return null; }
    }

person metaphori    schedule 19.08.2014    source источник


Ответы (3)


Фактически, только FedAuth файл cookie является обязательным, когда речь идет об аутентификации SharePoint Online/Office 365.

Согласно Удаленная проверка подлинности в SharePoint Online с использованием проверки подлинности на основе утверждений:

Файлы cookie FedAuth обеспечивают федеративную авторизацию, а файлы cookie rtFA позволяют выполнять выход пользователя со всех сайтов SharePoint, даже если процесс выхода начинается с сайта, отличного от SharePoint.

Итак, достаточно указать HTTP-заголовок SPOIDCRL, чтобы выполнить аутентификацию в SharePoint Online/Office 365, например:

var request = (HttpWebRequest)WebRequest.Create(endpointUri);
var credentials = new SharePointOnlineCredentials(userName,securePassword);
var authCookie = credentials.GetAuthenticationCookie(webUri);
request.Headers.Add(HttpRequestHeader.Cookie, authCookie);

В следующих примерах показано, как выполнить активную проверку подлинности в SharePointOnline/Office 365, предоставив файл cookie FedAuth.

Пример 1. Получение FormDigest через REST API SharePoint 2013 (uisng MsOnlineClaimsHelper class)

public static string GetFormDigest(Uri webUri, string userName, string password)
{
   var claimsHelper = new MsOnlineClaimsHelper(webUri, userName, password);
   var endpointUri = new Uri(webUri,"/_api/contextinfo");
   var request = (HttpWebRequest)WebRequest.Create(endpointUri);
   request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
   request.Method = WebRequestMethods.Http.Post;
   request.Accept = "application/json;odata=verbose";
   request.ContentType = "application/json;odata=verbose";
   request.ContentLength = 0;

   var fedAuthCookie = claimsHelper.CookieContainer.GetCookieHeader(webUri); //FedAuth are getting here
   request.Headers.Add(HttpRequestHeader.Cookie, fedAuthCookie); //only FedAuth cookie are provided here
   //request.CookieContainer = claimsHelper.CookieContainer;
   using (var response = (HttpWebResponse) request.GetResponse())
   {
        using (var streamReader = new StreamReader(response.GetResponseStream()))
        {
                var content = streamReader.ReadToEnd();
                var t = JToken.Parse(content);
                return t["d"]["GetContextWebInformation"]["FormDigestValue"].ToString();
        }     
    }
}

Пример 2. Получение FormDigest через REST API SharePoint 2013 (используя SharePointOnlineCredentials class)

public static string GetFormDigest(Uri webUri, string userName, string password)
{
   var endpointUri = new Uri(webUri, "/_api/contextinfo");
   var request = (HttpWebRequest)WebRequest.Create(endpointUri);
   request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
   request.Method = WebRequestMethods.Http.Post;
   request.Accept = "application/json;odata=verbose";
   request.ContentType = "application/json;odata=verbose";
   request.ContentLength = 0;

   var securePassword = new SecureString();
   foreach (char c in password)
   {
       securePassword.AppendChar(c);
   }
   request.Credentials = new SharePointOnlineCredentials(userName,securePassword);

   using (var response = (HttpWebResponse)request.GetResponse())
   {
       using (var streamReader = new StreamReader(response.GetResponseStream()))
       {
           var content = streamReader.ReadToEnd();
           var t = JToken.Parse(content);
           return t["d"]["GetContextWebInformation"]["FormDigestValue"].ToString();
        }
   }
}

Обновить

Модифицированная версия примера для скачивания файла:

public static async Task<byte[]> DownloadFile(Uri webUri,string userName,string password, string relativeFileUrl, CancellationToken ct, TimeSpan? timeout = null)
{
        try
        {

            var securePassword = new SecureString();
            foreach (var c in password)
            {
                securePassword.AppendChar(c);
            }
            var credentials = new SharePointOnlineCredentials(userName, securePassword);
            var authCookie = credentials.GetAuthenticationCookie(webUri);
            var fedAuthString = authCookie.TrimStart("SPOIDCRL=".ToCharArray());
            var cookieContainer = new CookieContainer();
            cookieContainer.Add(webUri, new Cookie("SPOIDCRL", fedAuthString));


            HttpClientHandler handler = new HttpClientHandler();
            handler.CookieContainer = cookieContainer;

            HttpClient _client = new HttpClient(handler);
            if (timeout.HasValue)
                _client.Timeout = timeout.Value;
            ct.ThrowIfCancellationRequested();

            var fileUrl = new Uri(webUri, relativeFileUrl);
            var resp = await _client.GetAsync(fileUrl);
            var result = await resp.Content.ReadAsByteArrayAsync();
            if (!resp.IsSuccessStatusCode)
                return null;
            return result;
        }
        catch (Exception) { return null; }
 }
person Vadim Gremyachev    schedule 28.08.2014
comment
Моя проблема заключается в том, что когда я пытаюсь загрузить файл из точки доступа (выполняя HTTP-запрос через HttpClient по URL-адресу, например ссылка), ответ считается успешным, ТОЛЬКО ЕСЛИ Я ПРЕДОСТАВЛЮ И FedAuth, и файл cookie rtFa. FedAuth недостаточно. Таким образом, я должен получить файл cookie rtFa ИЛИ заставить этот запрос работать без него. Ps: я не знаю, что такое FormDigest. - person metaphori; 29.08.2014
comment
На самом деле, поскольку мне удалось СКАЧАТЬ файл, используя как FedAuth, так и rtFA, я обратил внимание на то, что я считал источником проблемы: невозможность получить файл cookie rtFa. Я обновлю свой вопрос. - person metaphori; 29.08.2014
comment
Ответ обновлен, см. измененную версию вашего примера. я проверял и у меня работает нормально - person Vadim Gremyachev; 29.08.2014
comment
Спасибо, это работает! Таким образом, кажется, что есть разница между куки-файлами SPOYDCRL и FedAuth. Какой метод проще? На самом деле я использую этот подход, потому что не могу использовать клиентские библиотеки SharePoint (а также другие) в своем приложении Магазина Windows. - person metaphori; 29.08.2014
comment
Здорово! Другой вариант, показанный здесь, stackoverflow.com/a/23687279/1375553, но в этом примере также используются клиентские библиотеки SharePoint. - person Vadim Gremyachev; 29.08.2014
comment
Все эти примеры требуют, чтобы клиентские .dll были доступны локально на машине, верно? Любые решения, которые могут избежать этих зависимостей? - person Taylor Lopez; 26.10.2015
comment
@iAmMortos, действительно, в приведенных примерах есть зависимость от библиотек CSOM, поскольку класс SharePointOnlineCredentials используется для аутентификации. Но вы также можете рассмотреть подход с токеном доступа, чтобы избежать этих зависимостей - person Vadim Gremyachev; 27.10.2015
comment
ребята, у меня есть вопрос, файл cookie fedauth всегда имеет значение null на моей машине, хотя такое же решение на другой машине успешно извлекает файл cookie fedauth, любое предложение - person Eslam Soliman; 07.02.2016
comment
@VadimGremyachev Немного опоздал на вечеринку, но я просто хотел указать, что подход с токеном доступа здесь тоже не сработает, поскольку он также ограничен вызовами API. Фактические запросы к службе STS принимают только 3 параметра пользовательского ввода в фиксированных местах (5, если вы укажете дату и время истечения срока действия для своего сеанса), поэтому его сериализация, как это, немного излишняя. Я бы посоветовал всем, кто изучает это, взглянуть на allthatjs.com /2012/03/29/node-js-meet-sharepoint, чтобы лучше понять процесс. - person Charles Grunwald; 05.07.2016
comment
@VadimGremyachev это не работает при использовании единого входа ADFS — необработанное исключение: System.NullReferenceException: файл cookie пуст. Это ожидается? Как вы используете это в такой ситуации? - person Nicholas DiPiazza; 23.02.2018
comment
@NicholasDiPiazza, не уверен в этом сценарии, я бы предложил опубликовать отдельный вопрос - person Vadim Gremyachev; 24.02.2018

Я создал проект github на основе https://stackoverflow.com/users/1375553/vadim-gremyachev ответьте https://github.com/nddipiazza/SharepointOnlineCookieFetcher с проектом, который может создавать эти файлы cookie.

У него есть выпуски для Windows, Centos7 и Ubuntu16, и я использовал моно, чтобы построить его так, чтобы он был независимым от платформы.

Предназначен для пользователей, которые не создают программы с CSOM на C#, но все же хотят иметь возможность легко получать файлы cookie.

Использование

Одноразовый шаг: (см. Доступ к пути /etc/ моно/реестр запрещен)

sudo mkdir /etc/mono
sudo mkdir /etc/mono/registry
sudo chmod uog+rw /etc/mono/registry

Запустить программу:

Линукс: ./SharepointOnlineSecurityUtil -u [email protected] -w https://tenant.sharepoint.com

Окна: SharepointOnlineSecurityUtil.exe -u [email protected] -w https://tenant.sharepoint.com

Введите пароль при появлении запроса

Результат stdout будет иметь SPOIDCRL cookie.

person Nicholas DiPiazza    schedule 23.02.2018

Мне по-прежнему нужны файлы cookie FedAuth и rtFa для 2010-workflow-xoml-file-from-sharepoint-online-using-powershell?forum=sharepointdevelopmentprevious" rel="nofollow noreferrer">мои цели. Я пытался использовать только FedAuth, но без обоих это не сработало. Другой разработчик подтвердил, что видел такое же поведение.

ПРИМЕЧАНИЕ. Устаревшая аутентификация должна быть включен в вашем клиенте, чтобы это работало.

Вот поток, чтобы помочь получить как FedAuth, так и rtFa.

  1. Отправьте запрос на публикацию https://login.microsoftonline.com/extSTS.srf со следующим текстом.
    Замените имя пользователя, пароль, адрес конечной точки соответствующими значениями.
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
      xmlns:a="http://www.w3.org/2005/08/addressing"
      xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:To s:mustUnderstand="1">https://login.microsoftonline.com/extSTS.srf</a:To>
    <o:Security s:mustUnderstand="1"
       xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <o:UsernameToken>
        <o:Username>[username]</o:Username>
        <o:Password>[password]</o:Password>
      </o:UsernameToken>
    </o:Security>
  </s:Header>
  <s:Body>
    <t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
      <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
        <a:EndpointReference>
          <a:Address>[endpoint]</a:Address>
        </a:EndpointReference>
      </wsp:AppliesTo>
      <t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
      <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
      <t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
    </t:RequestSecurityToken>
  </s:Body>
</s:Envelope>
  1. Обратите внимание на содержимое узла wsse:BinarySecurityToken в данных ответа.
  2. Отправить запрос публикации на https://YourDomain.sharepoint.com/_forms/default.aspx?wa=wsignin1.0.
    Замените 'YourDomain соответствующим значением. Укажите wsse:BinarySecurityToken содержимое в теле запроса.

Заголовок ответа будет содержать файлы cookie FedAuth и rtFa.

person Tracy    schedule 15.09.2020