Example #1
0
        // Process a successful SAML response.
        private void ProcessSuccessSAMLResponse(SAMLResponse samlResponse, string relayState)
        {
            //Processing successful SAML response

            // Load the decryption key.
            X509Certificate2 x509Certificate = GetSelerixCertificate();

            // Extract the asserted identity from the SAML response.
            SAMLAssertion samlAssertion = null;

            if (samlResponse.GetAssertions().Count > 0)
            {
                samlAssertion = samlResponse.GetAssertions()[0];
            }
            else if (samlResponse.GetEncryptedAssertions().Count > 0)
            {
                //"Decrypting assertion");
                samlAssertion = samlResponse.GetEncryptedAssertions()[0].Decrypt(x509Certificate.PrivateKey, null);
            }
            else if (samlResponse.GetSignedAssertions().Count > 0)
            {
                samlAssertion = new SAMLAssertion(samlResponse.GetSignedAssertions()[0]);
            }
            else
            {
                throw new ArgumentException("No assertions in response");
            }

            // Get the subject name identifier.
            string userName = null;

            if (samlAssertion.Subject.NameID != null)
            {
                userName = samlAssertion.Subject.NameID.NameIdentifier;
            }
            else if (samlAssertion.Subject.EncryptedID != null)
            {
                //"Decrypting ID");
                NameID nameID = samlAssertion.Subject.EncryptedID.Decrypt(x509Certificate.PrivateKey, null);
                userName = nameID.NameIdentifier;
            }
            else
            {
                throw new ArgumentException("No name in subject");
            }

            Dictionary <string, string> outputData = new Dictionary <string, string>();

            foreach (AttributeStatement attributeStatement in samlAssertion.GetAttributeStatements())
            {
                foreach (SAMLAttribute samlAttribute in attributeStatement.GetUnencryptedAttributes())
                {
                    foreach (AttributeValue attributeValue in samlAttribute.Values)
                    {
                        if (!outputData.ContainsKey(samlAttribute.Name))
                        {
                            outputData.Add(samlAttribute.Name, attributeValue.ToString());
                        }
                        else
                        {
                            outputData[samlAttribute.Name] = attributeValue.ToString();
                        }
                    }
                }
                foreach (EncryptedAttribute encryptedAttribute in attributeStatement.GetEncryptedAttributes())
                {
                    SAMLAttribute samlAttribute = encryptedAttribute.Decrypt(x509Certificate.PrivateKey, null);
                    foreach (AttributeValue attributeValue in samlAttribute.Values)
                    {
                        if (!outputData.ContainsKey(samlAttribute.Name))
                        {
                            outputData.Add(samlAttribute.Name, attributeValue.ToString());
                        }
                        else
                        {
                            outputData[samlAttribute.Name] = attributeValue.ToString();
                        }
                    }
                }
            }

            // prevent the output of aspx page from being cached by the browser
            Response.AddHeader("Cache-Control", "no-cache");
            Response.AddHeader("Pragma", "no-cache");

            if (outputData.ContainsKey("Transmittal"))
            {
                Session["Transmittal"] = Selerix.Foundation.Data.SerializationHelper.DeserializeFromString(outputData["Transmittal"], typeof(Transmittal));
            }
            else
            {
                Session["Transmittal"] = null;
            }

            Session["SAMLParameters"] = outputData;

            Response.Redirect("~/ShowTransmittal.aspx", false);

            //"Processed successful SAML response");
        }
