summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--samples/OpenIdProviderWebForms/Code/CustomStore.cs10
-rw-r--r--samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.Designer.cs37
-rw-r--r--samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xsd20
-rw-r--r--samples/RelyingPartyWebForms/Code/CustomStore.cs10
-rw-r--r--samples/RelyingPartyWebForms/Code/CustomStoreDataSet.xsd18
-rw-r--r--samples/RelyingPartyWebForms/Code/CustomStoreDataSet1.Designer.cs37
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/TestReplayProtectedMessage.cs4
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/INonceStore.cs18
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/IReplayProtectedProtocolMessage.cs13
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs22
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs8
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs16
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs5
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs5
16 files changed, 166 insertions, 62 deletions
diff --git a/samples/OpenIdProviderWebForms/Code/CustomStore.cs b/samples/OpenIdProviderWebForms/Code/CustomStore.cs
index b716e8d..d8181fe 100644
--- a/samples/OpenIdProviderWebForms/Code/CustomStore.cs
+++ b/samples/OpenIdProviderWebForms/Code/CustomStore.cs
@@ -79,6 +79,10 @@ namespace OpenIdProviderWebForms.Code {
/// <summary>
/// Stores a given nonce and timestamp.
/// </summary>
+ /// <param name="context">The context, or namespace, within which the
+ /// <paramref name="nonce"/> must be unique.
+ /// The context SHOULD be treated as case-sensitive.
+ /// The value will never be <c>null</c> but may be the empty string.</param>
/// <param name="nonce">A series of random characters.</param>
/// <param name="timestamp">The timestamp that together with the nonce string make it unique.
/// The timestamp may also be used by the data store to clear out old nonces.</param>
@@ -93,7 +97,7 @@ namespace OpenIdProviderWebForms.Code {
/// is retrieved or set using the
/// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property.
/// </remarks>
- public bool StoreNonce(string nonce, DateTime timestamp) {
+ public bool StoreNonce(string context, string nonce, DateTime timestamp) {
// IMPORTANT: If actually persisting to a database that can be reached from
// different servers/instances of this class at once, it is vitally important
// to protect against race condition attacks by one or more of these:
@@ -106,12 +110,12 @@ namespace OpenIdProviderWebForms.Code {
// and display some message to have the user try to log in again, and possibly
// warn them about a replay attack.
lock (this) {
- if (dataSet.Nonce.FindByCode(nonce) != null) {
+ if (dataSet.Nonce.FindByCodeContext(nonce, context) != null) {
return false;
}
TimeSpan maxMessageAge = DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration.Messaging.MaximumMessageLifetime;
- dataSet.Nonce.AddNonceRow(nonce, timestamp.ToLocalTime(), (timestamp + maxMessageAge).ToLocalTime());
+ dataSet.Nonce.AddNonceRow(context, nonce, timestamp.ToLocalTime(), (timestamp + maxMessageAge).ToLocalTime());
return true;
}
}
diff --git a/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.Designer.cs b/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.Designer.cs
index 4870172..d836261 100644
--- a/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.Designer.cs
+++ b/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.Designer.cs
@@ -570,6 +570,8 @@ namespace OpenIdProviderWebForms.Code {
[global::System.Xml.Serialization.XmlSchemaProviderAttribute("GetTypedTableSchema")]
public partial class NonceDataTable : global::System.Data.TypedTableBase<NonceRow> {
+ private global::System.Data.DataColumn columnContext;
+
private global::System.Data.DataColumn columnCode;
private global::System.Data.DataColumn columnIssued;
@@ -607,6 +609,13 @@ namespace OpenIdProviderWebForms.Code {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public global::System.Data.DataColumn ContextColumn {
+ get {
+ return this.columnContext;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Data.DataColumn CodeColumn {
get {
return this.columnCode;
@@ -656,9 +665,10 @@ namespace OpenIdProviderWebForms.Code {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- public NonceRow AddNonceRow(string Code, System.DateTime Issued, System.DateTime Expires) {
+ public NonceRow AddNonceRow(string Context, string Code, System.DateTime Issued, System.DateTime Expires) {
NonceRow rowNonceRow = ((NonceRow)(this.NewRow()));
object[] columnValuesArray = new object[] {
+ Context,
Code,
Issued,
Expires};
@@ -668,9 +678,10 @@ namespace OpenIdProviderWebForms.Code {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- public NonceRow FindByCode(string Code) {
+ public NonceRow FindByCodeContext(string Code, string Context) {
return ((NonceRow)(this.Rows.Find(new object[] {
- Code})));
+ Code,
+ Context})));
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
@@ -687,6 +698,7 @@ namespace OpenIdProviderWebForms.Code {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
internal void InitVars() {
+ this.columnContext = base.Columns["Context"];
this.columnCode = base.Columns["Code"];
this.columnIssued = base.Columns["Issued"];
this.columnExpires = base.Columns["Expires"];
@@ -694,16 +706,19 @@ namespace OpenIdProviderWebForms.Code {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitClass() {
+ this.columnContext = new global::System.Data.DataColumn("Context", typeof(string), null, global::System.Data.MappingType.Element);
+ base.Columns.Add(this.columnContext);
this.columnCode = new global::System.Data.DataColumn("Code", typeof(string), null, global::System.Data.MappingType.Element);
base.Columns.Add(this.columnCode);
this.columnIssued = new global::System.Data.DataColumn("Issued", typeof(global::System.DateTime), null, global::System.Data.MappingType.Element);
base.Columns.Add(this.columnIssued);
this.columnExpires = new global::System.Data.DataColumn("Expires", typeof(global::System.DateTime), null, global::System.Data.MappingType.Element);
base.Columns.Add(this.columnExpires);
- this.Constraints.Add(new global::System.Data.UniqueConstraint("PrimaryKey", new global::System.Data.DataColumn[] {
- this.columnCode}, true));
+ this.Constraints.Add(new global::System.Data.UniqueConstraint("Constraint1", new global::System.Data.DataColumn[] {
+ this.columnCode,
+ this.columnContext}, true));
+ this.columnContext.AllowDBNull = false;
this.columnCode.AllowDBNull = false;
- this.columnCode.Unique = true;
this.columnIssued.AllowDBNull = false;
this.columnExpires.AllowDBNull = false;
}
@@ -893,6 +908,16 @@ namespace OpenIdProviderWebForms.Code {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public string Context {
+ get {
+ return ((string)(this[this.tableNonce.ContextColumn]));
+ }
+ set {
+ this[this.tableNonce.ContextColumn] = value;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public string Code {
get {
return ((string)(this[this.tableNonce.CodeColumn]));
diff --git a/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xsd b/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xsd
index d796d88..295fe74 100644
--- a/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xsd
+++ b/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xsd
@@ -15,19 +15,20 @@
<xs:element name="Association" msprop:Generator_UserTableName="Association" msprop:Generator_RowDeletedName="AssociationRowDeleted" msprop:Generator_RowChangedName="AssociationRowChanged" msprop:Generator_RowClassName="AssociationRow" msprop:Generator_RowChangingName="AssociationRowChanging" msprop:Generator_RowEvArgName="AssociationRowChangeEvent" msprop:Generator_RowEvHandlerName="AssociationRowChangeEventHandler" msprop:Generator_TableClassName="AssociationDataTable" msprop:Generator_TableVarName="tableAssociation" msprop:Generator_RowDeletingName="AssociationRowDeleting" msprop:Generator_TablePropName="Association">
<xs:complexType>
<xs:sequence>
- <xs:element name="DistinguishingFactor" msprop:Generator_UserColumnName="DistinguishingFactor" msprop:Generator_ColumnVarNameInTable="columnDistinguishingFactor" msprop:Generator_ColumnPropNameInRow="DistinguishingFactor" msprop:Generator_ColumnPropNameInTable="DistinguishingFactorColumn" type="xs:string" />
- <xs:element name="Handle" msprop:Generator_UserColumnName="Handle" msprop:Generator_ColumnVarNameInTable="columnHandle" msprop:Generator_ColumnPropNameInRow="Handle" msprop:Generator_ColumnPropNameInTable="HandleColumn" type="xs:string" />
- <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
- <xs:element name="PrivateData" msprop:Generator_UserColumnName="PrivateData" msprop:Generator_ColumnVarNameInTable="columnPrivateData" msprop:Generator_ColumnPropNameInRow="PrivateData" msprop:Generator_ColumnPropNameInTable="PrivateDataColumn" type="xs:base64Binary" />
+ <xs:element name="DistinguishingFactor" msprop:Generator_UserColumnName="DistinguishingFactor" msprop:Generator_ColumnPropNameInRow="DistinguishingFactor" msprop:Generator_ColumnVarNameInTable="columnDistinguishingFactor" msprop:Generator_ColumnPropNameInTable="DistinguishingFactorColumn" type="xs:string" />
+ <xs:element name="Handle" msprop:Generator_UserColumnName="Handle" msprop:Generator_ColumnPropNameInRow="Handle" msprop:Generator_ColumnVarNameInTable="columnHandle" msprop:Generator_ColumnPropNameInTable="HandleColumn" type="xs:string" />
+ <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
+ <xs:element name="PrivateData" msprop:Generator_UserColumnName="PrivateData" msprop:Generator_ColumnPropNameInRow="PrivateData" msprop:Generator_ColumnVarNameInTable="columnPrivateData" msprop:Generator_ColumnPropNameInTable="PrivateDataColumn" type="xs:base64Binary" />
</xs:sequence>
</xs:complexType>
</xs:element>
- <xs:element name="Nonce" msprop:Generator_UserTableName="Nonce" msprop:Generator_RowDeletedName="NonceRowDeleted" msprop:Generator_TableClassName="NonceDataTable" msprop:Generator_RowChangedName="NonceRowChanged" msprop:Generator_RowClassName="NonceRow" msprop:Generator_RowChangingName="NonceRowChanging" msprop:Generator_RowEvArgName="NonceRowChangeEvent" msprop:Generator_RowEvHandlerName="NonceRowChangeEventHandler" msprop:Generator_TablePropName="Nonce" msprop:Generator_TableVarName="tableNonce" msprop:Generator_RowDeletingName="NonceRowDeleting">
+ <xs:element name="Nonce" msprop:Generator_UserTableName="Nonce" msprop:Generator_RowDeletedName="NonceRowDeleted" msprop:Generator_RowChangedName="NonceRowChanged" msprop:Generator_RowClassName="NonceRow" msprop:Generator_RowChangingName="NonceRowChanging" msprop:Generator_RowEvArgName="NonceRowChangeEvent" msprop:Generator_RowEvHandlerName="NonceRowChangeEventHandler" msprop:Generator_TableClassName="NonceDataTable" msprop:Generator_TableVarName="tableNonce" msprop:Generator_RowDeletingName="NonceRowDeleting" msprop:Generator_TablePropName="Nonce">
<xs:complexType>
<xs:sequence>
- <xs:element name="Code" msprop:Generator_UserColumnName="Code" msprop:Generator_ColumnPropNameInRow="Code" msprop:Generator_ColumnVarNameInTable="columnCode" msprop:Generator_ColumnPropNameInTable="CodeColumn" type="xs:string" />
- <xs:element name="Issued" msprop:Generator_UserColumnName="Issued" msprop:Generator_ColumnPropNameInRow="Issued" msprop:Generator_ColumnVarNameInTable="columnIssued" msprop:Generator_ColumnPropNameInTable="IssuedColumn" type="xs:dateTime" />
- <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
+ <xs:element name="Context" msprop:Generator_UserColumnName="Context" msprop:Generator_ColumnPropNameInRow="Context" msprop:Generator_ColumnVarNameInTable="columnContext" msprop:Generator_ColumnPropNameInTable="ContextColumn" type="xs:string" />
+ <xs:element name="Code" msprop:Generator_UserColumnName="Code" msprop:Generator_ColumnVarNameInTable="columnCode" msprop:Generator_ColumnPropNameInRow="Code" msprop:Generator_ColumnPropNameInTable="CodeColumn" type="xs:string" />
+ <xs:element name="Issued" msprop:Generator_UserColumnName="Issued" msprop:Generator_ColumnVarNameInTable="columnIssued" msprop:Generator_ColumnPropNameInRow="Issued" msprop:Generator_ColumnPropNameInTable="IssuedColumn" type="xs:dateTime" />
+ <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -38,9 +39,10 @@
<xs:field xpath="mstns:DistinguishingFactor" />
<xs:field xpath="mstns:Handle" />
</xs:unique>
- <xs:unique name="Nonce_PrimaryKey" msdata:ConstraintName="PrimaryKey" msdata:PrimaryKey="true">
+ <xs:unique name="Constraint1" msdata:PrimaryKey="true">
<xs:selector xpath=".//mstns:Nonce" />
<xs:field xpath="mstns:Code" />
+ <xs:field xpath="mstns:Context" />
</xs:unique>
</xs:element>
</xs:schema> \ No newline at end of file
diff --git a/samples/RelyingPartyWebForms/Code/CustomStore.cs b/samples/RelyingPartyWebForms/Code/CustomStore.cs
index 0444bd8..c4a7854 100644
--- a/samples/RelyingPartyWebForms/Code/CustomStore.cs
+++ b/samples/RelyingPartyWebForms/Code/CustomStore.cs
@@ -34,6 +34,10 @@
/// <summary>
/// Stores a given nonce and timestamp.
/// </summary>
+ /// <param name="context">The context, or namespace, within which the
+ /// <paramref name="nonce"/> must be unique.
+ /// The context SHOULD be treated as case-sensitive.
+ /// The value will never be <c>null</c> but may be the empty string.</param>
/// <param name="nonce">A series of random characters.</param>
/// <param name="timestamp">The timestamp that together with the nonce string make it unique.
/// The timestamp may also be used by the data store to clear out old nonces.</param>
@@ -48,7 +52,7 @@
/// is retrieved or set using the
/// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property.
/// </remarks>
- public bool StoreNonce(string nonce, DateTime timestamp) {
+ public bool StoreNonce(string context, string nonce, DateTime timestamp) {
// IMPORTANT: If actually persisting to a database that can be reached from
// different servers/instances of this class at once, it is vitally important
// to protect against race condition attacks by one or more of these:
@@ -61,12 +65,12 @@
// and display some message to have the user try to log in again, and possibly
// warn them about a replay attack.
lock (this) {
- if (dataSet.Nonce.FindByCode(nonce) != null) {
+ if (dataSet.Nonce.FindByCodeContext(nonce, context) != null) {
return false;
}
TimeSpan maxMessageAge = DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration.Messaging.MaximumMessageLifetime;
- dataSet.Nonce.AddNonceRow(nonce, timestamp.ToLocalTime(), (timestamp + maxMessageAge).ToLocalTime());
+ dataSet.Nonce.AddNonceRow(context, nonce, timestamp.ToLocalTime(), (timestamp + maxMessageAge).ToLocalTime());
return true;
}
}
diff --git a/samples/RelyingPartyWebForms/Code/CustomStoreDataSet.xsd b/samples/RelyingPartyWebForms/Code/CustomStoreDataSet.xsd
index d70c352..b80310e 100644
--- a/samples/RelyingPartyWebForms/Code/CustomStoreDataSet.xsd
+++ b/samples/RelyingPartyWebForms/Code/CustomStoreDataSet.xsd
@@ -15,19 +15,20 @@
<xs:element name="Association" msprop:Generator_UserTableName="Association" msprop:Generator_RowDeletedName="AssociationRowDeleted" msprop:Generator_RowChangedName="AssociationRowChanged" msprop:Generator_RowClassName="AssociationRow" msprop:Generator_RowChangingName="AssociationRowChanging" msprop:Generator_RowEvArgName="AssociationRowChangeEvent" msprop:Generator_RowEvHandlerName="AssociationRowChangeEventHandler" msprop:Generator_TableClassName="AssociationDataTable" msprop:Generator_TableVarName="tableAssociation" msprop:Generator_RowDeletingName="AssociationRowDeleting" msprop:Generator_TablePropName="Association">
<xs:complexType>
<xs:sequence>
- <xs:element name="DistinguishingFactor" msprop:Generator_UserColumnName="DistinguishingFactor" msprop:Generator_ColumnPropNameInRow="DistinguishingFactor" msprop:Generator_ColumnVarNameInTable="columnDistinguishingFactor" msprop:Generator_ColumnPropNameInTable="DistinguishingFactorColumn" type="xs:string" />
- <xs:element name="Handle" msprop:Generator_UserColumnName="Handle" msprop:Generator_ColumnPropNameInRow="Handle" msprop:Generator_ColumnVarNameInTable="columnHandle" msprop:Generator_ColumnPropNameInTable="HandleColumn" type="xs:string" />
- <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
- <xs:element name="PrivateData" msprop:Generator_UserColumnName="PrivateData" msprop:Generator_ColumnPropNameInRow="PrivateData" msprop:Generator_ColumnVarNameInTable="columnPrivateData" msprop:Generator_ColumnPropNameInTable="PrivateDataColumn" type="xs:base64Binary" />
+ <xs:element name="DistinguishingFactor" msprop:Generator_UserColumnName="DistinguishingFactor" msprop:Generator_ColumnVarNameInTable="columnDistinguishingFactor" msprop:Generator_ColumnPropNameInRow="DistinguishingFactor" msprop:Generator_ColumnPropNameInTable="DistinguishingFactorColumn" type="xs:string" />
+ <xs:element name="Handle" msprop:Generator_UserColumnName="Handle" msprop:Generator_ColumnVarNameInTable="columnHandle" msprop:Generator_ColumnPropNameInRow="Handle" msprop:Generator_ColumnPropNameInTable="HandleColumn" type="xs:string" />
+ <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
+ <xs:element name="PrivateData" msprop:Generator_UserColumnName="PrivateData" msprop:Generator_ColumnVarNameInTable="columnPrivateData" msprop:Generator_ColumnPropNameInRow="PrivateData" msprop:Generator_ColumnPropNameInTable="PrivateDataColumn" type="xs:base64Binary" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Nonce" msprop:Generator_UserTableName="Nonce" msprop:Generator_RowDeletedName="NonceRowDeleted" msprop:Generator_RowChangedName="NonceRowChanged" msprop:Generator_RowClassName="NonceRow" msprop:Generator_RowChangingName="NonceRowChanging" msprop:Generator_RowEvArgName="NonceRowChangeEvent" msprop:Generator_RowEvHandlerName="NonceRowChangeEventHandler" msprop:Generator_TableClassName="NonceDataTable" msprop:Generator_TableVarName="tableNonce" msprop:Generator_RowDeletingName="NonceRowDeleting" msprop:Generator_TablePropName="Nonce">
<xs:complexType>
<xs:sequence>
- <xs:element name="Code" msprop:Generator_UserColumnName="Code" msprop:Generator_ColumnVarNameInTable="columnCode" msprop:Generator_ColumnPropNameInRow="Code" msprop:Generator_ColumnPropNameInTable="CodeColumn" type="xs:string" />
- <xs:element name="Issued" msprop:Generator_UserColumnName="Issued" msprop:Generator_ColumnVarNameInTable="columnIssued" msprop:Generator_ColumnPropNameInRow="Issued" msprop:Generator_ColumnPropNameInTable="IssuedColumn" type="xs:dateTime" />
- <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
+ <xs:element name="Context" msprop:Generator_UserColumnName="Context" msprop:Generator_ColumnPropNameInRow="Context" msprop:Generator_ColumnVarNameInTable="columnContext" msprop:Generator_ColumnPropNameInTable="ContextColumn" type="xs:string" />
+ <xs:element name="Code" msprop:Generator_UserColumnName="Code" msprop:Generator_ColumnPropNameInRow="Code" msprop:Generator_ColumnVarNameInTable="columnCode" msprop:Generator_ColumnPropNameInTable="CodeColumn" type="xs:string" />
+ <xs:element name="Issued" msprop:Generator_UserColumnName="Issued" msprop:Generator_ColumnPropNameInRow="Issued" msprop:Generator_ColumnVarNameInTable="columnIssued" msprop:Generator_ColumnPropNameInTable="IssuedColumn" type="xs:dateTime" />
+ <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -38,9 +39,10 @@
<xs:field xpath="mstns:DistinguishingFactor" />
<xs:field xpath="mstns:Handle" />
</xs:unique>
- <xs:unique name="Nonce_PrimaryKey" msdata:ConstraintName="PrimaryKey" msdata:PrimaryKey="true">
+ <xs:unique name="Constraint1" msdata:PrimaryKey="true">
<xs:selector xpath=".//mstns:Nonce" />
<xs:field xpath="mstns:Code" />
+ <xs:field xpath="mstns:Context" />
</xs:unique>
</xs:element>
</xs:schema> \ No newline at end of file
diff --git a/samples/RelyingPartyWebForms/Code/CustomStoreDataSet1.Designer.cs b/samples/RelyingPartyWebForms/Code/CustomStoreDataSet1.Designer.cs
index 49e1a90..9382ca7 100644
--- a/samples/RelyingPartyWebForms/Code/CustomStoreDataSet1.Designer.cs
+++ b/samples/RelyingPartyWebForms/Code/CustomStoreDataSet1.Designer.cs
@@ -570,6 +570,8 @@ namespace RelyingPartyWebForms.Code {
[global::System.Xml.Serialization.XmlSchemaProviderAttribute("GetTypedTableSchema")]
public partial class NonceDataTable : global::System.Data.TypedTableBase<NonceRow> {
+ private global::System.Data.DataColumn columnContext;
+
private global::System.Data.DataColumn columnCode;
private global::System.Data.DataColumn columnIssued;
@@ -607,6 +609,13 @@ namespace RelyingPartyWebForms.Code {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public global::System.Data.DataColumn ContextColumn {
+ get {
+ return this.columnContext;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Data.DataColumn CodeColumn {
get {
return this.columnCode;
@@ -656,9 +665,10 @@ namespace RelyingPartyWebForms.Code {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- public NonceRow AddNonceRow(string Code, System.DateTime Issued, System.DateTime Expires) {
+ public NonceRow AddNonceRow(string Context, string Code, System.DateTime Issued, System.DateTime Expires) {
NonceRow rowNonceRow = ((NonceRow)(this.NewRow()));
object[] columnValuesArray = new object[] {
+ Context,
Code,
Issued,
Expires};
@@ -668,9 +678,10 @@ namespace RelyingPartyWebForms.Code {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- public NonceRow FindByCode(string Code) {
+ public NonceRow FindByCodeContext(string Code, string Context) {
return ((NonceRow)(this.Rows.Find(new object[] {
- Code})));
+ Code,
+ Context})));
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
@@ -687,6 +698,7 @@ namespace RelyingPartyWebForms.Code {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
internal void InitVars() {
+ this.columnContext = base.Columns["Context"];
this.columnCode = base.Columns["Code"];
this.columnIssued = base.Columns["Issued"];
this.columnExpires = base.Columns["Expires"];
@@ -694,16 +706,19 @@ namespace RelyingPartyWebForms.Code {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitClass() {
+ this.columnContext = new global::System.Data.DataColumn("Context", typeof(string), null, global::System.Data.MappingType.Element);
+ base.Columns.Add(this.columnContext);
this.columnCode = new global::System.Data.DataColumn("Code", typeof(string), null, global::System.Data.MappingType.Element);
base.Columns.Add(this.columnCode);
this.columnIssued = new global::System.Data.DataColumn("Issued", typeof(global::System.DateTime), null, global::System.Data.MappingType.Element);
base.Columns.Add(this.columnIssued);
this.columnExpires = new global::System.Data.DataColumn("Expires", typeof(global::System.DateTime), null, global::System.Data.MappingType.Element);
base.Columns.Add(this.columnExpires);
- this.Constraints.Add(new global::System.Data.UniqueConstraint("PrimaryKey", new global::System.Data.DataColumn[] {
- this.columnCode}, true));
+ this.Constraints.Add(new global::System.Data.UniqueConstraint("Constraint1", new global::System.Data.DataColumn[] {
+ this.columnCode,
+ this.columnContext}, true));
+ this.columnContext.AllowDBNull = false;
this.columnCode.AllowDBNull = false;
- this.columnCode.Unique = true;
this.columnIssued.AllowDBNull = false;
this.columnExpires.AllowDBNull = false;
}
@@ -893,6 +908,16 @@ namespace RelyingPartyWebForms.Code {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public string Context {
+ get {
+ return ((string)(this[this.tableNonce.ContextColumn]));
+ }
+ set {
+ this[this.tableNonce.ContextColumn] = value;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public string Code {
get {
return ((string)(this[this.tableNonce.CodeColumn]));
diff --git a/src/DotNetOpenAuth.Test/Mocks/TestReplayProtectedMessage.cs b/src/DotNetOpenAuth.Test/Mocks/TestReplayProtectedMessage.cs
index 396db44..4650de2 100644
--- a/src/DotNetOpenAuth.Test/Mocks/TestReplayProtectedMessage.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/TestReplayProtectedMessage.cs
@@ -19,6 +19,10 @@ namespace DotNetOpenAuth.Test.Mocks {
#region IReplayProtectedProtocolMessage Members
+ string IReplayProtectedProtocolMessage.NonceContext {
+ get { return string.Empty; }
+ }
+
[MessagePart("Nonce")]
string IReplayProtectedProtocolMessage.Nonce {
get;
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/INonceStore.cs b/src/DotNetOpenAuth/Messaging/Bindings/INonceStore.cs
index 205e72e..fff251a 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/INonceStore.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/INonceStore.cs
@@ -14,13 +14,13 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// <summary>
/// Stores a given nonce and timestamp.
/// </summary>
- /// <param name="nonce">
- /// A series of random characters.
- /// </param>
- /// <param name="timestamp">
- /// The timestamp that together with the nonce string make it unique.
- /// The timestamp may also be used by the data store to clear out old nonces.
- /// </param>
+ /// <param name="context">The context, or namespace, within which the
+ /// <paramref name="nonce"/> must be unique.
+ /// The context SHOULD be treated as case-sensitive.
+ /// The value will never be <c>null</c> but may be the empty string.</param>
+ /// <param name="nonce">A series of random characters.</param>
+ /// <param name="timestamp">The timestamp that together with the nonce string make it unique.
+ /// The timestamp may also be used by the data store to clear out old nonces.</param>
/// <returns>
/// True if the nonce+timestamp (combination) was not previously in the database.
/// False if the nonce was stored previously with the same timestamp.
@@ -28,11 +28,11 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// <remarks>
/// The nonce must be stored for no less than the maximum time window a message may
/// be processed within before being discarded as an expired message.
- /// This maximum message age can be looked up via the
+ /// This maximum message age can be looked up via the
/// <see cref="DotNetOpenAuth.Configuration.MessagingElement.MaximumMessageLifetime"/>
/// property, accessible via the <see cref="DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration"/>
/// property.
/// </remarks>
- bool StoreNonce(string nonce, DateTime timestamp);
+ bool StoreNonce(string context, string nonce, DateTime timestamp);
}
}
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/IReplayProtectedProtocolMessage.cs b/src/DotNetOpenAuth/Messaging/Bindings/IReplayProtectedProtocolMessage.cs
index 02f3ee3..1edf934 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/IReplayProtectedProtocolMessage.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/IReplayProtectedProtocolMessage.cs
@@ -15,7 +15,18 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// All replay-protected messages must also be set to expire so the nonces do not have
/// to be stored indefinitely.
/// </remarks>
- internal interface IReplayProtectedProtocolMessage : IExpiringProtocolMessage {
+ internal interface IReplayProtectedProtocolMessage : IExpiringProtocolMessage, IDirectedProtocolMessage {
+ /// <summary>
+ /// Gets the context within which the nonce must be unique.
+ /// </summary>
+ /// <value>
+ /// The value of this property must be a value assigned by the nonce consumer
+ /// to represent the entity that generated the nonce. The value must never be
+ /// <c>null</c> but may be the empty string.
+ /// This value is treated as case-sensitive.
+ /// </value>
+ string NonceContext { get; }
+
/// <summary>
/// Gets or sets the nonce that will protect the message from replay attacks.
/// </summary>
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs b/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs
index 37414cc..1d4d28e 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs
@@ -57,13 +57,10 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// <summary>
/// Stores a given nonce and timestamp.
/// </summary>
- /// <param name="nonce">
- /// A series of random characters.
- /// </param>
- /// <param name="timestamp">
- /// The timestamp that together with the nonce string make it unique.
- /// The timestamp may also be used by the data store to clear out old nonces.
- /// </param>
+ /// <param name="context">The context, or namespace, within which the <paramref name="nonce"/> must be unique.</param>
+ /// <param name="nonce">A series of random characters.</param>
+ /// <param name="timestamp">The timestamp that together with the nonce string make it unique.
+ /// The timestamp may also be used by the data store to clear out old nonces.</param>
/// <returns>
/// True if the nonce+timestamp (combination) was not previously in the database.
/// False if the nonce was stored previously with the same timestamp.
@@ -72,27 +69,30 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// The nonce must be stored for no less than the maximum time window a message may
/// be processed within before being discarded as an expired message.
/// If the binding element is applicable to your channel, this expiration window
- /// is retrieved or set using the
+ /// is retrieved or set using the
/// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property.
/// </remarks>
- public bool StoreNonce(string nonce, DateTime timestamp) {
+ public bool StoreNonce(string context, string nonce, DateTime timestamp) {
if (timestamp.ToUniversalTime() + this.maximumMessageAge < DateTime.UtcNow) {
// The expiration binding element should have taken care of this, but perhaps
// it's at the boundary case. We should fail just to be safe.
return false;
}
+ // We just concatenate the context with the nonce to form a complete, namespace-protected nonce.
+ string completeNonce = context + "\0" + nonce;
+
lock (this.nonceLock) {
List<string> nonces;
if (!this.usedNonces.TryGetValue(timestamp, out nonces)) {
this.usedNonces[timestamp] = nonces = new List<string>(4);
}
- if (nonces.Contains(nonce)) {
+ if (nonces.Contains(completeNonce)) {
return false;
}
- nonces.Add(nonce);
+ nonces.Add(completeNonce);
// Clear expired nonces if it's time to take a moment to do that.
// Unchecked so that this can int overflow without an exception.
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
index bdba009..c72c29b 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
@@ -128,7 +128,7 @@ namespace DotNetOpenAuth.Messaging.Bindings {
if (nonceMessage != null && nonceMessage.Nonce != null) {
ErrorUtilities.VerifyProtocol(nonceMessage.Nonce.Length > 0 || this.AllowZeroLengthNonce, MessagingStrings.InvalidNonceReceived);
- if (!this.nonceStore.StoreNonce(nonceMessage.Nonce, nonceMessage.UtcCreationDate)) {
+ if (!this.nonceStore.StoreNonce(nonceMessage.NonceContext, nonceMessage.Nonce, nonceMessage.UtcCreationDate)) {
throw new ReplayedMessageException(message);
}
diff --git a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
index cbe8b8a..d1abb58 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
@@ -115,6 +115,14 @@ namespace DotNetOpenAuth.OAuth.Messages {
#region IReplayProtectedProtocolMessage Members
/// <summary>
+ /// Gets the context within which the nonce must be unique.
+ /// </summary>
+ /// <value>The consumer key.</value>
+ string IReplayProtectedProtocolMessage.NonceContext {
+ get { return this.ConsumerKey; }
+ }
+
+ /// <summary>
/// Gets or sets the message nonce used for replay detection.
/// </summary>
[MessagePart("oauth_nonce", IsRequired = true)]
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
index 9f89518..54635ff 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
@@ -163,7 +163,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
throw new ExpiredMessageException(expirationDate, message);
}
- if (!this.nonceStore.StoreNonce(nonce.RandomPartAsString, nonce.CreationDateUtc)) {
+ IReplayProtectedProtocolMessage replayResponse = response;
+ if (!this.nonceStore.StoreNonce(replayResponse.NonceContext, nonce.RandomPartAsString, nonce.CreationDateUtc)) {
throw new ReplayedMessageException(message);
}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
index 2bb6be2..7e9f054 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
@@ -171,6 +171,22 @@ namespace DotNetOpenAuth.OpenId.Messages {
string IReplayProtectedProtocolMessage.Nonce { get; set; }
/// <summary>
+ /// Gets the context within which the nonce must be unique.
+ /// </summary>
+ string IReplayProtectedProtocolMessage.NonceContext {
+ get {
+ if (this.ProviderEndpoint != null) {
+ return this.ProviderEndpoint.AbsoluteUri;
+ } else {
+ // This is the Provider, on an OpenID 1.x check_authentication message.
+ // We don't need any special nonce context because the Provider
+ // generated and consumed the nonce.
+ return string.Empty;
+ }
+ }
+ }
+
+ /// <summary>
/// Gets or sets the UTC date/time the message was originally sent onto the network.
/// </summary>
/// <remarks>
diff --git a/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs b/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs
index a1afe11..7085e72 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs
@@ -120,6 +120,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// Stores a given nonce and timestamp.
/// </summary>
+ /// <param name="context">The context, or namespace, within which the <paramref name="nonce"/> must be unique.</param>
/// <param name="nonce">A series of random characters.</param>
/// <param name="timestamp">The timestamp that together with the nonce string make it unique.
/// The timestamp may also be used by the data store to clear out old nonces.</param>
@@ -134,8 +135,8 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// is retrieved or set using the
/// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property.
/// </remarks>
- public bool StoreNonce(string nonce, DateTime timestamp) {
- return this.nonceStore.StoreNonce(nonce, timestamp);
+ public bool StoreNonce(string context, string nonce, DateTime timestamp) {
+ return this.nonceStore.StoreNonce(context, nonce, timestamp);
}
#endregion
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
index 5fa3617..96dd8d8 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
@@ -104,6 +104,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Stores a given nonce and timestamp.
/// </summary>
+ /// <param name="context">The context, or namespace, within which the <paramref name="nonce"/> must be unique.</param>
/// <param name="nonce">A series of random characters.</param>
/// <param name="timestamp">The timestamp that together with the nonce string make it unique.
/// The timestamp may also be used by the data store to clear out old nonces.</param>
@@ -118,8 +119,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// is retrieved or set using the
/// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property.
/// </remarks>
- public bool StoreNonce(string nonce, DateTime timestamp) {
- return this.nonceStore.StoreNonce(nonce, timestamp);
+ public bool StoreNonce(string context, string nonce, DateTime timestamp) {
+ return this.nonceStore.StoreNonce(context, nonce, timestamp);
}
#endregion