Сценарий следующий: мне нужно выполнить федеративную аутентификацию пользователя (использующего его университетскую учетную запись) на сайте 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/ ).
Этот класс работает с обычной аутентификацией, но не работает с федеративной аутентификацией.
Поэтому я отрегулировал его, чтобы заставить его работать в этом случае. Насколько я понимаю, шаги следующие:
- Выполните аутентификацию, используя имя пользователя и пароль, в службе STS ADFS университета («федеративная сторона» или ISSUER) — здесь проверяющей стороной является Sharepoint O365 STS («https://login.microsoftonline.com/extSTS.srf")
- Если аутентификация прошла успешно, я получаю подтверждение SAML, содержащее утверждения и токен безопасности.
- Теперь я аутентифицируюсь на сайте SharePoint, передавая токен безопасности
- Если токен распознан, я получаю ответ, содержащий два файла 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; }
}