Sharepoint 2013 中的联合身份验证:获取 rtFa 和 FedAuth cookie [英] Federated authentication in Sharepoint 2013: getting rtFa and FedAuth cookies

查看:22
本文介绍了Sharepoint 2013 中的联合身份验证:获取 rtFa 和 FedAuth cookie的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

场景如下:我需要对一个用户(使用他的大学帐户)执行联合身份验证到他大学的 Sharepoint 站点并获得 FedAuth 和 rtFa cookie(我必须将其传递给 SharePoint REST Web 服务才能访问资源).

The scenario is the following: I need to perform a federated authentication of a user (which uses his university account) into the Sharepoint site of his university and to obtain both the FedAuth and rtFa cookies (which I have to pass to SharePoint REST webservices in order to access resources).

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

I made some attempts but there is at least an issue in each one:

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.

This way I manage to get the FedAuth cookie but I am unable to get the rtFa cookie.

此时如何获取 rtFa cookie?我可以拦截此类操作中涉及的 HTTP 请求(即 context.ExecuteQuery())——它大概在标头中包含 rtFa cookie?或者,我能否仅利用 FedAuth cookie 来获取 rtFa cookie?

How can I get the rtFa cookie at this point? Can I intercept the HTTP request involved in such an operation (i.e., context.ExecuteQuery()) -- which presumably contains the rtFa cookie in the headers? Or, can I get the rtFa cookie by only leveraging on the FedAuth cookie?

这是一个可以在 Internet 上找到的帮助器类(例如,这里 http://blog.kloud.com.au/tag/msonlineclaimshelper/).

This is a helper class which can be found on the Internet (e.g., here http://blog.kloud.com.au/tag/msonlineclaimshelper/ ).

这个类,就其本身而言,适用于普通身份验证,但使用联合身份验证失败.

This class, as it is, works with normal authentication but fails with federated authentication.

所以我调整了它以使其在这种情况下工作.据我了解,步骤如下:

So I adjusted it in order to make it work in this case. As long as I understand, the steps are the following:

  1. 使用用户名和密码对大学的 STS ADFS 服务(联邦方"或 ISSUER)进行身份验证——这里的依赖方是 Sharepoint O365 STS(https://login.microsoftonline.com/extSTS.srf")
  2. 如果身份验证成功,我会返回一个包含声明和安全令牌的 SAML 断言
  3. 现在,我通过传递安全令牌对 SharePoint 网站进行身份验证
  4. 如果令牌被识别,我会返回一个包含两个 cookie(FedAuth 和 rtFa)的响应

我不是这方面的专家,我得出了以下代码:

I am not an expert in this matter, and I came out with the following code:

这是调用上述方法并尝试分两步从凭证中获取 FedAuth 和 rtFa 的代码(步骤 1:从联合方获取 SAML 令牌;步骤 2:将令牌从联合方传递到 Sharepoint):

This is the code that calls the method above and try to get FedAuth and rtFa from credentials in two steps (step 1: get SAML token from Federated Party; step 2: pass token from Federated Party to 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 集合为空)

I managed to get back a SAML token from the university STS. However, when parsed, the resulting SecurityToken has no security keys (i.e., the SecurityKeys collection is empty)

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

With no keys, I get on GetO365BinaryTokenFromToken() but when I try to send the token to the SharePoint Authentication service -- I get the following error: "The signing token Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken has no keys. The security token is used in a context that requires it to perform cryptographic operations, but the token contains no cryptographic keys. Either the token type does not support cryptographic operations, or the particular token instance does not contain cryptographic keys. Check your configuration to ensure that cryptographically disabled token types (for example, UserNameSecurityToken) are not specified in a context that requires cryptographic operations (for example, an endorsing supporting token)."

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

I think that there are also some configuration issues that I cannot control directly, on both sides (the university STS ADFS and the Sharepoint STS).

我希望更多的专家能够在这个过程中提供清晰的信息,甚至提供建议以实际实现这个场景.

I hope that more expert people would bring clarity in this process and even provide advice to actually make this scenario work.

使用以下功能,我可以下载文件(给定一个 URL,例如 https://myfederatedparty.sharepoint.com/sites/MYSITE/path/myfile.pdf) 通过发布BOTH FedAuth 和 rtFa cookie.如果我不传递 rtFa cookie,我会收到未经授权"的响应.

With the following function, I am able to download a file (given an URL such as https://myfederatedparty.sharepoint.com/sites/MYSITE/path/myfile.pdf) by issuing BOTH the FedAuth and the rtFa cookie. If I do not pass the rtFa cookie, I get an "Unauthorized" response.

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

推荐答案

事实上,当涉及到 SharePoint Online/Office 365 身份验证时,只有 FedAuth cookie 是必需的.

In fact, only FedAuth cookie is mandatory when it comes to SharePoint Online/Office 365 authentication.

根据 SharePoint Online 中使用基于声明的身份验证的远程身份验证:

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

The FedAuth cookies enable federated authorization, and the rtFA cookie enables signing out the user from all SharePoint sites, even if the sign-out process starts from a non-SharePoint site.

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

So, it is enough to provide SPOIDCRL HTTP header in order to perform authentication in SharePoint Online/Office 365, for example:

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 中执行主动身份验证.


The following examples demonstrates how to perform active authentication in SharePointOnline/Office 365 by providing FedAuth cookie.

示例 1:通过 SharePoint 2013 REST API 检索 FormDigest(uisng MsOnlineClaimsHelper 类)

Example 1: Retrieve FormDigest via SharePoint 2013 REST API (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 类)

Example 2: Retrieve FormDigest via SharePoint 2013 REST API (using 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();
        }
   }
}

<小时>

更新

下载文件示例的修改版本:

The modified version of the example for downloading a file:

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

这篇关于Sharepoint 2013 中的联合身份验证:获取 rtFa 和 FedAuth cookie的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