Example #2
0
        public ActionResult SingleSignOn(string attributes, string targetUrl, string partnerSP)
        {
            try
            {
                // Initiate single sign-on to the service provider (IdP-initiated SSO)]
                // by sending a SAML response containing a SAML assertion to the SP.

                // get the member id (was IWS number) from the database
                var member = Services.MemberService.GetByUsername(User.Identity.Name);
                Trace.TraceInformation(DateTime.Now.ToShortTimeString() + ":" + string.Format("---------------------USER '{0}' initiated the SSO---------------------", member.Username));

                // Create a dictionary of attributes to add to the SAML assertion
                var attribs = new Dictionary <string, string>();


                /////////////////////////////////////////////////////////////////////////
                // SAML Parameter Configurations
                /////////////////////////////////////////////////////////////////////////

                // Attributes for StatDoctors
                if (partnerSP == "StatDoctors")
                {
                    string AccountUniqueContactId = member.GetValue("yNumber").ToString();

                    string AccountFamilyId = member.GetValue("yNumber").ToString();
                    if (AccountFamilyId.Length > 7)
                    {
                        AccountFamilyId = AccountFamilyId.Substring(0, 7);
                    }

                    string FamilyDependentId = member.GetValue("yNumber").ToString();
                    if (FamilyDependentId.Length > 7)
                    {
                        FamilyDependentId = FamilyDependentId.Substring(7, 2);
                    }
                    {
                        // Create attribute list an populate with needed data
                        var attrib = new Dictionary <string, string>
                        {
                            { "AccountUniqueContactId", AccountUniqueContactId },
                            { "AccountFamilyId", AccountFamilyId },
                            { "FamilyDependentId", FamilyDependentId },
                            { "PartnerId", "AC4134" },
                            { "PartnerAccountId", "" },
                            { "ReturnUrl", "" }
                        };


                        // Send an IdP initiated SAML assertion
                        SAMLIdentityProvider.InitiateSSO(
                            Response,
                            member.GetValue("yNumber").ToString(),
                            attrib,
                            "",
                            partnerSP);
                    }
                }

                // Attributes for US Script
                if (partnerSP == "USScript")
                {
                    string yNumber = member.GetValue("yNumber").ToString();
                    if (yNumber.Length > 7)
                    {
                        yNumber = yNumber.Substring(0, 7);
                    }

                    var samlAttributes = new Dictionary <string, string>
                    {
                        { "urn:uss:saml:attrib::id", yNumber },
                        { "urn:uss:saml:attrib::firstname", member.GetValue("msFirstName").ToString() },
                        { "urn:uss:saml:attrib::lastname", member.GetValue("msLastName").ToString() },
                        { "urn:uss:saml:attrib::groupid", member.GetValue("groupId").ToString() },
                        { "urn:uss:saml:attrib::dateofbirth", Convert.ToDateTime(member.GetValue("birthday")).ToString("yyyy-MM-dd") },
                        { "urn:uss:saml:attrib::email", member.Email }
                    };

                    PgpSAML20Assertion.GuideSSO(Response, partnerSP, String.Empty, samlAttributes);
                }

                // Attributes for MagnaCare
                if (partnerSP == "MagnaCare")
                {
                    var samlAttributes = new Dictionary <string, string>
                    {
                        { "member:id", member.GetValue("yNumber").ToString() },
                        { "member:first_name", member.GetValue("msFirstName").ToString() },
                        { "member:last_name", member.GetValue("msLastName").ToString() },
                        { "member:product", member.GetValue("healthPlanName").ToString() }
                    };

                    SAML20Assertion.GuideSSO(Response, partnerSP, member.GetValue("yNumber").ToString(), samlAttributes);
                }

                // Attributes for HealthX
                if (partnerSP == "https://secure.healthx.com/PublicService/SSO/AutoLogin.aspx" ||
                    partnerSP == "https://secure.healthx.com/PublicService/SSO/AutoLogin.aspx?mobile=1")
                {
                    // Create attribute list an populate with needed data
                    var attrib = new List <SAMLAttribute>
                    {
                        // Version 1 is constant value set by HealthX
                        new SAMLAttribute("Version", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "Version",
                                          "xs:string", "1"),
                        // This is the site ID and is redundant since it is in the Assertion consumer url. I added this for completeness
                        new SAMLAttribute("ServiceId", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "ServiceID",
                                          "xs:string", "d99bfe58-3896-4eb6-9586-d2f9ae673052"),
                        // This is the service ID and is redundant since it is in the Assertion consumer url. I added this for completeness
                        new SAMLAttribute("SiteId", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "SiteId", "xs:string",
                                          "e6fa832c-fbd3-48c7-860f-e4f04b22bab7"),
                        new SAMLAttribute("RelationshipCode", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
                                          "RelationshipCode", "xs:string", "18"),
                        new SAMLAttribute("UserId", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "UserId", "xs:string",
                                          member.GetValue("yNumber").ToString().ToUpper()),
                        new SAMLAttribute("MemberLastName", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
                                          "MemberLastName", "xs:string", member.GetValue("msLastName").ToString().ToUpper()),
                        new SAMLAttribute("MemberFirstName", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
                                          "MemberFirstName", "xs:string", member.GetValue("msFirstName").ToString().ToUpper()),
                        new SAMLAttribute("UserLastName", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "UserLastName",
                                          "xs:string", member.GetValue("msLastName").ToString().ToUpper()),
                        new SAMLAttribute("UserFirstName", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
                                          "UserFirstName", "xs:string", member.GetValue("msFirstName").ToString().ToUpper())
                    };

                    // Nest a node named ServiceId in the RedirectInfo attribute
                    // Add a serializer to allow the nesting of the serviceid attribute without it being url encoded
                    if (!AttributeType.IsAttributeValueSerializerRegistered("RedirectInfo", null))
                    {
                        AttributeType.RegisterAttributeValueSerializer("RedirectInfo", null, new XmlAttributeValueSerializer());
                    }

                    // Add Redirect Info xml
                    var xmlRedirectInfo = new XmlDocument {
                        PreserveWhitespace = true
                    };
                    xmlRedirectInfo.LoadXml(targetUrl);
                    var attrRedirectInfo = new SAMLAttribute("RedirectInfo", "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", "RedirectInfo");
                    attrRedirectInfo.Values.Add(new AttributeValue(xmlRedirectInfo.DocumentElement));
                    attrib.Add(attrRedirectInfo);

                    // Send an IdP initiated SAML assertion
                    SAMLIdentityProvider.InitiateSSO(
                        Response,
                        member.GetValue("yNumber").ToString(),
                        attrib.ToArray(),
                        "",
                        partnerSP);
                }

                // Attributes for Morneau Shapell
                if (partnerSP == "SBCSystems")
                {
                    // Replace the template variables in the url
                    if (targetUrl.IndexOf("<%PLANID%>") != -1)
                    {
                        targetUrl = targetUrl.Replace("<%PLANID%>", member.GetValue("healthplanid").ToString());
                    }

                    // Replace "initialEnrollment" with "specialEnrollmentSelect" if outside of 11/15-3/31
                    if (targetUrl.Contains("initialEnrollment") && !IsInInitialEnrollmentPeriod())
                    {
                        targetUrl = targetUrl.Replace("initialEnrollment", "specialEnrollmentSelect");
                    }

                    // Send an IdP initiated SAML assertion
                    SAMLIdentityProvider.InitiateSSO(
                        Response,
                        member.GetValue("memberId").ToString(),
                        attribs,
                        targetUrl,
                        partnerSP);
                }

                // Add the response to the ViewBag so we can access it on the front end if we need to
                ViewBag.Response     = Response;
                TempData["response"] = Response;
                // Return an empty response since we wait for the SAML consumer to send us the requested page
                return(new EmptyResult());
            }
            catch (Exception ex)
            {
                // Create an error message with sufficient info to contact the user
                string additionalInfo = "SSO Error for user " + User.Identity.Name + ". Partner: " + partnerSP + ". TargetUrl: " + targetUrl + ".";
                // Add the error message to the log4net output
                log4net.GlobalContext.Properties["additionalInfo"] = additionalInfo;
                // Log the error
                logger.Error("Unable to use SSO", ex);

                return(new EmptyResult());
            }
        }
