web-dev-qa-db-de.com

Verbundauthentifizierung in Sharepoint 2013: Abrufen von RTFA- und FedAuth-Cookies

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:

1) Verwenden der Microsoft.SharePoint.Client-Bibliothek

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?

2) Verwenden von MsOnlineClaimsHelper

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:

  1. Authentifizieren Sie sich mit Benutzername und Kennwort beim STS ADFS-Dienst der Universität (der "Verbundpartei" oder dem Emittenten) - hier ist die vertrauende Partei Sharepoint O365 STS (" https://login.microsoftonline.com/extSTS.srf ")
  2. Wenn die Authentifizierung erfolgreich ist, erhalte ich eine SAML-Zusicherung mit den Ansprüchen und einem Sicherheitstoken zurück
  3. Jetzt authentifiziere ich mich bei der SharePoint-Website, indem ich das Sicherheitstoken weitergebe
  4. Wenn das Token erkannt wird, erhalte ich eine Antwort, die die beiden Cookies enthält (FedAuth und rtFa).

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.

Dateidownload-Funktion

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

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 Cookie rtFA 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; }
 }
11

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.

0