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

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

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

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

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, но Я не могу получить rtFa печенье.

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

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

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

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

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

  1. аутентификация с использованием имени пользователя и пароля к службе STS ADFS университет ("Федеративная сторона" или эмитент) -- здесь проверяющей стороной является Sharepoint O365 STS ("https://login.microsoftonline.com/extSTS.srf")
  2. если аутентификация завершится успешно, я получу утверждение SAML, содержащее утверждения и маркер безопасности
  3. теперь, я аутентифицироваться на сайте SharePoint, передавая маркер безопасности
  4. если маркер распознан, я получаю ответ, который содержит два куки (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.Жетоны.Saml2.У Saml2SecurityToken нет ключей. Маркер безопасности используется в контексте, требующем выполнения криптографических операций, но маркер не содержит криптографических ключей. Либо Тип маркера не поддерживает криптографический операции, или конкретный экземпляр маркера не содержит криптографических ключей. Проверьте конфигурацию, чтобы убедиться, что криптографически отключенные типы маркеров (например, UserNameSecurityToken) не указаны в контексте, требующем криптографических операций (например, поддерживающий маркер)."

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

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

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

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

    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; }
    }

2 ответов


на самом деле, только FedAuth cookie является обязательным, когда дело доходит до проверки подлинности SharePoint Online/Office 365.

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

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

итак, достаточно предоставить SPOIDCRL заголовок HTTP для выполнения проверки подлинности в 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, предоставив FedAuth cookie.

Пример 1. Получение FormDigest через API REST 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 через API REST 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; }
 }

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

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

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

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

один раз только шаг: (см. доступ к пути "/ etc/mono / registry" запрещен)

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

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

Linux:./SharepointOnlineSecurityUtil -u youruser@yourdomain.com -w https://tenant.sharepoint.com

Windows:SharepointOnlineSecurityUtil.exe -u youruser@yourdomain.com -w https://tenant.sharepoint.com

введите пароль, когда promped

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