Example #3
0
        public async Task <ActionResult> SSOService()
        {
            var logger = log4net.LogManager.GetLogger("SAMLController");

            logger.Info("SSOService() - Single sign-on service entered");

            var pendingSessionKey = Session[ssoPendingSessionKey];

            logger.Debug("Session[ssoPendingSessionKey] = " + ((pendingSessionKey == null) ? "null" : pendingSessionKey.ToString()));

            // Either an authn request has been received or login has just completed in response to a previous authn request.
            // The SSO pending session flag is false if an authn request is expected. Otherwise, it is true if
            // a login has just completed and control is being returned to this page.
            bool ssoPending = pendingSessionKey != null && (bool)Session[ssoPendingSessionKey] == true;

            logger.Debug("ssoPending = " + ssoPending.ToString());
            logger.Debug("User.Identity.IsAuthenticated = " + User.Identity.IsAuthenticated.ToString());
            if (!(ssoPending && User.Identity.IsAuthenticated))
            {
                string partnerSP = null;

                // Receive the authn request from the service provider (SP-initiated SSO).
                SAMLIdentityProvider.ReceiveSSO(Request, out partnerSP);
                logger.Debug("partnerSP = " + partnerSP);

                // If the user isn't logged in at the identity provider, force the user to login.
                if (!User.Identity.IsAuthenticated)
                {
                    Session[ssoPendingSessionKey] = true;
                    logger.Info("User not authenticated, redirecting to login page()");

                    FormsAuthentication.RedirectToLoginPage();
                    return(new EmptyResult());
                }
            }

            Session[ssoPendingSessionKey] = null;

            // The user is logged in at the identity provider.
            // Respond to the authn request by sending a SAML response containing a SAML assertion to the SP.
            // Use the configured or logged in user name as the user name to send to the service provider (SP).
            // Include some user attributes.
            var userName = WebConfigurationManager.AppSettings[AppSettings.SubjectName];

            var identity = (ClaimsIdentity)User.Identity;

            if (string.IsNullOrEmpty(userName))
            {
                var bid = identity.FindFirst("bid_id");
                if (bid == null)
                {
                    logger.Info("bid_id claim not found, redirecting to login page");
                    FormsAuthentication.RedirectToLoginPage();
                    return(new EmptyResult());
                }

                userName = bid.Value;

                var firstName = identity.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname");
                var lastName  = identity.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname");

                logger.Info("Username obtained from BankID : " + userName);
                logger.Info("Firstname from BankID : " + ((firstName == null) ? "<not provided>" : firstName.Value));
                logger.Info("Lastname from BankID : " + ((lastName == null) ? "<not provided>" : lastName.Value));

                var iseSettings = Utility.Configuration.Settings.IseServer;

                var iseUri = new UriBuilder("https", iseSettings.ServerIP.ToString(), 9060);

                var developerConnection = new CiscoISE.ISEConnection(iseUri.Uri, iseSettings.ApiUsername, iseSettings.ApiPassword);

                var portals = await CiscoISE.Portals.Get(developerConnection);

                if (portals == null)
                {
                    throw new Exception("Could not access ISE to enumerate portals");
                }

                logger.Debug("Retrieved portals from the Cisco ISE server using the API account");

                // TODO : Research whether this is the best way to get the sponsor portal.
                var sponsorPortal = portals.Where(x => x.Name == "Sponsor Portal (default)").FirstOrDefault();
                if (sponsorPortal == null)
                {
                    throw new Exception("Could not access ISE to find GUID for Sponsor Portal (default)");
                }

                logger.Debug("Sponsor portal found on ISE server : " + sponsorPortal.Name);

                var sponsorConnection = new CiscoISE.ISEConnection(iseUri.Uri, iseSettings.SponsorPortalUsername, iseSettings.SponsorPortalPassword);

                logger.Debug("Finding an existing guest user with username : "******"Retrieved user " + userName + " from ISE server\n" + JsonConvert.SerializeObject(guestUser));
                }
                else
                {
                    logger.Info("Existing user (" + userName + ") not found, attempt to create a new one");

                    logger.Debug("Getting GuestTypes from ISE server");
                    // TODO : Catch errors on connecting to the API
                    var guestTypes = await CiscoISE.GuestTypes.Get(developerConnection);

                    if (guestTypes == null)
                    {
                        // TODO : Provide a better method of handling API issues
                        throw new Exception("Failed to get a list of GuestTypes from Cisco ISE");
                    }

                    // TODO : Provide a configuration option to allow specific guest types to be
                    //          chosen based on how the user is logging in.
                    var guestType = guestTypes.FirstOrDefault();
                    if (guestType == null)
                    {
                        // TODO : Provide a better method of dealing with there not being any guest types
                        throw new Exception("There appears to be no guest types configured in Cisco ISE");
                    }

                    logger.Info("Using first guest type in the list : " + guestType.Name);

                    logger.Debug("Getting list of guest locations from the ISE server");
                    // TODO : Catch exceptions on guest locations
                    var guestLocations = await CiscoISE.GuestLocations.Get(developerConnection);

                    if (guestLocations == null)
                    {
                        // TODO : Provide a better method of handling API issues
                        throw new Exception("Failed to obtain a list of guest locations from Cisco ISE");
                    }

                    // TODO : Provide a configuration option to allow specific guest locations to be
                    //         chosen based on where the user is logging in
                    var guestLocation = guestLocations.FirstOrDefault();
                    if (guestLocation == null)
                    {
                        // TODO : Provide a better method of handling API issues
                        throw new Exception("ISE does not appear to have any guest locations configured");
                    }

                    logger.Info("Using first guest location from the list " + guestLocation.Name);

                    //var randomPassword = Membership.GeneratePassword(16, 4);
                    var randomPassword = CiscoISE.Utility.PasswordGenerator.GenerateStrongGuestPassword();

                    // TODO : Consider removing this from the code. Though, the password is not used anywhere, it could be a "alarm" during a code audit
                    logger.Info("Automatically generated a password for the new user (" + randomPassword + ")");

                    // TODO : Add configuration settings to specify the duration which a guest account is valid
                    var validFrom  = DateTime.Now;
                    var validUntil = validFrom.AddHours(4);

                    guestUser = new GuestUserViewModel
                    {
                        GuestType = guestType.Name, // TODO : File "bug" with Cisco over name referencing instead of ID
                        PortalId  = sponsorPortal.Id.ToString(),
                        GuestInfo = new CiscoISE.GuestInfoViewModel
                        {
                            Username  = userName,
                            Password  = randomPassword,
                            FirstName = firstName == null ? "<unknown>" : firstName.Value,
                            LastName  = lastName == null ? "<unknown>" : lastName.Value,
                            Enabled   = true
                        },
                        GuestAccessInfo = new GuestAccessInfoViewModel
                        {
                            ValidDays = 1,
                            FromDate  = validFrom,
                            ToDate    = validUntil,
                            Location  = guestLocation.Name // TODO : File "bug" with Cisco over name reference instead of ID
                        }
                    };
                    logger.Debug("guestUser = "******"Creating new guest user");
                    var created = await CiscoISE.GuestUsers.Create(
                        sponsorConnection,
                        guestUser
                        );

                    logger.Info(created ? "Guest user created" : "Guest user failed to be created");
                    if (!created)
                    {
                        throw new Exception("Failed to create new guest user, cannot continue login process");
                    }
                }
            }

            var ClaimToAttributes = new []
            {
                new {
                    claimType         = Microsoft.IdentityModel.Protocols.OpenIdConnectParameterNames.AccessToken,
                    samlAttributeName = "bidAccessToken"
                },
                new {
                    claimType         = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth",
                    samlAttributeName = "dateofbirth"
                },
                new {
                    claimType         = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
                    samlAttributeName = "givenname"
                },
                new {
                    claimType         = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname",
                    samlAttributeName = "surname"
                },
                new {
                    claimType         = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
                    samlAttributeName = "name"
                }
            };

            logger.Info("Appending attributes to SAML assertion");
            var samlAttributes = new List <SAMLAttribute>();

            foreach (var claimToAttribute in ClaimToAttributes)
            {
                var claim = identity.FindFirst(claimToAttribute.claimType);
                if (claim != null)
                {
                    var attribute = new SAMLAttribute(
                        claimToAttribute.samlAttributeName,
                        SAMLIdentifiers.AttributeNameFormats.Unspecified,
                        null,
                        "xs:string",
                        claim.Value
                        );

                    samlAttributes.Add(attribute);
                }
            }

            ComponentSpace.SAML2.SAMLController.TraceLevel = System.Diagnostics.TraceLevel.Verbose;
            logger.Info("Sending SSO to SAML service provider");
            SAMLIdentityProvider.SendSSO(Response, userName, samlAttributes.ToArray());

            return(new EmptyResult());
        }
        public ActionResult SingleSignOn(string attributes, string targetUrl, string partnerSP)
        {
            try
            {
                // Initiate single sign-on to the service provider (IdP-initiated SSO)]
                // by sending a SAML response containing a SAML assertion to the SP.

                // get the member id (was IWS number) from the database
                var member = Services.MemberService.GetByUsername(User.Identity.Name);
                Trace.TraceInformation(DateTime.Now.ToShortTimeString() + ":" + string.Format("---------------------USER '{0}' initiated the SSO---------------------", member.Username));

                // Create a dictionary of attributes to add to the SAML assertion
                var attribs = new Dictionary<string, string>();

                /////////////////////////////////////////////////////////////////////////
                // SAML Parameter Configurations
                /////////////////////////////////////////////////////////////////////////

                // Attributes for StatDoctors
                if (partnerSP == "StatDoctors")
                {

                    string AccountUniqueContactId = member.GetValue("yNumber").ToString();

                    string AccountFamilyId = member.GetValue("yNumber").ToString();
                    if (AccountFamilyId.Length > 7)
                        AccountFamilyId = AccountFamilyId.Substring(0, 7);

                    string FamilyDependentId = member.GetValue("yNumber").ToString();
                    if (FamilyDependentId.Length > 7)
                        FamilyDependentId = FamilyDependentId.Substring(7, 2);
                    {
                        // Create attribute list an populate with needed data
                        var attrib = new Dictionary<string, string>
                        {

                            {"AccountUniqueContactId", AccountUniqueContactId},
                            {"AccountFamilyId", AccountFamilyId},
                            {"FamilyDependentId", FamilyDependentId},
                            {"PartnerId", "AC4134"},
                            {"PartnerAccountId", ""},
                           {"ReturnUrl", ""}

                        };

                        // Send an IdP initiated SAML assertion
                        SAMLIdentityProvider.InitiateSSO(
                            Response,
                            member.GetValue("yNumber").ToString(),
                            attrib,
                            "",
                            partnerSP);
                    }
                }

                // Attributes for US Script
                if (partnerSP == "USScript")
                {
                    string yNumber = member.GetValue("yNumber").ToString();
                    if (yNumber.Length > 7)
                        yNumber = yNumber.Substring(0, 7);

                    var samlAttributes = new Dictionary<string, string>
                    {
                        {"urn:uss:saml:attrib::id", yNumber},
                        {"urn:uss:saml:attrib::firstname", member.GetValue("msFirstName").ToString()},
                        {"urn:uss:saml:attrib::lastname", member.GetValue("msLastName").ToString()},
                        {"urn:uss:saml:attrib::groupid", member.GetValue("groupId").ToString()},
                        {"urn:uss:saml:attrib::dateofbirth", Convert.ToDateTime(member.GetValue("birthday")).ToString("yyyy-MM-dd")},
                        {"urn:uss:saml:attrib::email", member.Email}
                    };

                    PgpSAML20Assertion.GuideSSO(Response, partnerSP, String.Empty, samlAttributes);
                }

                // Attributes for MagnaCare
                if (partnerSP == "MagnaCare")
                {
                    var samlAttributes = new Dictionary<string, string>
                    {
                        {"member:id", member.GetValue("yNumber").ToString()},
                        {"member:first_name", member.GetValue("msFirstName").ToString()},
                        {"member:last_name", member.GetValue("msLastName").ToString()},
                        {"member:product", member.GetValue("healthPlanName").ToString()}
                    };

                    SAML20Assertion.GuideSSO(Response, partnerSP, member.GetValue("yNumber").ToString(), samlAttributes);
                }

                // Attributes for HealthX
                if (partnerSP == "https://secure.healthx.com/PublicService/SSO/AutoLogin.aspx"
                    || partnerSP == "https://secure.healthx.com/PublicService/SSO/AutoLogin.aspx?mobile=1")
                {
                    // Create attribute list an populate with needed data
                    var attrib = new List<SAMLAttribute>
                    {
                        // Version 1 is constant value set by HealthX
                        new SAMLAttribute("Version", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "Version",
                            "xs:string", "1"),
                        // This is the site ID and is redundant since it is in the Assertion consumer url. I added this for completeness
                        new SAMLAttribute("ServiceId", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "ServiceID",
                            "xs:string", "d99bfe58-3896-4eb6-9586-d2f9ae673052"),
                        // This is the service ID and is redundant since it is in the Assertion consumer url. I added this for completeness
                        new SAMLAttribute("SiteId", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "SiteId", "xs:string",
                            "e6fa832c-fbd3-48c7-860f-e4f04b22bab7"),
                        new SAMLAttribute("RelationshipCode", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
                            "RelationshipCode", "xs:string", "18"),
                        new SAMLAttribute("UserId", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "UserId", "xs:string",
                            member.GetValue("yNumber").ToString().ToUpper()),
                        new SAMLAttribute("MemberLastName", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
                            "MemberLastName", "xs:string", member.GetValue("msLastName").ToString().ToUpper()),
                        new SAMLAttribute("MemberFirstName", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
                            "MemberFirstName", "xs:string", member.GetValue("msFirstName").ToString().ToUpper()),
                        new SAMLAttribute("UserLastName", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "UserLastName",
                            "xs:string", member.GetValue("msLastName").ToString().ToUpper()),
                        new SAMLAttribute("UserFirstName", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
                            "UserFirstName", "xs:string", member.GetValue("msFirstName").ToString().ToUpper())
                    };

                    // Nest a node named ServiceId in the RedirectInfo attribute
                    // Add a serializer to allow the nesting of the serviceid attribute without it being url encoded
                    if (!AttributeType.IsAttributeValueSerializerRegistered("RedirectInfo", null))
                        AttributeType.RegisterAttributeValueSerializer("RedirectInfo", null, new XmlAttributeValueSerializer());

                    // Add Redirect Info xml
                    var xmlRedirectInfo = new XmlDocument { PreserveWhitespace = true };
                    xmlRedirectInfo.LoadXml(targetUrl);
                    var attrRedirectInfo = new SAMLAttribute("RedirectInfo", "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", "RedirectInfo");
                    attrRedirectInfo.Values.Add(new AttributeValue(xmlRedirectInfo.DocumentElement));
                    attrib.Add(attrRedirectInfo);

                    // Send an IdP initiated SAML assertion
                    SAMLIdentityProvider.InitiateSSO(
                        Response,
                        member.GetValue("yNumber").ToString(),
                        attrib.ToArray(),
                        "",
                        partnerSP);
                }

                // Attributes for Morneau Shapell
                if (partnerSP == "SBCSystems")
                {
                    // Replace the template variables in the url
                    if (targetUrl.IndexOf("<%PLANID%>") != -1)
                        targetUrl = targetUrl.Replace("<%PLANID%>", member.GetValue("healthplanid").ToString());

                    // Replace "initialEnrollment" with "specialEnrollmentSelect" if outside of 11/15-3/31
                    if (targetUrl.Contains("initialEnrollment") && !IsInInitialEnrollmentPeriod())
                    {
                        targetUrl = targetUrl.Replace("initialEnrollment", "specialEnrollmentSelect");
                    }

                    // Send an IdP initiated SAML assertion
                    SAMLIdentityProvider.InitiateSSO(
                        Response,
                        member.GetValue("memberId").ToString(),
                        attribs,
                        targetUrl,
                        partnerSP);
                }

                // Add the response to the ViewBag so we can access it on the front end if we need to
                ViewBag.Response = Response;
                TempData["response"] = Response;
                // Return an empty response since we wait for the SAML consumer to send us the requested page
                return new EmptyResult();
            }
            catch (Exception ex)
            {
                // Create an error message with sufficient info to contact the user
                string additionalInfo = "SSO Error for user " + User.Identity.Name + ". Partner: " + partnerSP + ". TargetUrl: " + targetUrl + ".";
                // Add the error message to the log4net output
                log4net.GlobalContext.Properties["additionalInfo"] = additionalInfo;
                // Log the error
                logger.Error("Unable to use SSO", ex);

                return new EmptyResult();
            }
        }