Das Szenario sieht folgendermaßen aus: Ich muss eine Verbundauthentifizierung eines Benutzers (der sein Universitätskonto verwendet) auf der Sharepoint-Site seiner Universität durchführen und sowohl die FedAuth- als auch die rtFa-Cookies abrufen (was ich tun muss) an SharePoint REST Webservices übergeben, um auf Ressourcen zuzugreifen).
Ich habe einige Versuche unternommen, aber in jedem gibt es mindestens ein Problem:
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);
Auf diese Weise erhalte ich den FedAuth-Cookie, aber ich erhalte den rtFa-Cookie nicht .
Wie kann ich den rtFa-Cookie zu diesem Zeitpunkt erhalten? Kann ich die HTTP-Anforderung abfangen, die an einer solchen Operation beteiligt ist (d. H. Context.ExecuteQuery ()) - die vermutlich das rtFa-Cookie in den Headern enthält? Oder kann ich das rtFa-Cookie nur durch Nutzung des FedAuth-Cookies erhalten?
Dies ist eine Hilfsklasse, die im Internet zu finden ist (z. B. hier http://blog.kloud.com.au/tag/msonlineclaimshelper/ ).
Diese Klasse funktioniert so wie sie ist mit normaler Authentifizierung, aber schlägt mit Verbundauthentifizierung fehl .
Also habe ich es angepasst, damit es in diesem Fall funktioniert. Soweit ich weiß, sind die Schritte wie folgt:
Ich bin kein Experte in dieser Angelegenheit und habe den folgenden Code erhalten:
Dies ist der Code, der die obige Methode aufruft und in zwei Schritten versucht, FedAuth und rtFa von den Anmeldeinformationen abzurufen (Schritt 1: SAML-Token von Federated Party abrufen; Schritt 2: Token von Federated Party an Sharepoint übergeben):
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;
}
}
Ich habe es geschafft, ein SAML-Token von der Universität STS zurückzubekommen. Bei der Analyse hat das resultierende SecurityToken jedoch keine Sicherheitsschlüssel (d. H. Die SecurityKeys-Auflistung ist leer).
Ohne Schlüssel erhalte ich GetO365BinaryTokenFromToken (), aber wenn ich versuche, das Token an den SharePoint-Authentifizierungsdienst zu senden, erhalte ich die folgende Fehlermeldung: "Das Signatur-Token Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken hat keine Schlüssel. Die Sicherheit Token wird in einem Kontext verwendet, in dem kryptografische Operationen ausgeführt werden müssen, das Token jedoch keine kryptografischen Schlüssel enthält Entweder unterstützt der Token-Typ kryptografische Operationen nicht oder die jeweilige Token-Instanz enthält keine kryptografischen Schlüssel. Überprüfen Sie Ihre Konfiguration, um dies kryptografisch sicherzustellen deaktivierte Tokentypen (z. B. UserNameSecurityToken) werden nicht in einem Kontext angegeben, der kryptografische Vorgänge erfordert (z. B. ein unterstützendes Endorsing-Token). "
Ich denke, dass es auch einige Konfigurationsprobleme gibt, die ich nicht direkt auf beiden Seiten kontrollieren kann (das STS ADFS der Universität und das Sharepoint STS).
Ich hoffe, dass mehr Experten Klarheit in diesen Prozess bringen und sogar Ratschläge geben, damit dieses Szenario tatsächlich funktioniert.
Mit der folgenden Funktion kann ich eine Datei herunterladen (unter Angabe einer URL wie https://myfederatedparty.sharepoint.com/sites/MYSITE/path/myfile.pdf ), indem ich BOTH das FedAuth und das rtFa Cookie. Wenn ich den rtFa-Cookie nicht übergebe, erhalte ich die Antwort "Nicht autorisiert".
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; }
}
Tatsächlich ist für die SharePoint Online/Office 365-Authentifizierung nur das Cookie FedAuth
obligatorisch.
Laut Remote-Authentifizierung in SharePoint Online mit anspruchsbasierter Authentifizierung :
Die Cookies
FedAuth
aktivieren die Verbundautorisierung und das CookiertFA
ermöglicht das Abmelden des Benutzers von allen SharePoint-Websites, auch wenn der Abmeldevorgang von einer Nicht-SharePoint-Website aus startet.
Es reicht also aus, den HTTP-Header SPOIDCRL
anzugeben, um die Authentifizierung in SharePoint Online/Office 365 durchzuführen. Beispiel:
var request = (HttpWebRequest)WebRequest.Create(endpointUri);
var credentials = new SharePointOnlineCredentials(userName,securePassword);
var authCookie = credentials.GetAuthenticationCookie(webUri);
request.Headers.Add(HttpRequestHeader.Cookie, authCookie);
In den folgenden Beispielen wird veranschaulicht, wie eine aktive Authentifizierung in SharePointOnline/Office 365 durchgeführt wird, indem FedAuth
cookie bereitgestellt wird.
Beispiel 1: FormDigest über SharePoint 2013 REST API abrufen (MsOnlineClaimsHelper class
verwenden)
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();
}
}
}
Beispiel 2: FormDigest über SharePoint 2013 REST API abrufen (mit 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();
}
}
}
Update
Die geänderte Version des Beispiels zum Herunterladen einer Datei:
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; }
}
Ich habe ein Github-Projekt basierend auf https://stackoverflow.com/users/1375553/vadim-gremyachev s answer https://github.com/nddipiazza/SharepointOnlineCookieFetcher mit einem Projekt erstellt, das generiert werden kann diese Cookies.
Es hat Releases für Windows, Centos7 und Ubuntu16 und ich habe Mono verwendet, um es so zu entwickeln, dass es plattformunabhängig ist.
Bestimmt für Benutzer, die keine Programme mit CSOM in c # erstellen, die Cookies jedoch problemlos abrufen möchten.
Verwendung
Einmaliger Schritt: (siehe Zugriff auf den Pfad "/ etc/mono/registry" verweigert )
Sudo mkdir /etc/mono
Sudo mkdir /etc/mono/registry
Sudo chmod uog+rw /etc/mono/registry
Programm ausführen:
Linux: ./SharepointOnlineSecurityUtil -u [email protected] -w https://tenant.sharepoint.com
Windows: SharepointOnlineSecurityUtil.exe -u [email protected] -w https://tenant.sharepoint.com
Geben Sie ein Passwort ein, wenn Sie dazu aufgefordert werden
Das Ergebnis von stdout wird ein SPOIDCRL
cookie haben.