diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-11-18 09:53:32 -0800 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-11-18 11:43:53 -0800 |
commit | 1dc7805c0cea4f94760250a597fc7a980f4c377b (patch) | |
tree | 28d392725f0235eb53c9ab2f09c598cb70c10947 | |
parent | 4728b81b32252207b20abd21b3b2f8e969b63b61 (diff) | |
download | DotNetOpenAuth-1dc7805c0cea4f94760250a597fc7a980f4c377b.zip DotNetOpenAuth-1dc7805c0cea4f94760250a597fc7a980f4c377b.tar.gz DotNetOpenAuth-1dc7805c0cea4f94760250a597fc7a980f4c377b.tar.bz2 |
Added custom database store for nonces and associations and wired it up to the OAuth SP and OpenID RP in the project template.
10 files changed, 899 insertions, 11 deletions
diff --git a/projecttemplates/RelyingPartyLogic/CreateDatabase.sql b/projecttemplates/RelyingPartyLogic/CreateDatabase.sql index 3e36000..dd42724 100644 --- a/projecttemplates/RelyingPartyLogic/CreateDatabase.sql +++ b/projecttemplates/RelyingPartyLogic/CreateDatabase.sql @@ -2,6 +2,85 @@ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO +CREATE PROCEDURE [dbo].[ClearExpiredNonces] +AS + +DELETE FROM dbo.[Nonce] +WHERE [Expires] < getutcdate() +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +CREATE PROCEDURE [dbo].[ClearExpiredAssociations] +AS + +DELETE FROM dbo.OpenIDAssociation +WHERE [Expiration] < getutcdate() +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +SET ANSI_PADDING ON +GO +CREATE TABLE [dbo].[Nonce]( + [NonceId] [int] IDENTITY(1,1) NOT NULL, + [Context] [varchar](255) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, + [Code] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, + [Issued] [datetime] NOT NULL, + [Expires] [datetime] NOT NULL, + CONSTRAINT [PK_Nonce] PRIMARY KEY CLUSTERED +( + [NonceId] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] +GO +SET ANSI_PADDING OFF +GO +CREATE UNIQUE NONCLUSTERED INDEX [IX_Nonce_Code] ON [dbo].[Nonce] +( + [Context] ASC, + [Code] ASC, + [Issued] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +GO +CREATE NONCLUSTERED INDEX [IX_Nonce_Expires] ON [dbo].[Nonce] +( + [Expires] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +SET ANSI_PADDING ON +GO +CREATE TABLE [dbo].[OpenIDAssociation]( + [AssociationId] [int] IDENTITY(1,1) NOT NULL, + [DistinguishingFactor] [varchar](255) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, + [AssociationHandle] [varchar](255) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, + [Expiration] [datetime] NOT NULL, + [PrivateData] [binary](64) NOT NULL, + [PrivateDataLength] [int] NOT NULL, + CONSTRAINT [PK_OpenIDAssociations] PRIMARY KEY CLUSTERED +( + [AssociationId] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] +GO +SET ANSI_PADDING OFF +GO +CREATE UNIQUE NONCLUSTERED INDEX [IX_OpenIDAssociations] ON [dbo].[OpenIDAssociation] +( + [DistinguishingFactor] ASC, + [AssociationHandle] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO CREATE TABLE [dbo].[Consumer]( [ConsumerId] [int] IDENTITY(1,1) NOT NULL, [ConsumerKey] [nvarchar](255) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, @@ -158,17 +237,19 @@ AS RETURN @userid GO +ALTER TABLE [dbo].[Nonce] ADD CONSTRAINT [DF_Nonce_Issued] DEFAULT (getutcdate()) FOR [Issued] +GO ALTER TABLE [dbo].[User] ADD CONSTRAINT [DF_User_EmailAddressVerified] DEFAULT ((0)) FOR [EmailAddressVerified] GO -ALTER TABLE [dbo].[User] ADD CONSTRAINT [DF_User_CreatedOn] DEFAULT (getdate()) FOR [CreatedOn] +ALTER TABLE [dbo].[User] ADD CONSTRAINT [DF_User_CreatedOn] DEFAULT (getutcdate()) FOR [CreatedOn] GO -ALTER TABLE [dbo].[IssuedToken] ADD CONSTRAINT [DF_IssuedToken_CreatedOn] DEFAULT (getdate()) FOR [CreatedOn] +ALTER TABLE [dbo].[IssuedToken] ADD CONSTRAINT [DF_IssuedToken_CreatedOn] DEFAULT (getutcdate()) FOR [CreatedOn] GO ALTER TABLE [dbo].[IssuedToken] ADD CONSTRAINT [DF_IssuedToken_IsAccessToken] DEFAULT ((0)) FOR [IsAccessToken] GO -ALTER TABLE [dbo].[AuthenticationToken] ADD CONSTRAINT [DF_AuthenticationToken_CreatedOn] DEFAULT (getdate()) FOR [CreatedOn] +ALTER TABLE [dbo].[AuthenticationToken] ADD CONSTRAINT [DF_AuthenticationToken_CreatedOn] DEFAULT (getutcdate()) FOR [CreatedOn] GO -ALTER TABLE [dbo].[AuthenticationToken] ADD CONSTRAINT [DF_AuthenticationToken_LastUsed] DEFAULT (getdate()) FOR [LastUsed] +ALTER TABLE [dbo].[AuthenticationToken] ADD CONSTRAINT [DF_AuthenticationToken_LastUsed] DEFAULT (getutcdate()) FOR [LastUsed] GO ALTER TABLE [dbo].[AuthenticationToken] ADD CONSTRAINT [DF_AuthenticationToken_UsageCount] DEFAULT ((0)) FOR [UsageCount] GO diff --git a/projecttemplates/RelyingPartyLogic/Model.Designer.cs b/projecttemplates/RelyingPartyLogic/Model.Designer.cs index af86171..dba46ed 100644 --- a/projecttemplates/RelyingPartyLogic/Model.Designer.cs +++ b/projecttemplates/RelyingPartyLogic/Model.Designer.cs @@ -15,7 +15,7 @@ [assembly: global::System.Data.Objects.DataClasses.EdmRelationshipAttribute("DatabaseModel", "FK_IssuedToken_User1", "User", global::System.Data.Metadata.Edm.RelationshipMultiplicity.ZeroOrOne, typeof(RelyingPartyLogic.User), "IssuedToken", global::System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(RelyingPartyLogic.IssuedToken))] // Original file name: -// Generation date: 11/16/2009 8:23:18 PM +// Generation date: 11/18/2009 9:09:23 AM namespace RelyingPartyLogic { @@ -125,6 +125,36 @@ namespace RelyingPartyLogic } private global::System.Data.Objects.ObjectQuery<IssuedToken> _IssuedToken; /// <summary> + /// There are no comments for Nonces in the schema. + /// </summary> + public global::System.Data.Objects.ObjectQuery<Nonce> Nonces + { + get + { + if ((this._Nonces == null)) + { + this._Nonces = base.CreateQuery<Nonce>("[Nonces]"); + } + return this._Nonces; + } + } + private global::System.Data.Objects.ObjectQuery<Nonce> _Nonces; + /// <summary> + /// There are no comments for OpenIdAssociations in the schema. + /// </summary> + public global::System.Data.Objects.ObjectQuery<OpenIdAssociation> OpenIdAssociations + { + get + { + if ((this._OpenIdAssociations == null)) + { + this._OpenIdAssociations = base.CreateQuery<OpenIdAssociation>("[OpenIdAssociations]"); + } + return this._OpenIdAssociations; + } + } + private global::System.Data.Objects.ObjectQuery<OpenIdAssociation> _OpenIdAssociations; + /// <summary> /// There are no comments for Role in the schema. /// </summary> public void AddToRole(Role role) @@ -159,6 +189,20 @@ namespace RelyingPartyLogic { base.AddObject("IssuedToken", issuedToken); } + /// <summary> + /// There are no comments for Nonces in the schema. + /// </summary> + public void AddToNonces(Nonce nonce) + { + base.AddObject("Nonces", nonce); + } + /// <summary> + /// There are no comments for OpenIdAssociations in the schema. + /// </summary> + public void AddToOpenIdAssociations(OpenIdAssociation openIdAssociation) + { + base.AddObject("OpenIdAssociations", openIdAssociation); + } } /// <summary> /// There are no comments for DatabaseModel.AuthenticationToken in the schema. @@ -1265,4 +1309,319 @@ namespace RelyingPartyLogic partial void OnExpirationDateUtcChanging(global::System.Nullable<global::System.DateTime> value); partial void OnExpirationDateUtcChanged(); } + /// <summary> + /// There are no comments for DatabaseModel.Nonce in the schema. + /// </summary> + /// <KeyProperties> + /// NonceId + /// </KeyProperties> + [global::System.Data.Objects.DataClasses.EdmEntityTypeAttribute(NamespaceName="DatabaseModel", Name="Nonce")] + [global::System.Runtime.Serialization.DataContractAttribute(IsReference=true)] + [global::System.Serializable()] + public partial class Nonce : global::System.Data.Objects.DataClasses.EntityObject + { + /// <summary> + /// Create a new Nonce object. + /// </summary> + /// <param name="nonceId">Initial value of NonceId.</param> + /// <param name="context">Initial value of Context.</param> + /// <param name="code">Initial value of Code.</param> + /// <param name="issuedUtc">Initial value of IssuedUtc.</param> + /// <param name="expiresUtc">Initial value of ExpiresUtc.</param> + public static Nonce CreateNonce(int nonceId, string context, string code, global::System.DateTime issuedUtc, global::System.DateTime expiresUtc) + { + Nonce nonce = new Nonce(); + nonce.NonceId = nonceId; + nonce.Context = context; + nonce.Code = code; + nonce.IssuedUtc = issuedUtc; + nonce.ExpiresUtc = expiresUtc; + return nonce; + } + /// <summary> + /// There are no comments for Property NonceId in the schema. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public int NonceId + { + get + { + return this._NonceId; + } + set + { + this.OnNonceIdChanging(value); + this.ReportPropertyChanging("NonceId"); + this._NonceId = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value); + this.ReportPropertyChanged("NonceId"); + this.OnNonceIdChanged(); + } + } + private int _NonceId; + partial void OnNonceIdChanging(int value); + partial void OnNonceIdChanged(); + /// <summary> + /// Gets or sets the Provider Endpoint URL the nonce came from. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(IsNullable=false)] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public string Context + { + get + { + return this._Context; + } + set + { + this.OnContextChanging(value); + this.ReportPropertyChanging("Context"); + this._Context = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, false); + this.ReportPropertyChanged("Context"); + this.OnContextChanged(); + } + } + private string _Context; + partial void OnContextChanging(string value); + partial void OnContextChanged(); + /// <summary> + /// There are no comments for Property Code in the schema. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(IsNullable=false)] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public string Code + { + get + { + return this._Code; + } + set + { + this.OnCodeChanging(value); + this.ReportPropertyChanging("Code"); + this._Code = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, false); + this.ReportPropertyChanged("Code"); + this.OnCodeChanged(); + } + } + private string _Code; + partial void OnCodeChanging(string value); + partial void OnCodeChanged(); + /// <summary> + /// There are no comments for Property IssuedUtc in the schema. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(IsNullable=false)] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public global::System.DateTime IssuedUtc + { + get + { + return this._IssuedUtc; + } + set + { + this.OnIssuedUtcChanging(value); + this.ReportPropertyChanging("IssuedUtc"); + this._IssuedUtc = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value); + this.ReportPropertyChanged("IssuedUtc"); + this.OnIssuedUtcChanged(); + } + } + private global::System.DateTime _IssuedUtc; + partial void OnIssuedUtcChanging(global::System.DateTime value); + partial void OnIssuedUtcChanged(); + /// <summary> + /// There are no comments for Property ExpiresUtc in the schema. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(IsNullable=false)] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public global::System.DateTime ExpiresUtc + { + get + { + return this._ExpiresUtc; + } + set + { + this.OnExpiresUtcChanging(value); + this.ReportPropertyChanging("ExpiresUtc"); + this._ExpiresUtc = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value); + this.ReportPropertyChanged("ExpiresUtc"); + this.OnExpiresUtcChanged(); + } + } + private global::System.DateTime _ExpiresUtc; + partial void OnExpiresUtcChanging(global::System.DateTime value); + partial void OnExpiresUtcChanged(); + } + /// <summary> + /// There are no comments for DatabaseModel.OpenIdAssociation in the schema. + /// </summary> + /// <KeyProperties> + /// AssociationId + /// </KeyProperties> + [global::System.Data.Objects.DataClasses.EdmEntityTypeAttribute(NamespaceName="DatabaseModel", Name="OpenIdAssociation")] + [global::System.Runtime.Serialization.DataContractAttribute(IsReference=true)] + [global::System.Serializable()] + public partial class OpenIdAssociation : global::System.Data.Objects.DataClasses.EntityObject + { + /// <summary> + /// Create a new OpenIdAssociation object. + /// </summary> + /// <param name="associationId">Initial value of AssociationId.</param> + /// <param name="distinguishingFactor">Initial value of DistinguishingFactor.</param> + /// <param name="associationHandle">Initial value of AssociationHandle.</param> + /// <param name="expirationUtc">Initial value of ExpirationUtc.</param> + /// <param name="privateData">Initial value of PrivateData.</param> + /// <param name="privateDataLength">Initial value of PrivateDataLength.</param> + public static OpenIdAssociation CreateOpenIdAssociation(int associationId, string distinguishingFactor, string associationHandle, global::System.DateTime expirationUtc, byte[] privateData, int privateDataLength) + { + OpenIdAssociation openIdAssociation = new OpenIdAssociation(); + openIdAssociation.AssociationId = associationId; + openIdAssociation.DistinguishingFactor = distinguishingFactor; + openIdAssociation.AssociationHandle = associationHandle; + openIdAssociation.ExpirationUtc = expirationUtc; + openIdAssociation.PrivateData = privateData; + openIdAssociation.PrivateDataLength = privateDataLength; + return openIdAssociation; + } + /// <summary> + /// There are no comments for Property AssociationId in the schema. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public int AssociationId + { + get + { + return this._AssociationId; + } + set + { + this.OnAssociationIdChanging(value); + this.ReportPropertyChanging("AssociationId"); + this._AssociationId = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value); + this.ReportPropertyChanged("AssociationId"); + this.OnAssociationIdChanged(); + } + } + private int _AssociationId; + partial void OnAssociationIdChanging(int value); + partial void OnAssociationIdChanged(); + /// <summary> + /// Gets or sets the Provider Endpoint URL the association is with. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(IsNullable=false)] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public string DistinguishingFactor + { + get + { + return this._DistinguishingFactor; + } + set + { + this.OnDistinguishingFactorChanging(value); + this.ReportPropertyChanging("DistinguishingFactor"); + this._DistinguishingFactor = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, false); + this.ReportPropertyChanged("DistinguishingFactor"); + this.OnDistinguishingFactorChanged(); + } + } + private string _DistinguishingFactor; + partial void OnDistinguishingFactorChanging(string value); + partial void OnDistinguishingFactorChanged(); + /// <summary> + /// There are no comments for Property AssociationHandle in the schema. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(IsNullable=false)] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public string AssociationHandle + { + get + { + return this._AssociationHandle; + } + set + { + this.OnAssociationHandleChanging(value); + this.ReportPropertyChanging("AssociationHandle"); + this._AssociationHandle = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, false); + this.ReportPropertyChanged("AssociationHandle"); + this.OnAssociationHandleChanged(); + } + } + private string _AssociationHandle; + partial void OnAssociationHandleChanging(string value); + partial void OnAssociationHandleChanged(); + /// <summary> + /// There are no comments for Property ExpirationUtc in the schema. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(IsNullable=false)] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public global::System.DateTime ExpirationUtc + { + get + { + return this._ExpirationUtc; + } + set + { + this.OnExpirationUtcChanging(value); + this.ReportPropertyChanging("ExpirationUtc"); + this._ExpirationUtc = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value); + this.ReportPropertyChanged("ExpirationUtc"); + this.OnExpirationUtcChanged(); + } + } + private global::System.DateTime _ExpirationUtc; + partial void OnExpirationUtcChanging(global::System.DateTime value); + partial void OnExpirationUtcChanged(); + /// <summary> + /// There are no comments for Property PrivateData in the schema. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(IsNullable=false)] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public byte[] PrivateData + { + get + { + return global::System.Data.Objects.DataClasses.StructuralObject.GetValidValue(this._PrivateData); + } + set + { + this.OnPrivateDataChanging(value); + this.ReportPropertyChanging("PrivateData"); + this._PrivateData = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, false); + this.ReportPropertyChanged("PrivateData"); + this.OnPrivateDataChanged(); + } + } + private byte[] _PrivateData; + partial void OnPrivateDataChanging(byte[] value); + partial void OnPrivateDataChanged(); + /// <summary> + /// There are no comments for Property PrivateDataLength in the schema. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(IsNullable=false)] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public int PrivateDataLength + { + get + { + return this._PrivateDataLength; + } + set + { + this.OnPrivateDataLengthChanging(value); + this.ReportPropertyChanging("PrivateDataLength"); + this._PrivateDataLength = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value); + this.ReportPropertyChanged("PrivateDataLength"); + this.OnPrivateDataLengthChanged(); + } + } + private int _PrivateDataLength; + partial void OnPrivateDataLengthChanging(int value); + partial void OnPrivateDataLengthChanged(); + } } diff --git a/projecttemplates/RelyingPartyLogic/Model.OpenIdAssociation.cs b/projecttemplates/RelyingPartyLogic/Model.OpenIdAssociation.cs new file mode 100644 index 0000000..94ab09a --- /dev/null +++ b/projecttemplates/RelyingPartyLogic/Model.OpenIdAssociation.cs @@ -0,0 +1,18 @@ +//----------------------------------------------------------------------- +// <copyright file="Model.OpenIdAssociation.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace RelyingPartyLogic { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + public partial class OpenIdAssociation { + partial void OnPrivateDataChanged() { + this.PrivateDataLength = this.PrivateData.Length; + } + } +} diff --git a/projecttemplates/RelyingPartyLogic/Model.cs b/projecttemplates/RelyingPartyLogic/Model.cs new file mode 100644 index 0000000..10c1518 --- /dev/null +++ b/projecttemplates/RelyingPartyLogic/Model.cs @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------- +// <copyright file="Model.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace RelyingPartyLogic { + using System; + using System.Collections.Generic; + using System.Data; + using System.Data.Common; + using System.Data.EntityClient; + using System.Data.Objects; + using System.Linq; + using System.Text; + + public partial class DatabaseEntities { + /// <summary> + /// Clears the expired nonces. + /// </summary> + internal void ClearExpiredNonces() { + this.ExecuteCommand("ClearExpiredNonces"); + } + + /// <summary> + /// Clears the expired associations. + /// </summary> + internal void ClearExpiredAssociations() { + this.ExecuteCommand("ClearExpiredAssociations"); + } + } +} diff --git a/projecttemplates/RelyingPartyLogic/Model.edmx b/projecttemplates/RelyingPartyLogic/Model.edmx index 21fa98a..76fd57a 100644 --- a/projecttemplates/RelyingPartyLogic/Model.edmx +++ b/projecttemplates/RelyingPartyLogic/Model.edmx @@ -9,6 +9,8 @@ <EntitySet Name="AuthenticationToken" EntityType="DatabaseModel.Store.AuthenticationToken" store:Type="Tables" Schema="dbo" /> <EntitySet Name="Consumer" EntityType="DatabaseModel.Store.Consumer" store:Type="Tables" Schema="dbo" /> <EntitySet Name="IssuedToken" EntityType="DatabaseModel.Store.IssuedToken" store:Type="Tables" Schema="dbo" /> + <EntitySet Name="Nonce" EntityType="DatabaseModel.Store.Nonce" store:Type="Tables" Schema="dbo" /> + <EntitySet Name="OpenIDAssociation" EntityType="DatabaseModel.Store.OpenIDAssociation" store:Type="Tables" Schema="dbo" /> <EntitySet Name="Role" EntityType="DatabaseModel.Store.Role" store:Type="Tables" Schema="dbo" /> <EntitySet Name="User" EntityType="DatabaseModel.Store.User" store:Type="Tables" Schema="dbo" /> <EntitySet Name="UserRole" EntityType="DatabaseModel.Store.UserRole" store:Type="Tables" Schema="dbo" /> @@ -75,6 +77,27 @@ <Property Name="IsAccessToken" Type="bit" Nullable="false" /> <Property Name="Scope" Type="nvarchar" MaxLength="255" /> </EntityType> + <EntityType Name="Nonce"> + <Key> + <PropertyRef Name="NonceId" /> + </Key> + <Property Name="NonceId" Type="int" Nullable="false" StoreGeneratedPattern="Identity" /> + <Property Name="Context" Type="varchar" Nullable="false" MaxLength="255" /> + <Property Name="Code" Type="varchar" Nullable="false" MaxLength="255" /> + <Property Name="Issued" Type="datetime" Nullable="false" /> + <Property Name="Expires" Type="datetime" Nullable="false" /> + </EntityType> + <EntityType Name="OpenIDAssociation"> + <Key> + <PropertyRef Name="AssociationId" /> + </Key> + <Property Name="AssociationId" Type="int" Nullable="false" StoreGeneratedPattern="Identity" /> + <Property Name="DistinguishingFactor" Type="varchar" Nullable="false" MaxLength="255" /> + <Property Name="AssociationHandle" Type="varchar" Nullable="false" MaxLength="255" /> + <Property Name="Expiration" Type="datetime" Nullable="false" /> + <Property Name="PrivateData" Type="binary" Nullable="false" MaxLength="32" /> + <Property Name="PrivateDataLength" Type="int" Nullable="false" /> + </EntityType> <EntityType Name="Role"> <Key> <PropertyRef Name="RoleId" /> @@ -171,6 +194,8 @@ </Dependent> </ReferentialConstraint> </Association> + <Function Name="ClearExpiredAssociations" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo" /> + <Function Name="ClearExpiredNonces" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo" /> </Schema></edmx:StorageModels> <!-- CSDL content --> <edmx:ConceptualModels> @@ -193,7 +218,10 @@ <End Role="IssuedToken" EntitySet="IssuedToken" /></AssociationSet> <AssociationSet Name="FK_IssuedToken_User1" Association="DatabaseModel.FK_IssuedToken_User1"> <End Role="User" EntitySet="User" /> - <End Role="IssuedToken" EntitySet="IssuedToken" /></AssociationSet></EntityContainer> + <End Role="IssuedToken" EntitySet="IssuedToken" /></AssociationSet> + <EntitySet Name="Nonces" EntityType="DatabaseModel.Nonce" /> + <EntitySet Name="OpenIdAssociations" EntityType="DatabaseModel.OpenIdAssociation" /> + <FunctionImport Name="ClearExpiredNonces" /></EntityContainer> <EntityType Name="AuthenticationToken" Abstract="false"> <Key> <PropertyRef Name="AuthenticationTokenId" /></Key> @@ -266,7 +294,28 @@ <End Type="DatabaseModel.IssuedToken" Role="IssuedToken" Multiplicity="*" /></Association> <Association Name="FK_IssuedToken_User1"> <End Type="DatabaseModel.User" Role="User" Multiplicity="0..1" /> - <End Type="DatabaseModel.IssuedToken" Role="IssuedToken" Multiplicity="*" /></Association></Schema> + <End Type="DatabaseModel.IssuedToken" Role="IssuedToken" Multiplicity="*" /></Association> + <EntityType Name="Nonce" a:TypeAccess="Public" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration"> + <Key> + <PropertyRef Name="NonceId" /></Key> + <Property Name="NonceId" Type="Int32" Nullable="false" /> + <Property Name="Context" Type="String" Nullable="false" > + <Documentation> + <Summary>Gets or sets the Provider Endpoint URL the nonce came from.</Summary></Documentation></Property> + <Property Name="Code" Type="String" Nullable="false" /> + <Property Name="IssuedUtc" Type="DateTime" Nullable="false" /> + <Property Name="ExpiresUtc" Type="DateTime" Nullable="false" /></EntityType> + <EntityType Name="OpenIdAssociation" a:TypeAccess="Public" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration"> + <Key> + <PropertyRef Name="AssociationId" /></Key> + <Property Name="AssociationId" Type="Int32" Nullable="false" /> + <Property Name="DistinguishingFactor" Type="String" Nullable="false" > + <Documentation> + <Summary>Gets or sets the Provider Endpoint URL the association is with.</Summary></Documentation></Property> + <Property Name="AssociationHandle" Type="String" Nullable="false" /> + <Property Name="ExpirationUtc" Type="DateTime" Nullable="false" /> + <Property Name="PrivateData" Type="Binary" Nullable="false" /> + <Property Name="PrivateDataLength" Type="Int32" Nullable="false" /></EntityType></Schema> </edmx:ConceptualModels> <!-- C-S mapping content --> <edmx:Mappings> @@ -356,7 +405,25 @@ <ScalarProperty Name="IssuedTokenId" ColumnName="IssuedTokenId" /></EndProperty> <EndProperty Name="User"> <ScalarProperty Name="UserId" ColumnName="UserId" /></EndProperty> - <Condition ColumnName="UserId" IsNull="false" /></AssociationSetMapping></EntityContainerMapping> + <Condition ColumnName="UserId" IsNull="false" /></AssociationSetMapping> + <EntitySetMapping Name="Nonces"> + <EntityTypeMapping TypeName="IsTypeOf(DatabaseModel.Nonce)"> + <MappingFragment StoreEntitySet="Nonce"> + <ScalarProperty Name="ExpiresUtc" ColumnName="Expires" /> + <ScalarProperty Name="IssuedUtc" ColumnName="Issued" /> + <ScalarProperty Name="Code" ColumnName="Code" /> + <ScalarProperty Name="Context" ColumnName="Context" /> + <ScalarProperty Name="NonceId" ColumnName="NonceId" /></MappingFragment></EntityTypeMapping></EntitySetMapping> + <EntitySetMapping Name="OpenIdAssociations"> + <EntityTypeMapping TypeName="IsTypeOf(DatabaseModel.OpenIdAssociation)"> + <MappingFragment StoreEntitySet="OpenIDAssociation"> + <ScalarProperty Name="PrivateDataLength" ColumnName="PrivateDataLength" /> + <ScalarProperty Name="PrivateData" ColumnName="PrivateData" /> + <ScalarProperty Name="ExpirationUtc" ColumnName="Expiration" /> + <ScalarProperty Name="AssociationHandle" ColumnName="AssociationHandle" /> + <ScalarProperty Name="DistinguishingFactor" ColumnName="DistinguishingFactor" /> + <ScalarProperty Name="AssociationId" ColumnName="AssociationId" /></MappingFragment></EntityTypeMapping></EntitySetMapping> + <FunctionImportMapping FunctionImportName="ClearExpiredNonces" FunctionName="DatabaseModel.Store.ClearExpiredNonces" /></EntityContainerMapping> </Mapping> </edmx:Mappings> </edmx:Runtime> @@ -374,7 +441,7 @@ </edmx:Options> <!-- Diagram content (shape and connector positions) --> <edmx:Diagrams> - <Diagram Name="Model" ZoomLevel="86"> + <Diagram Name="Model" ZoomLevel="98"> <EntityTypeShape EntityType="DatabaseModel.AuthenticationToken" Width="1.875" PointX="5.25" PointY="0.75" Height="2.5571907552083339" IsExpanded="true" /> <EntityTypeShape EntityType="DatabaseModel.Role" Width="1.5" PointX="0.75" PointY="1.25" Height="1.59568359375" IsExpanded="true" /> <EntityTypeShape EntityType="DatabaseModel.User" Width="1.75" PointX="2.875" PointY="0.5" Height="3.1340950520833339" IsExpanded="true" /> @@ -406,6 +473,8 @@ <AssociationConnector Association="DatabaseModel.FK_IssuedToken_User1" > <ConnectorPoint PointX="3.75" PointY="3.6340950520833339" /> <ConnectorPoint PointX="3.75" PointY="4.0627779870647478" /> - <ConnectorPoint PointX="5.25" PointY="4.0627779870647478" /></AssociationConnector></Diagram></edmx:Diagrams> + <ConnectorPoint PointX="5.25" PointY="4.0627779870647478" /></AssociationConnector> + <EntityTypeShape EntityType="DatabaseModel.Nonce" Width="1.5" PointX="0.5" PointY="7.75" Height="1.9802864583333326" /> + <EntityTypeShape EntityType="DatabaseModel.OpenIdAssociation" Width="1.75" PointX="2.25" PointY="7.75" Height="1.9802864583333333" /></Diagram></edmx:Diagrams> </edmx:Designer> </edmx:Edmx>
\ No newline at end of file diff --git a/projecttemplates/RelyingPartyLogic/NonceDbStore.cs b/projecttemplates/RelyingPartyLogic/NonceDbStore.cs new file mode 100644 index 0000000..2f3c670 --- /dev/null +++ b/projecttemplates/RelyingPartyLogic/NonceDbStore.cs @@ -0,0 +1,132 @@ +//----------------------------------------------------------------------- +// <copyright file="NonceDbStore.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace RelyingPartyLogic { + using System; + using System.Collections.Generic; + using System.Data; + using System.Data.Common; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Configuration; + using DotNetOpenAuth.Messaging.Bindings; + + /// <summary> + /// A database-backed nonce store for OpenID and OAuth services. + /// </summary> + public class NonceDbStore : INonceStore { + private const int NonceClearingInterval = 5; + + /// <summary> + /// A counter that tracks how many nonce stores have been done. + /// </summary> + private static int nonceClearingCounter; + + /// <summary> + /// Initializes a new instance of the <see cref="NonceDbStore"/> class. + /// </summary> + public NonceDbStore() { + } + + #region INonceStore Members + + /// <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="timestampUtc">The UTC timestamp that together with the nonce string make it unique + /// within the given <paramref name="context"/>. + /// The timestamp may also be used by the data store to clear out old nonces.</param> + /// <returns> + /// True if the context+nonce+timestamp (combination) was not previously in the database. + /// False if the nonce was stored previously with the same timestamp and context. + /// </returns> + /// <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 + /// <see cref="DotNetOpenAuth.Configuration.MessagingElement.MaximumMessageLifetime"/> + /// property, accessible via the <see cref="DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration"/> + /// property. + /// </remarks> + public bool StoreNonce(string context, string nonce, DateTime timestampUtc) { + try { + using (var dataContext = new TransactedDatabaseEntities(IsolationLevel.ReadCommitted)) { + Nonce nonceEntity = new Nonce { + Context = context, + Code = nonce, + IssuedUtc = timestampUtc, + ExpiresUtc = timestampUtc + DotNetOpenAuthSection.Configuration.Messaging.MaximumMessageLifetime, + }; + + // The database columns [context] and [code] MUST be using + // a case sensitive collation for this to be secure. + dataContext.AddToNonces(nonceEntity); + } + } catch (UpdateException) { + // A nonce collision + return false; + } + + // Only clear nonces after successfully storing a nonce. + // This mitigates cheap DoS attacks that take up a lot of + // database cycles. + ClearNoncesIfAppropriate(); + return true; + } + + #endregion + + /// <summary> + /// Clears the nonces if appropriate. + /// </summary> + private static void ClearNoncesIfAppropriate() { + if (++nonceClearingCounter % NonceClearingInterval == 0) { + using (var dataContext = new TransactedDatabaseEntities(IsolationLevel.ReadCommitted)) { + dataContext.ClearExpiredNonces(); + } + } + } + + /// <summary> + /// A transacted data context. + /// </summary> + protected class TransactedDatabaseEntities : DatabaseEntities { + /// <summary> + /// The transaction for this data context. + /// </summary> + private DbTransaction transaction; + + /// <summary> + /// Initializes a new instance of the <see cref="TransactedDatabaseEntities"/> class. + /// </summary> + /// <param name="isolationLevel">The isolation level.</param> + public TransactedDatabaseEntities(IsolationLevel isolationLevel) { + this.Connection.Open(); + this.transaction = this.Connection.BeginTransaction(isolationLevel); + } + + /// <summary> + /// Releases the resources used by the object context. + /// </summary> + /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> + protected override void Dispose(bool disposing) { + try { + this.SaveChanges(); + this.transaction.Commit(); + } finally { + this.Connection.Close(); + } + + base.Dispose(disposing); + } + } + } +} diff --git a/projecttemplates/RelyingPartyLogic/RelyingPartyApplicationDbStore.cs b/projecttemplates/RelyingPartyLogic/RelyingPartyApplicationDbStore.cs new file mode 100644 index 0000000..9a2f8bb --- /dev/null +++ b/projecttemplates/RelyingPartyLogic/RelyingPartyApplicationDbStore.cs @@ -0,0 +1,158 @@ +//----------------------------------------------------------------------- +// <copyright file="RelyingPartyApplicationDbStore.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace RelyingPartyLogic { + using System; + using System.Collections.Generic; + using System.Data; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.RelyingParty; + + /// <summary> + /// A database-backed state store for OpenID relying parties. + /// </summary> + public class RelyingPartyApplicationDbStore : NonceDbStore, IRelyingPartyApplicationStore { + /// <summary> + /// Initializes a new instance of the <see cref="RelyingPartyApplicationDbStore"/> class. + /// </summary> + public RelyingPartyApplicationDbStore() { + } + + #region IAssociationStore<Uri> Members + + /// <summary> + /// Saves an <see cref="Association"/> for later recall. + /// </summary> + /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for providers).</param> + /// <param name="association">The association to store.</param> + /// <remarks> + /// TODO: what should implementations do on association handle conflict? + /// </remarks> + public void StoreAssociation(Uri distinguishingFactor, Association association) { + using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { + var sharedAssociation = new OpenIdAssociation { + DistinguishingFactor = distinguishingFactor.AbsoluteUri, + AssociationHandle = association.Handle, + ExpirationUtc = association.Expires, + PrivateData = association.SerializePrivateData(), + }; + + dataContext.AddToOpenIdAssociations(sharedAssociation); + } + } + + /// <summary> + /// Gets the best association (the one with the longest remaining life) for a given key. + /// </summary> + /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for Providers).</param> + /// <param name="securityRequirements">The security requirements that the returned association must meet.</param> + /// <returns> + /// The requested association, or null if no unexpired <see cref="Association"/>s exist for the given key. + /// </returns> + /// <remarks> + /// In the event that multiple associations exist for the given + /// <paramref name="distinguishingFactor"/>, it is important for the + /// implementation for this method to use the <paramref name="securityRequirements"/> + /// to pick the best (highest grade or longest living as the host's policy may dictate) + /// association that fits the security requirements. + /// Associations that are returned that do not meet the security requirements will be + /// ignored and a new association created. + /// </remarks> + public Association GetAssociation(Uri distinguishingFactor, SecuritySettings securityRequirements) { + using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { + var relevantAssociations = from assoc in dataContext.OpenIdAssociations + where assoc.DistinguishingFactor == distinguishingFactor.AbsoluteUri + where assoc.ExpirationUtc > DateTime.UtcNow + where assoc.PrivateDataLength * 8 >= securityRequirements.MinimumHashBitLength + where assoc.PrivateDataLength * 8 <= securityRequirements.MaximumHashBitLength + orderby assoc.ExpirationUtc descending + select assoc; + var qualifyingAssociations = relevantAssociations.AsEnumerable() + .Select(assoc => DeserializeAssociation(assoc)); + return qualifyingAssociations.FirstOrDefault(); + } + } + + /// <summary> + /// Gets the association for a given key and handle. + /// </summary> + /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for Providers).</param> + /// <param name="handle">The handle of the specific association that must be recalled.</param> + /// <returns> + /// The requested association, or null if no unexpired <see cref="Association"/>s exist for the given key and handle. + /// </returns> + public Association GetAssociation(Uri distinguishingFactor, string handle) { + using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { + var associations = from assoc in dataContext.OpenIdAssociations + where assoc.DistinguishingFactor == distinguishingFactor.AbsoluteUri + where assoc.AssociationHandle == handle + where assoc.ExpirationUtc > DateTime.UtcNow + select assoc; + return associations.AsEnumerable() + .Select(assoc => DeserializeAssociation(assoc)) + .FirstOrDefault(); + } + } + + /// <summary> + /// Removes a specified handle that may exist in the store. + /// </summary> + /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for Providers).</param> + /// <param name="handle">The handle of the specific association that must be deleted.</param> + /// <returns> + /// True if the association existed in this store previous to this call. + /// </returns> + /// <remarks> + /// No exception should be thrown if the association does not exist in the store + /// before this call. + /// </remarks> + public bool RemoveAssociation(Uri distinguishingFactor, string handle) { + using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { + var association = dataContext.OpenIdAssociations.FirstOrDefault(a => a.DistinguishingFactor == distinguishingFactor.AbsoluteUri && a.AssociationHandle == handle); + if (association != null) { + dataContext.DeleteObject(association); + return true; + } else { + return false; + } + } + } + + /// <summary> + /// Clears all expired associations from the store. + /// </summary> + /// <remarks> + /// If another algorithm is in place to periodically clear out expired associations, + /// this method call may be ignored. + /// This should be done frequently enough to avoid a memory leak, but sparingly enough + /// to not be a performance drain. + /// </remarks> + public void ClearExpiredAssociations() { + using (var dataContext = new TransactedDatabaseEntities(IsolationLevel.ReadCommitted)) { + dataContext.ClearExpiredAssociations(); + } + } + + #endregion + + /// <summary> + /// Deserializes an association from the database. + /// </summary> + /// <param name="association">The association from the database.</param> + /// <returns>The deserialized association.</returns> + private static Association DeserializeAssociation(OpenIdAssociation association) { + if (association == null) { + throw new ArgumentNullException("association"); + } + + byte[] privateData = new byte[association.PrivateDataLength]; + Array.Copy(association.PrivateData, privateData, association.PrivateDataLength); + return Association.Deserialize(association.AssociationHandle, association.ExpirationUtc, privateData); + } + } +} diff --git a/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj b/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj index 1e7e759..9eba352 100644 --- a/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj +++ b/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj @@ -82,6 +82,7 @@ <Reference Include="System.Web.Mobile" /> </ItemGroup> <ItemGroup> + <Compile Include="Model.cs" /> <Compile Include="Model.IssuedToken.cs" /> <Compile Include="Database.cs" /> <Compile Include="DataRoleProvider.cs" /> @@ -94,7 +95,9 @@ </Compile> <Compile Include="Model.IssuedAccessToken.cs" /> <Compile Include="Model.IssuedRequestToken.cs" /> + <Compile Include="Model.OpenIdAssociation.cs" /> <Compile Include="Model.User.cs" /> + <Compile Include="NonceDbStore.cs" /> <Compile Include="OAuthAuthenticationModule.cs" /> <Compile Include="OAuthAuthorizationManager.cs" /> <Compile Include="OAuthConsumerTokenManager.cs" /> @@ -104,6 +107,7 @@ <Compile Include="OAuthTokenManager.cs" /> <Compile Include="Policies.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="RelyingPartyApplicationDbStore.cs" /> <Compile Include="Utilities.cs" /> </ItemGroup> <ItemGroup> diff --git a/projecttemplates/RelyingPartyLogic/Utilities.cs b/projecttemplates/RelyingPartyLogic/Utilities.cs index affbe26..5c9ae52 100644 --- a/projecttemplates/RelyingPartyLogic/Utilities.cs +++ b/projecttemplates/RelyingPartyLogic/Utilities.cs @@ -7,6 +7,10 @@ namespace RelyingPartyLogic { using System; using System.Collections.Generic; + using System.Data; + using System.Data.Common; + using System.Data.EntityClient; + using System.Data.Objects; using System.Globalization; using System.IO; using System.Linq; @@ -17,7 +21,7 @@ namespace RelyingPartyLogic { using Microsoft.SqlServer.Management.Common; using Microsoft.SqlServer.Management.Smo; - public class Utilities { + public static class Utilities { internal const string DefaultNamespace = "RelyingPartyLogic"; /// <summary> @@ -61,6 +65,31 @@ GO } } + /// <summary> + /// Executes a SQL command against the SQL connection. + /// </summary> + /// <param name="objectContext">The object context.</param> + /// <param name="command">The command to execute.</param> + /// <returns>The result of executing the command.</returns> + public static int ExecuteCommand(this ObjectContext objectContext, string command) { + DbConnection connection = ((EntityConnection)objectContext.Connection).StoreConnection; + bool opening = (connection.State == ConnectionState.Closed); + if (opening) { + connection.Open(); + } + + DbCommand cmd = connection.CreateCommand(); + cmd.CommandText = command; + cmd.CommandType = CommandType.StoredProcedure; + try { + return cmd.ExecuteNonQuery(); + } finally { + if (opening && connection.State == ConnectionState.Open) { + connection.Close(); + } + } + } + internal static void VerifyThrowNotLocalTime(DateTime value) { // When we want UTC time, we have to accept Unspecified kind // because that's how it is set to us in the database. diff --git a/projecttemplates/WebFormsRelyingParty/Web.config b/projecttemplates/WebFormsRelyingParty/Web.config index 0861893..901626b 100644 --- a/projecttemplates/WebFormsRelyingParty/Web.config +++ b/projecttemplates/WebFormsRelyingParty/Web.config @@ -48,8 +48,14 @@ with OPs that use Attribute Exchange (in various formats). --> <add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" /> </behaviors> + <store type="RelyingPartyLogic.RelyingPartyApplicationDbStore, RelyingPartyLogic"/> </relyingParty> </openid> + <oauth> + <serviceProvider> + <store type="RelyingPartyLogic.NonceDbStore, RelyingPartyLogic"/> + </serviceProvider> + </oauth> </dotNetOpenAuth> <!-- log4net is a 3rd party (free) logger library that dotnetopenid will use if present but does not require. --> <log4net> |