Click here to Skip to main content
15,887,267 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I have written an MVC controller for a much larger web application that facilitates an SSO integration with a business partner using SAML 2.0.
The users for web application must first authenticate against Active Directory before having access.

The application simply pulls additional data from the AD object for the user currently logged in then uses this data to generate the SAML Assertion and then posts it to their SSO provider Url using the following code:

/* Build & Post Assertion */
string _ssoPostData = _sso.GenerateRequest();

/* Send Data to Provider */
_request = (HttpWebRequest)WebRequest.Create(_sso.Recipient);
_request.CookieContainer = _cookieContainer;
_request.AllowAutoRedirect = true;
_request.UserAgent = "Identity Provider Client";
_request.Method = "POST";
_request.ContentType = "application/x-www-form-urlencoded";
_request.ContentLength = _ssoPostData.Length;

Stream _requestStream = _request.GetRequestStream();
_requestStream.Write(System.Text.Encoding.UTF8.GetBytes(_ssoPostData), 0, _ssoPostData.Length);

_response = (HttpWebResponse)_request.GetResponse();


The problem I'm trying to solve is this; when the WebRequest is made and a connection to the server is established, the server assigns a JSESSIONID for this connection. The response received contains the destination URL the user should be redirected to along with 2 cookies, one with the JSESSIONID value and another with a USERID value, that must be returned with every subsequent call to the server.
So my question is, is there a way to take the session/cookies created when the web request/session is established to the SSO server and hand that session back to the browser that the web application was launched from?

Any help is very much appreciated.

Thanks,
Richard

What I have tried:

I've tried using
Response.Redirect()
with the Url returned by the call to the SSO service but it still invalidates the session and takes me back to the services login page.
Posted
Updated 1-Dec-17 7:30am
v2
Comments
F-ES Sitecore 29-Nov-17 6:54am    
This isn't possible, no. You're effectively trying to circumvent the very security principals that these systems are enforcing. If the user wants to be authenticated to that service in their browser then they have to authenticate themselves from that browser. You can't authenticate people on other websites without their intervention.
Richie_W 30-Nov-17 14:19pm    
Thanks F-ES Sitecore.

I think I may not have been very clear with explaining what I am trying to do. This is actually nothing more than an IdP-initiated SSO session.

My company's users will be the clients and our AD server will provide IdP services.

The anticipated workflow will be as follows:

1. One of our users comes into the office first thing and logs into their workstation with their user creds.

2. They then open their browser which defaults to the company's intranet page and on this page, there is a button that says "Connect to Business Partner's Portal"

3. The user clicks the button which calls my webpage that queries AD and pulls additional information for generating the SAML Assertion.

4. The webpage instantiates a new HttpWebRequest object and uses this to POST the SAML request which is then responded to by our Business Partner's SSO consumption service. Before the response is generated, the service validates the request and if successfully validated, it sends back the session cookies mentioned in my initial question and also the destination URL of the location the user is taken to after successfully logging into their system.

It's this last part I'm having issues with is taking the data returned by the HttpWebRequest and somehow passing that session to the browser the user initially launched the intranet page with the button that started this process.

I'm not sure if this makes any difference but I thought I would be a little more verbose.

