Sharepoint 2013中的联合身份验证:获取rtFa和FedAuth cookie

时间:2014-08-19 16:39:32

标签: c# authentication sharepoint claims-based-identity ws-federation

方案如下:我需要对用户(使用他的大学帐户)执行联盟身份验证到他大学的Sharepoint站点,并获取FedAuth和rtFa cookie (我必须传递给SharePoint REST Web服务才能访问资源。)

我做了一些尝试,但每个问题至少有一个问题:

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

这样我设法获得FedAuth cookie,但我无法获得rtFa cookie

我怎样才能获得rtFa cookie? 我可以拦截这种操作中涉及的HTTP请求(即context.ExecuteQuery()) - 它可能包含标题中的rtFa cookie? 或者,我是否可以通过仅利用FedAuth cookie获得rtFa cookie?

2)使用MsOnlineClaimsHelper

这是一个帮助类,可以在互联网上找到(例如,http://blog.kloud.com.au/tag/msonlineclaimshelper/)。

此类实际上适用于普通身份验证,但使用联合身份验证失败

所以我调整了它以使其在这种情况下起作用。 只要我理解,步骤如下:

  1. 使用用户名和密码对大学的STS ADFS服务(“联合方”或ISSUER)进行身份验证 - 此处依赖方为Sharepoint O365 STS(“https://login.microsoftonline.com/extSTS.srf”)
  2. 如果auth成功,我会收到包含声明和安全令牌的SAML断言
  3. 现在,我通过传递安全令牌
  4. 对SharePoint网站进行身份验证
  5. 如果识别出令牌,我会收到包含两个cookie的回复(FedAuth和rtFa)
  6. 我不是这方面的专家,我提出了以下代码:

    这是调用上述方法的代码,尝试通过两个步骤从凭证中获取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;
            }
        }
    

    我设法从大学STS取回一个SAML令牌。但是,在解析时,生成的SecurityToken没有安全密钥(即,SecurityKeys集合为空)

    没有密钥,我使用GetO365BinaryTokenFromToken()但是当我尝试将令牌发送到SharePoint身份验证服务时 - 我收到以下错误: “签名令牌Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken没有密钥。安全令牌用于需要它执行加密操作的上下文中,但令牌不包含加密密钥。令牌类型不支持加密操作或者特定令牌实例不包含加密密钥。检查您的配置以确保在需要加密操作的上下文中未指定加密禁用的令牌类型(例如,UserNameSecurityToken)(例如,支持支持令牌)。 / p>

    我认为还存在一些我无法直接控制的配置问题,双方(大学STS ADFS和Sharepoint STS)。

    我希望更多的专家能够在这个过程中提供清晰度,甚至提供建议,以实际使这个场景有效。

    文件下载功能

    使用以下功能,我可以通过发出 BOTH FedAuth和rtFa cookie来下载文件(给定https://myfederatedparty.sharepoint.com/sites/MYSITE/path/myfile.pdf等URL)。如果我没有通过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; }
        }
    

3 个答案:

答案 0 :(得分:11)

事实上,在SharePoint Online / Office 365身份验证方面,只有FedAuth cookie是必需的。

根据Remote Authentication in SharePoint Online Using Claims-Based Authentication

  

FedAuth Cookie启用联合授权,rtFA   cookie允许从所有SharePoint站点注销用户,即使   注销过程从非SharePoint站点开始。

因此,为了在SharePoint Online / Office 365中执行身份验证,提供SPOIDCRL HTTP标头就足够了,例如:

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

以下示例演示如何通过提供FedAuth cookie在SharePointOnline / Office 365中执行活动身份验证。

示例1:通过SharePoint 2013 REST API检索FormDigest(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:通过SharePoint 2013 REST API检索FormDigest(使用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; }
 }

答案 1 :(得分:0)

我根据https://stackoverflow.com/users/1375553/vadim-gremyachev的回答创建了一个github项目https://github.com/nddipiazza/SharepointOnlineCookieFetcher 一个可以生成这些cookie的项目。

它有适用于Windows,Centos7和Ubuntu16的版本,我使用mono develop来构建它,以便它独立于平台。

适用于未在c#中使用CSOM制作程序但仍希望能够轻松获取cookie的用户。

<强>用法

一次性步骤:(见Access to the path "/etc/mono/registry" is denied

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

提示时输入密码

stdout的结果将有SPOIDCRL cookie。

答案 2 :(得分:0)

my purposes我仍然需要FedAuth和rtFa cookie。我尝试仅使用FedAuth,但没有两者都无法使用。 Another developer确认他也看到了相同的行为。

注意:传统身份验证必须为enabled in your tenant才能起作用。

这里有a thread可以帮助获取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内容。

响应头将包含FedAuth和rtFa cookie。