diff options
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 |