I have also included the code for my page that gets called from the intranet button:

   public partial class sso : System.Web.UI.Page
    {
        [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool InternetGetCookieEx(
           string url,
           string cookieName,
           StringBuilder cookieData,
           ref int size,
           Int32 dwFlags,
           IntPtr lpReserved);

        [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool InternetSetCookie(
            string lpszUrlName,
            string lpszCookieName,
            string lpszCookieData);

        private const Int32 InternetCookieHttponly = 0x2000;

        protected void Page_Load(object sender, EventArgs e)
        {
            SSO _sso;
            UserPrincipal _user;

            HttpWebRequest _request = null;
            HttpWebResponse _response = null;
            CookieContainer _cookieContainer = new CookieContainer();

            string _userName = Environment.UserName;
            string _domain = Environment.UserDomainName;

            string _destUrl = String.Empty;

            /* Pull local credentials for logged in user and display it*/
            using (PrincipalContext _principalContext = new PrincipalContext(ContextType.Domain, "MYCOMPANY"))
            {
                _user = UserPrincipal.FindByIdentity(_principalContext, _userName);

                if (_user != null)
                {
                    _sso = new SSO();
                    _sso.Recipient = "https://www.business-partner.com/customer/login/sso";
                    _sso.RecipientNameQualifier = "tga";
                    _sso.Issuer = "https://idp.mycompany.com";
                    _sso.Domain = "mycompany.com";
                    _sso.Subject = _user.EmailAddress;
                    _sso.CertificateStoreLocation = null;
                    _sso.CertificateStoreName = null;
                    _sso.CertificateFindType = null;
                    _sso.CertificateFindValue = null;
                    _sso.CertificateFile = System.Configuration.ConfigurationManager.AppSettings["SSOCERT"];
                    _sso.CertificatePassword = "SuperSecurePassword123";
                    _sso.Attributes.Add("uid", _user.DisplayName);
                    _sso.Attributes.Add("mail", _user.EmailAddress);
Richie_W 30-Nov-17 14:23pm    
                    _sso.SignatureType = SigningHelper.SignatureType.Response;
                    _sso.RelayState = "";

                    /* Build & Post Assertion */
                    string _ssoPostData = _sso.GenerateRequest();

                    /* Send Data to Business Partner */

                    _request = (HttpWebRequest)WebRequest.Create(_sso.Recipient);
                    _request.CookieContainer = _cookieContainer;
                    _request.AllowAutoRedirect = true;
                    _request.UserAgent = "My Company's Identity Provider Client";
                    _request.Method = "POST";
                    _request.ContentType = "application/x-www-form-urlencoded";
                    _request.ContentLength = _ssoPostData.Length;

                    Stream _requestStream = _request.GetRequestStream();
                    _requestStream.Write(System.Text.Encoding.UTF8.GetBytes(_ssoPostData), 0, _ssoPostData.Length);

                    _response = (HttpWebResponse)_request.GetResponse();
                    Stream _responseStream = _response.GetResponseStream();

                    Response.Cookies.Clear();

                    CookieCollection _cookieCollection = GetCookies(_cookieContainer);
                    foreach (Cookie _netCookie in _cookieCollection)
                    {
                        InternetSetCookie(_response.ResponseUri.ToString(), null, _netCookie.ToString());

                        /* Convert from NetCookie to HttpCookie */
                        HttpCookie _httpCookie = new HttpCookie(_netCookie.Name);
                        foreach (string value in _netCookie.Value.Split('&'))
                        {
                            string[] val = value.Split('=');
                            if (val.Count() == 1)
                            {
                                _httpCookie.Values.Add(_netCookie.Name, val[0]);
                            }
                            else if(val.Count() == 2)
                            {
                                _httpCookie.Values.Add(val[0], val[1]);
                            }
                        }

                        /* Copy NetCookie properties to HttpCookie */
                        _httpCookie.Domain = _netCookie.Domain;
                        _httpCookie.Expires = _netCookie.Expires;
                        _httpCookie.HttpOnly = _netCookie.HttpOnly;
                        _httpCookie.Path = _netCookie.Path;
                        _httpCookie.Secure = _netCookie.Secure;

                        /* Append cookie to Response object */
                        Response.Cookies.Add(_httpCookie);  
                    }

                    Response.Redirect(_response.ResponseUri.ToString());
                }
            }
        }
        private CookieCollection GetCookies(CookieContainer container)
        {
            var allCookies = new CookieCollection();
            var domainTableField = container.GetType().GetRuntimeFields().FirstOrDefault(x => x.Name == "m_domainTable");
            var domains = (IDictionary)domainTableField.GetValue(container);

            foreach (var val in domains.Values)
            {
                var type = val.GetType().GetRuntimeFields().First(x => x.Name == "m_list");
                var values = (IDictionary)type.GetValue(val);
                foreach (CookieCollection cookies in values.Values)
                {
                    allCookies.Add(cookies);
                }
            }
            return allCookies;
        }
        public static CookieContainer GetUriCookieContainer(Uri uri)
        {
            CookieContainer cookies = null;
            // Determine the size of the cookie
            int datasize = 8192 * 16;
            StringBuilder cookieData = new StringBuilder(datasize);
            if (!InternetGetCookieEx(uri.ToString
Richie_W 30-Nov-17 14:26pm    
, null, cookieData, ref datasize, InternetCookieHttponly, IntPtr.Zero))
            {
                if (datasize < 0)
                    return null;
                // Allocate stringbuilder large enough to hold the cookie
                cookieData = new StringBuilder(datasize);
                if (!InternetGetCookieEx(
                    uri.ToString(),
                    null, cookieData,
                    ref datasize,
                    InternetCookieHttponly,
                    IntPtr.Zero))
                    return null;
            }
            if (cookieData.Length > 0)
            {
                cookies = new CookieContainer();
                cookies.SetCookies(uri, cookieData.ToString().Replace(';', ','));
            }
            return cookies;
        }


    }



Any additional insight or direction is appreciated.

Thanks,
Richard
F-ES Sitecore 1-Dec-17 4:42am    
You have to remember that cookies are tied to the domain. If your users access your intranet which then does the authentication with the other website and returns cookies, if you return those cookies to your client browser those cookies are attached to your intranet domain. When that user then contacts any other site that uses these cookies for authentication the browser will *not* send them as those cookies are attached to your intranet, not the site the user is now browsing. You can't set cookies on the client to be used by domains other than the one returning the cookies.

1 solution

Thanks F-ES! That makes perfect sense.

And thanks for the insight on browser behavior. I learned something new today. :)
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900