public void Saml2SubjectExtensions_ToXElement_SubjectConfirmationData()
        {
            var destination = new Uri("http://sp.example.com");
            var inResponseTo = new Saml2Id("abc123");
            var notOnOrAfter = DateTime.UtcNow.AddMinutes(2);
            var notBefore = DateTime.UtcNow;

            var saml2SubjectConfirmationData = new Saml2SubjectConfirmationData
            {
                NotOnOrAfter = notOnOrAfter,
                InResponseTo = inResponseTo,
                Recipient = destination,
                NotBefore = notBefore
            };

            var confirmation = saml2SubjectConfirmationData.ToXElement();

            confirmation.Attribute("NotOnOrAfter").Value.Should().Be(notOnOrAfter.ToSaml2DateTimeString());

            confirmation.Attribute("NotBefore").Value.Should().Be(notBefore.ToSaml2DateTimeString());

            confirmation.Attribute("Recipient").Value.Should().Be(destination.OriginalString);

            confirmation.Attribute("InResponseTo").Value.Should().Be(inResponseTo.Value);
        }
        protected override void ValidateConfirmationData(Saml2SubjectConfirmationData confirmationData)
        {
            if (confirmationData == null)
            {
                throw new ArgumentNullException("confirmationData");
            }
            if (confirmationData.Address != null)
            {
                throw new NotSupportedException("Token 'Address' confirmation is not currently supported");
            }
            if (confirmationData.InResponseTo != null)  //ignore in response to (this is present when issuing an authen request but would require persisting state to track)
            {
                //throw new SecurityTokenException("ID4154: confirmationData.InResponseTo not supperted");
            }
            if (null != confirmationData.Recipient && _subjectRecipientValidationMode != SubjectRecipientValidationMode.None)
            {
                switch(_subjectRecipientValidationMode)
                {
                    case SubjectRecipientValidationMode.ExactMatch:
                        if(HttpContext.Current.Request.Url != confirmationData.Recipient)
                        {
                            //handle the special case where we redirected from the old oauth.ashx patterns..
                            if (HttpContext.Current.Request.Url.ToString().Replace("samlresponse","oauth.ashx").ToLower() != confirmationData.Recipient.ToString().ToLower())
                            {
                                throw new ArgumentException(string.Format("Token 'Recipient' value {0} does not match the current URL requirement of {1}", confirmationData.Recipient, HttpContext.Current.Request.Url));
                            }
                        }
                        break;

                    case SubjectRecipientValidationMode.HostAndScheme:
                        if(HttpContext.Current.Request.Url.Host != confirmationData.Recipient.Host || HttpContext.Current.Request.Url.Scheme != confirmationData.Recipient.Scheme)
                        {
                            throw new ArgumentException(string.Format("Token 'Recipient' value {0} does not match the current host and or scheme requirement of {1}", confirmationData.Recipient, HttpContext.Current.Request.Url));
                        }
                        break;

                    case SubjectRecipientValidationMode.HostOnly:
                        if(HttpContext.Current.Request.Url.Host != confirmationData.Recipient.Host)
                        {
                            throw new ArgumentException(string.Format("Token 'Recipient' value {0} does not match the current host requirement of {1}", confirmationData.Recipient, HttpContext.Current.Request.Url));
                        }
                        break;
                }
            }
            DateTime utcNow = DateTime.UtcNow;
            if (confirmationData.NotBefore.HasValue && (AddTimespan(utcNow, base.Configuration.MaxClockSkew) < confirmationData.NotBefore.Value))
            {
                throw new ArgumentOutOfRangeException(string.Format("The token is not valid before {0} and the current time is {1}", confirmationData.NotBefore.Value, utcNow ));
            }
            if (confirmationData.NotOnOrAfter.HasValue && (AddTimespan(utcNow, base.Configuration.MaxClockSkew.Negate()) >= confirmationData.NotOnOrAfter.Value))
            {
                throw new ArgumentOutOfRangeException(string.Format("The token is not valid after {0} and the current time is {1}", confirmationData.NotOnOrAfter.Value, utcNow));
            }
        }
        /// <summary>
        /// Initializes an instance of <see cref="Saml2SubjectConfirmation"/> from a <see cref="Uri"/> indicating the
        /// method of confirmation and <see cref="Saml2SubjectConfirmationData"/>.
        /// </summary>
        /// <param name="method">The <see cref="Uri"/> to use for initialization.</param>
        /// <param name="data">The <see cref="Saml2SubjectConfirmationData"/> to use for initialization.</param>
        public Saml2SubjectConfirmation(Uri method, Saml2SubjectConfirmationData data)
        {
            if (null == method)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("method");
            }

            if (!method.IsAbsoluteUri)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("method", SR.GetString(SR.ID0013));
            }

            this.method = method;
            this.data = data;
        }
        /// <summary>
        /// Initializes an instance of <see cref="Saml2SubjectConfirmation"/> from a <see cref="Uri"/> indicating the
        /// method of confirmation and <see cref="Saml2SubjectConfirmationData"/>.
        /// </summary>
        /// <param name="method">The <see cref="Uri"/> to use for initialization.</param>
        /// <param name="data">The <see cref="Saml2SubjectConfirmationData"/> to use for initialization.</param>
        public Saml2SubjectConfirmation(Uri method, Saml2SubjectConfirmationData data)
        {
            if (null == method)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("method");
            }

            if (!method.IsAbsoluteUri)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("method", SR.GetString(SR.ID0013));
            }

            this.method = method;
            this.data   = data;
        }
        /// <summary>
        /// Writes the &lt;saml:SubjectConfirmationData> element.
        /// </summary>
        /// <remarks>
        /// When the data.KeyIdentifiers collection is not empty, an xsi:type
        /// attribute will be written specifying saml:KeyInfoConfirmationDataType.
        /// </remarks>
        /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2SubjectConfirmationData"/>.</param>
        /// <param name="data">The <see cref="Saml2SubjectConfirmationData"/> to serialize.</param>
        protected virtual void WriteSubjectConfirmationData(XmlWriter writer, Saml2SubjectConfirmationData data)
        {
            if (null == writer)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }

            if (null == data)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
            }

            // <SubjectConfirmationData>
            writer.WriteStartElement(Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace);

            // @attributes

            // @xsi:type
            if (data.KeyIdentifiers != null && data.KeyIdentifiers.Count > 0)
            {
                writer.WriteAttributeString("type", XmlSchema.InstanceNamespace, Saml2Constants.Types.KeyInfoConfirmationDataType);
            }

            // @Address - optional
            if (!string.IsNullOrEmpty(data.Address))
            {
                writer.WriteAttributeString(Saml2Constants.Attributes.Address, data.Address);
            }

            // @InResponseTo - optional
            if (null != data.InResponseTo)
            {
                writer.WriteAttributeString(Saml2Constants.Attributes.InResponseTo, data.InResponseTo.Value);
            }

            // @NotBefore - optional
            if (null != data.NotBefore)
            {
                writer.WriteAttributeString(Saml2Constants.Attributes.NotBefore, XmlConvert.ToString(data.NotBefore.Value.ToUniversalTime(), DateTimeFormats.Generated));
            }

            // @NotOnOrAfter - optional
            if (null != data.NotOnOrAfter)
            {
                writer.WriteAttributeString(Saml2Constants.Attributes.NotOnOrAfter, XmlConvert.ToString(data.NotOnOrAfter.Value.ToUniversalTime(), DateTimeFormats.Generated));
            }

            // @Recipient - optional
            if (null != data.Recipient)
            {
                writer.WriteAttributeString(Saml2Constants.Attributes.Recipient, data.Recipient.OriginalString);
            }

            // Content

            // <ds:KeyInfo> 0-OO
            foreach (SecurityKeyIdentifier keyIdentifier in data.KeyIdentifiers)
            {
                this.WriteSubjectKeyInfo(writer, keyIdentifier);
            }

            // </SubjectConfirmationData>
            writer.WriteEndElement();
        }
        /// <summary>
        /// Reads the &lt;saml:SubjectConfirmationData> element.
        /// </summary>
        /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2SubjectConfirmationData"/> element.</param>
        /// <returns>An instance of <see cref="Saml2SubjectConfirmationData"/> .</returns>
        /// <remarks>
        /// The default implementation handles the unextended element 
        /// as well as the extended type saml:KeyInfoConfirmationDataType.
        /// </remarks>
        protected virtual Saml2SubjectConfirmationData ReadSubjectConfirmationData(XmlReader reader)
        {
            if (null == reader)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }

            if (!reader.IsStartElement(Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace))
            {
                reader.ReadStartElement(Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace);
            }

            try
            {
                Saml2SubjectConfirmationData confirmationData = new Saml2SubjectConfirmationData();
                bool isEmpty = reader.IsEmptyElement;

                // @attributes
                string value;

                // @xsi:type
                bool requireKeyInfo = false;
                XmlQualifiedName type = XmlUtil.GetXsiType(reader);

                if (null != type)
                {
                    if (XmlUtil.EqualsQName(type, Saml2Constants.Types.KeyInfoConfirmationDataType, Saml2Constants.Namespace))
                    {
                        requireKeyInfo = true;
                    }
                    else if (!XmlUtil.EqualsQName(type, Saml2Constants.Types.SubjectConfirmationDataType, Saml2Constants.Namespace))
                    {
                        throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4112, type.Name, type.Namespace));
                    }
                }

                // KeyInfoConfirmationData cannot be empty
                if (requireKeyInfo && isEmpty)
                {
                    throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.GetString(SR.ID4111)));
                }

                // @Address - optional
                value = reader.GetAttribute(Saml2Constants.Attributes.Address);
                if (!string.IsNullOrEmpty(value))
                {
                    confirmationData.Address = value;
                }

                // @InResponseTo - optional
                value = reader.GetAttribute(Saml2Constants.Attributes.InResponseTo);
                if (!string.IsNullOrEmpty(value))
                {
                    confirmationData.InResponseTo = new Saml2Id(value);
                }

                // @NotBefore - optional
                value = reader.GetAttribute(Saml2Constants.Attributes.NotBefore);
                if (!string.IsNullOrEmpty(value))
                {
                    confirmationData.NotBefore = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
                }

                // @NotOnOrAfter - optional
                value = reader.GetAttribute(Saml2Constants.Attributes.NotOnOrAfter);
                if (!string.IsNullOrEmpty(value))
                {
                    confirmationData.NotOnOrAfter = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
                }

                // @Recipient - optional
                value = reader.GetAttribute(Saml2Constants.Attributes.Recipient);
                if (!string.IsNullOrEmpty(value))
                {
                    if (!UriUtil.CanCreateValidUri(value, UriKind.Absolute))
                    {
                        throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Recipient, Saml2Constants.Elements.SubjectConfirmationData));
                    }

                    confirmationData.Recipient = new Uri(value);
                }

                // Contents
                reader.Read();

                if (!isEmpty)
                {
                    // <ds:KeyInfo> 0-OO OR 1-OO
                    if (requireKeyInfo)
                    {
                        confirmationData.KeyIdentifiers.Add(this.ReadSubjectKeyInfo(reader));
                    }

                    while (reader.IsStartElement(XmlSignatureConstants.Elements.KeyInfo, XmlSignatureConstants.Namespace))
                    {
                        confirmationData.KeyIdentifiers.Add(this.ReadSubjectKeyInfo(reader));
                    }

                    // If this isn't KeyInfo restricted, there might be open content here ...
                    if (!requireKeyInfo && XmlNodeType.EndElement != reader.NodeType)
                    {
                        // So throw and tell the user how to handle the open content
                        throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4114, Saml2Constants.Elements.SubjectConfirmationData));
                    }

                    reader.ReadEndElement();
                }

                return confirmationData;
            }
            catch (Exception e)
            {
                if (System.Runtime.Fx.IsFatal(e))
                    throw;
                
                Exception wrapped = TryWrapReadException(reader, e);
                if (null == wrapped)
                {
                    throw;
                }
                else
                {
                    throw wrapped;
                }
            }
        }
        /// <summary>
        /// Validates the Saml2SubjectConfirmation data.
        /// </summary>
        /// <param name="confirmationData">The Saml2 subject confirmation data.</param>
        protected virtual void ValidateConfirmationData(Saml2SubjectConfirmationData confirmationData)
        {
            if (null == confirmationData)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("confirmationData");
            }

            if (null != confirmationData.Address)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4153)));
            }

            if (null != confirmationData.InResponseTo)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4154)));
            }

            if (null != confirmationData.Recipient)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4157)));
            }

            DateTime now = DateTime.UtcNow;

            if (null != confirmationData.NotBefore
                    && DateTimeUtil.Add(now, Configuration.MaxClockSkew) < confirmationData.NotBefore.Value)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4176, confirmationData.NotBefore.Value, now)));
            }

            if (null != confirmationData.NotOnOrAfter
                && DateTimeUtil.Add(now, Configuration.MaxClockSkew.Negate()) >= confirmationData.NotOnOrAfter.Value)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4177, confirmationData.NotOnOrAfter.Value, now)));
            }
        }
 protected override void ValidateConfirmationData(Saml2SubjectConfirmationData confirmationData)
 {
 }
        private void AddSubjectConfirmationData(int subjectConfirmationLifetime)
        {
            var subjectConfirmationData = new Saml2SubjectConfirmationData
            {
                Recipient = Destination.Uri,
                NotOnOrAfter = DateTime.UtcNow.AddMinutes(subjectConfirmationLifetime),
            };

            Saml2SecurityToken.Assertion.Subject.SubjectConfirmations.Clear();
            Saml2SecurityToken.Assertion.Subject.SubjectConfirmations.Add(new Saml2SubjectConfirmation(Saml2Constants.Saml2BearerToken, subjectConfirmationData));
        }