问题描述
情况如下:我需要对用户(使用他的大学帐户)进行联合身份验证,进入其大学的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:
- 使用用户名和密码对大学的STS ADFS服务(联合方"或ISSUER)进行身份验证-这里的依赖方是Sharepoint O365 STS("")
- 如果身份验证成功,我将返回一个SAML断言,其中包含声明和安全令牌
- 现在,我通过传递安全令牌来对SharePoint网站进行身份验证
- 如果识别出令牌,我将返回包含两个cookie(FedAuth和rtFa)的响应
- Authenticate using username and password to the STS ADFS service of the university (the "federated party" or the ISSUER) -- here the Relying Party is Sharepoint O365 STS ("https://login.microsoftonline.com/extSTS.srf")
- If the auth succeeds, I get back a SAML assertion containing the claims and a security token
- Now, I authenticate to the SharePoint site by passing the Security Token
- If the token is recognized, I get back a response which contains the two cookies (FedAuth and 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中进行远程身份验证:
因此,只需提供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(uisng MsOnlineClaimsHelper class
)检索FormDigest
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(使用SharePointOnlineCredentials class
)检索FormDigest
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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!