namespace OAuthAuthorizationServer.Code { using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using DotNetOpenAuth.Messaging.Bindings; /// /// A database-persisted nonce store. /// public class DatabaseKeyNonceStore : INonceStore, ICryptoKeyStore { /// /// Initializes a new instance of the class. /// public DatabaseKeyNonceStore() { } #region INonceStore Members /// /// Stores a given nonce and timestamp. /// /// The context, or namespace, within which the /// must be unique. /// The context SHOULD be treated as case-sensitive. /// The value will never be null but may be the empty string. /// A series of random characters. /// The UTC timestamp that together with the nonce string make it unique /// within the given . /// The timestamp may also be used by the data store to clear out old nonces. /// /// 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. /// /// /// 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 /// /// property, accessible via the /// property. /// public bool StoreNonce(string context, string nonce, DateTime timestampUtc) { MvcApplication.DataContext.Nonces.InsertOnSubmit(new Nonce { Context = context, Code = nonce, Timestamp = timestampUtc }); try { MvcApplication.DataContext.SubmitChanges(); return true; } catch (System.Data.Linq.DuplicateKeyException) { return false; } catch (SqlException) { return false; } } #endregion #region ICryptoKeyStore Members public CryptoKey GetKey(string bucket, string handle) { // It is critical that this lookup be case-sensitive, which can only be configured at the database. var matches = from key in MvcApplication.DataContext.SymmetricCryptoKeys where key.Bucket == bucket && key.Handle == handle select new CryptoKey(key.Secret, key.ExpiresUtc.AsUtc()); return matches.FirstOrDefault(); } public IEnumerable> GetKeys(string bucket) { return from key in MvcApplication.DataContext.SymmetricCryptoKeys where key.Bucket == bucket orderby key.ExpiresUtc descending select new KeyValuePair(key.Handle, new CryptoKey(key.Secret, key.ExpiresUtc.AsUtc())); } public void StoreKey(string bucket, string handle, CryptoKey key) { var keyRow = new SymmetricCryptoKey() { Bucket = bucket, Handle = handle, Secret = key.Key, ExpiresUtc = key.ExpiresUtc, }; MvcApplication.DataContext.SymmetricCryptoKeys.InsertOnSubmit(keyRow); MvcApplication.DataContext.SubmitChanges(); } public void RemoveKey(string bucket, string handle) { var match = MvcApplication.DataContext.SymmetricCryptoKeys.FirstOrDefault(k => k.Bucket == bucket && k.Handle == handle); if (match != null) { MvcApplication.DataContext.SymmetricCryptoKeys.DeleteOnSubmit(match); } } #endregion } }