使用SSO将Office 365登录名绕过SharePoint Online ADFS

问题描述:

我正在尝试连接到SharePoint网站,该网站首先重定向到Microsoft登录页面/office 365登录页面,一旦输入我的公司电子邮件ID,然后将其重定向到我的公司ADFS"登录页面,其中该电子邮件ID已存在文本框和密码为必填项.我在这里想要避免的是登录页面(即先是microsoft/0ffice365登录页面,然后是我公司的sso登录页面),然后直接打开sharepoint在线网站,而无需任何重定向/提示输入登录凭证.我正在尝试使用asp.net实现此目的.

I am trying to connect to a sharepoint website, which first redirect to Microsoft login page/office 365 login page, And once i enter my company email id then its redirect to My company ADFS login page, where email id already exist in the text box and password is required. what i am trying here is to avoid the Login page i.e.(first microsoft/0ffice365 login page and then to my company sso login page) and directly open the sharepoint online website without any redirect/prompt for login crediantials. i am trying to achieve this using asp.net.

ADFS SSO:- https://sso.mycompany.com/adfs/services/trust/13/usernamemixed

RP信任URL:- https://login.microsoftonline.com /extSTS.srf

SharePoint在线URL:- https://mycompanyo365.sharepoint.com/sites/procC/SitePages/Index.aspx

在这里,我可以使用.net连接到我的公司ADFS,并从ADFS获取SAML令牌.但是当我将令牌传递到sharepoint网站时,我的响应状态为OK,但没有获得FedAuth cookie.

Here i am able to connect to my company ADFS using .net and get the SAML token from ADFS. but when i am passing the token to sharepoint site i am getting the response status as OK, but i am not getting the FedAuth cookies.

步骤:-

  • 开发了一个.net页面,其中包含用户凭据,即(用户 名称和密码).
  • 通过编程,我们将用户凭据传递给了ADFS.
  • 然后,一旦用户通过身份验证,我们就会获得SMAL令牌作为响应.
  • 然后,我们将SAML令牌发布到SharePoint网站,但没有得到Fedauth cookie的响应(即使状态码为200(确定)).
  • Developed a .net page which contains the user credentials i.e. (User name and Password).
  • Programmatically we passed the user credentials to ADFS.
  • Then we got the SMAL token in response once user is authenticated.
  • Then we Posted the SAML token to SharePoint site but didnt recive the Fedauth cookie in response (even the status code is 200 (OK)).

页面加载具有用户凭证和重新引导到共享点站点的页面.

protected void Page_Load(object sender, EventArgs e)
        {
            UserName = UserNmae;
            password = Password;
            ConnectAdfsToGetSamlToken();
            Response.Redirect("https://Mycompanyo365.sharepoint.com/sites/ProcC/SitePages/Index.aspx", false);
        }

连接ADFS并获取SAML令牌

 #region Connect Adfs To Get SamlToken to pass in fedauth cookies
        public void ConnectAdfsToGetSamlToken()
        {
                // STS endpoint...
                WSTrustChannelFactory factory = new WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), new EndpointAddress("https://sso.mycompany.com/adfs/services/trust/13/usernamemixed"));
                factory.TrustVersion = TrustVersion.WSTrust13;

                // Username and Password here...
                factory.Credentials.UserName.UserName = UserName;
                factory.Credentials.UserName.Password = password;

                //Request creation with relaying party...
                RequestSecurityToken rst = new RequestSecurityToken
                {
                    RequestType = Microsoft.IdentityModel.Protocols.WSTrust.WSTrust13Constants.RequestTypes.Issue,
                    AppliesTo = new EndpointAddress("https://login.microsoftonline.com/extSTS.srf"),
                    KeyType = Microsoft.IdentityModel.Protocols.WSTrust.WSTrust13Constants.KeyTypes.Bearer,
                };

                IWSTrustChannelContract channel = factory.CreateChannel();

                //Saml token created...
                GenericXmlSecurityToken genericToken = channel.Issue(rst) as GenericXmlSecurityToken;

                //cookie  created...
                Cookie fedauthcookiesdata = TransformSamlTokenToFedAuth(genericToken.TokenXml.OuterXml, "https://mycompanyo365.sharepoint.com/sites/ProcC/SitePages/Index.aspx");

                //Cookie assigned...
                Response.Cookies["FedAuth"].Domain = "mycompany.com";
                Response.Cookies["FedAuth"].Expires = fedauthcookiesdata.Expires;
                Response.Cookies["FedAuth"].HttpOnly = fedauthcookiesdata.HttpOnly;
                Response.Cookies["FedAuth"].Name = fedauthcookiesdata.Name;
                Response.Cookies["FedAuth"].Path = fedauthcookiesdata.Path;
                Response.Cookies["FedAuth"].Secure = fedauthcookiesdata.Secure;
                Response.Cookies["FedAuth"].Value = fedauthcookiesdata.Value;
        }
        #endregion

