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 <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 <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)); }