转移SMAL以获得FedAuth cookie

 #region Transform Saml Token To get FedAuth cookies
        internal Cookie TransformSamlTokenToFedAuth(string samlToken, string samlSite)
        {
            samlToken = WrapInSoapMessage(samlToken, samlSite);
            string samlServer = samlSite.EndsWith("/") ? samlSite : samlSite + "/";
            Uri samlServerRoot = new Uri(samlServer);
            var sharepointSite = new
            {
                Wctx = "https://mycompanyo365.sharepoint.com/sites/ProcC/SitePages/Index.aspx",
                Wtrealm = "urn:federation:MicrosoftOnline",
                Wreply = String.Format("{0}://{1}/_trust/", samlServerRoot.Scheme, samlServerRoot.Host)
            };

            string stringData = String.Format("wa=wsignin1.0&wctx={0}&wresult={1}", HttpUtility.UrlEncode(sharepointSite.Wctx), HttpUtility.UrlEncode(samlToken));
            Cookie fedAuthCookieValue;

            HttpWebRequest sharepointRequest = HttpWebRequest.Create("https://mycompanyo365.sharepoint.com/_trust/default.aspx/") as HttpWebRequest;
            sharepointRequest.Method = "POST";
            sharepointRequest.ContentType = "application/x-www-form-urlencoded";
            sharepointRequest.CookieContainer = new CookieContainer();
            sharepointRequest.AllowAutoRedirect = false; // This is important
            Stream newStream = sharepointRequest.GetRequestStream();

            byte[] data = Encoding.UTF8.GetBytes(stringData);
            newStream.Write(data, 0, data.Length);
            newStream.Close();

            HttpWebResponse webResponse = sharepointRequest.GetResponse() as HttpWebResponse;
            string[] strCookies = webResponse.Headers.GetValues(1);
            fedAuthCookieValue = webResponse.Cookies["FedAuth"];
            return fedAuthCookieValue;

        }
        #endregion

包装肥皂消息

#region wrap in soap message
            /// <summary>
            /// Wrap SAML token in RequestSecurityTokenResponse soap message
            /// </summary>
            /// <param name="stsResponse">SAML token obtained via active authentication to ADFS</param>
            /// <param name="relyingPartyIdentifier">Identifier of the ADFS relying party that we're hitting</param>
            /// <returns>RequestSecurityTokenResponse soap message</returns>
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Xml.XmlDocument.CreateTextNode(System.String)")]
            private string WrapInSoapMessage(string stsResponse, string relyingPartyIdentifier)
            {
                try
                {
                    XmlDocument samlAssertion = new XmlDocument();
                    samlAssertion.PreserveWhitespace = true;
                    samlAssertion.LoadXml(stsResponse);

                    //Select the book node with the matching attribute value.
                    String notBefore = samlAssertion.DocumentElement.FirstChild.Attributes["NotBefore"].Value;
                    String notOnOrAfter = samlAssertion.DocumentElement.FirstChild.Attributes["NotOnOrAfter"].Value;

                    XmlDocument soapMessage = new XmlDocument();
                    XmlElement soapEnvelope = soapMessage.CreateElement("t", "RequestSecurityTokenResponse", "http://schemas.xmlsoap.org/ws/2005/02/trust");
                    soapMessage.AppendChild(soapEnvelope);
                    XmlElement lifeTime = soapMessage.CreateElement("t", "Lifetime", soapMessage.DocumentElement.NamespaceURI);
                    soapEnvelope.AppendChild(lifeTime);
                    XmlElement created = soapMessage.CreateElement("wsu", "Created", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
                    XmlText createdValue = soapMessage.CreateTextNode(notBefore);
                    created.AppendChild(createdValue);
                    lifeTime.AppendChild(created);
                    XmlElement expires = soapMessage.CreateElement("wsu", "Expires", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
                    XmlText expiresValue = soapMessage.CreateTextNode(notOnOrAfter);
                    expires.AppendChild(expiresValue);
                    lifeTime.AppendChild(expires);
                    XmlElement appliesTo = soapMessage.CreateElement("wsp", "AppliesTo", "http://schemas.xmlsoap.org/ws/2004/09/policy");
                    soapEnvelope.AppendChild(appliesTo);
                    XmlElement endPointReference = soapMessage.CreateElement("wsa", "EndpointReference", "http://www.w3.org/2005/08/addressing");
                    appliesTo.AppendChild(endPointReference);
                    XmlElement address = soapMessage.CreateElement("wsa", "Address", endPointReference.NamespaceURI);
                    XmlText addressValue = soapMessage.CreateTextNode(relyingPartyIdentifier);
                    address.AppendChild(addressValue);
                    endPointReference.AppendChild(address);
                    XmlElement requestedSecurityToken = soapMessage.CreateElement("t", "RequestedSecurityToken", soapMessage.DocumentElement.NamespaceURI);
                    XmlNode samlToken = soapMessage.ImportNode(samlAssertion.DocumentElement, true);
                    requestedSecurityToken.AppendChild(samlToken);
                    soapEnvelope.AppendChild(requestedSecurityToken);
                    XmlElement tokenType = soapMessage.CreateElement("t", "TokenType", soapMessage.DocumentElement.NamespaceURI);
                    XmlText tokenTypeValue = soapMessage.CreateTextNode("urn:oasis:names:tc:SAML:1.0:assertion");
                    tokenType.AppendChild(tokenTypeValue);
                    soapEnvelope.AppendChild(tokenType);
                    XmlElement requestType = soapMessage.CreateElement("t", "RequestType", soapMessage.DocumentElement.NamespaceURI);
                    XmlText requestTypeValue = soapMessage.CreateTextNode("http://schemas.xmlsoap.org/ws/2005/02/trust/Issue");
                    requestType.AppendChild(requestTypeValue);
                    soapEnvelope.AppendChild(requestType);
                    XmlElement keyType = soapMessage.CreateElement("t", "KeyType", soapMessage.DocumentElement.NamespaceURI);
                    XmlText keyTypeValue = soapMessage.CreateTextNode("http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey");
                    keyType.AppendChild(keyTypeValue);
                    soapEnvelope.AppendChild(keyType);

                    return soapMessage.OuterXml;
                }
                catch (Exception ex)
                {
                   // ExceptionLogging.SendErrorToText(ex);
                    Response.Write("WrapInSoapMessage" + "Some Technical Error occurred,Please visit after some time" + ex.InnerException);
                    return "";
                }
            }
            #endregion

我正在尝试做类似的事情...我使用以下代码通过集成身份验证从我们的ADFS服务器生成令牌:

I'm trying to do something similar myself... I used this code to generate a token from our ADFS server using integrated authentication:

        WS2007HttpBinding binding = new WS2007HttpBinding(SecurityMode.Transport);
        binding.Security.Message.EstablishSecurityContext = false;
        binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
        WSTrustChannelFactory factory = new WSTrustChannelFactory((binding), new EndpointAddress(stsEndpoint));
        factory.TrustVersion = TrustVersion.WSTrustFeb2005;
        factory.Credentials.SupportInteractive = false;
        var rst = new RequestSecurityToken
        {
            RequestType = RequestTypes.Issue,
            AppliesTo = new EndpointReference(realm),
            KeyType = KeyTypes.Bearer
        };
        IWSTrustChannelContract channel = factory.CreateChannel();
        return channel.Issue(rst) as GenericXmlSecurityToken;

GenericXmlSecurityToken对象具有TokenXml属性,可以通过XmlReader将其传递到Saml2SecurityTokenHandler.ReadToken以获取令牌.然后,我有了一个参数化SOAP请求的方法,该请求传递到Microsof的STS服务器上:

The GenericXmlSecurityToken object has a TokenXml property which can be passed via an XmlReader into a Saml2SecurityTokenHandler.ReadToken to get a token. I've then got a method that parametises a SOAP request which is passed onto Microsof's STS server:

        StringBuilder s = new StringBuilder();
        s.Append("<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\" xmlns:u=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">");
        s.Append("<s:Header>");
        s.Append("<a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>");
        s.Append("<a:ReplyTo>");
        s.Append("<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>");
        s.Append("</a:ReplyTo>");
        s.Append("<a:To s:mustUnderstand=\"1\">[toUrl]</a:To>");
        s.Append("<o:Security s:mustUnderstand=\"1\" xmlns:o=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">[assertion]");
        s.Append("</o:Security>");
        s.Append("</s:Header>");
        s.Append("<s:Body>");
        s.Append("<t:RequestSecurityToken xmlns:t=\"http://schemas.xmlsoap.org/ws/2005/02/trust\">");
        s.Append("<wsp:AppliesTo xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\">");
        s.Append("<a:EndpointReference>");
        s.Append("<a:Address>[url]</a:Address>");
        s.Append("</a:EndpointReference>");
        s.Append("</wsp:AppliesTo>");
        s.Append("<t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>");
        s.Append("<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>");
        s.Append("<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>");
        s.Append("</t:RequestSecurityToken>");
        s.Append("</s:Body>");
        s.Append("</s:Envelope>");

        string samlRTString = s.ToString();
        samlRTString = samlRTString.Replace("[assertion]", samlAssertion);
        samlRTString = samlRTString.Replace("[url]", url);
        samlRTString = samlRTString.Replace("[toUrl]", toUrl);

        return samlRTString;

无论如何,这就是计划……我还没有机会在我们的环境中测试自己的想法,下个月我不在办公室,但是希望这可以对您有所帮助:)

That's the plan, anyway... I haven't had chance to test my thoughts in our environment yet, and I'm away from the office for the next month, but hopefully this might help you :)