diff options
Diffstat (limited to 'samples/OAuthAuthorizationServer/Code')
-rw-r--r-- | samples/OAuthAuthorizationServer/Code/DataClasses.dbml | 8 | ||||
-rw-r--r-- | samples/OAuthAuthorizationServer/Code/DataClasses.dbml.layout | 6 | ||||
-rw-r--r-- | samples/OAuthAuthorizationServer/Code/DataClasses.designer.cs | 147 | ||||
-rw-r--r-- | samples/OAuthAuthorizationServer/Code/DatabaseKeyNonceStore.cs (renamed from samples/OAuthAuthorizationServer/Code/DatabaseNonceStore.cs) | 47 | ||||
-rw-r--r-- | samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs | 115 | ||||
-rw-r--r-- | samples/OAuthAuthorizationServer/Code/Utilities.cs | 21 |
6 files changed, 276 insertions, 68 deletions
diff --git a/samples/OAuthAuthorizationServer/Code/DataClasses.dbml b/samples/OAuthAuthorizationServer/Code/DataClasses.dbml index 33e6eda..0ef987d 100644 --- a/samples/OAuthAuthorizationServer/Code/DataClasses.dbml +++ b/samples/OAuthAuthorizationServer/Code/DataClasses.dbml @@ -37,4 +37,12 @@ <Column Name="Timestamp" Type="System.DateTime" IsPrimaryKey="true" CanBeNull="false" /> </Type> </Table> + <Table Name="" Member="SymmetricCryptoKeys"> + <Type Name="SymmetricCryptoKey"> + <Column Name="Bucket" Type="System.String" IsPrimaryKey="true" CanBeNull="false" UpdateCheck="Never" /> + <Column Name="Handle" Type="System.String" IsPrimaryKey="true" CanBeNull="false" UpdateCheck="Never" /> + <Column Name="ExpiresUtc" Type="System.DateTime" CanBeNull="false" UpdateCheck="Never" /> + <Column Name="Secret" Type="System.Byte[]" CanBeNull="false" UpdateCheck="Never" /> + </Type> + </Table> </Database>
\ No newline at end of file diff --git a/samples/OAuthAuthorizationServer/Code/DataClasses.dbml.layout b/samples/OAuthAuthorizationServer/Code/DataClasses.dbml.layout index e2982ce..f4de725 100644 --- a/samples/OAuthAuthorizationServer/Code/DataClasses.dbml.layout +++ b/samples/OAuthAuthorizationServer/Code/DataClasses.dbml.layout @@ -40,5 +40,11 @@ <classShapeMoniker Id="895ebbc8-8352-4c04-9e53-b8e6c8302d36" /> </nodes> </associationConnector> + <classShape Id="93df6fa9-cc66-44a9-8885-960b1e670dd7" absoluteBounds="4.125, 6.25, 2, 1.5785953776041666"> + <DataClassMoniker Name="/DataClassesDataContext/SymmetricCryptoKey" /> + <nestedChildShapes> + <elementListCompartment Id="0b486eb8-31a4-4f11-b58f-09540c56319b" absoluteBounds="4.14, 6.71, 1.9700000000000002, 1.0185953776041665" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" /> + </nestedChildShapes> + </classShape> </nestedChildShapes> </ordesignerObjectsDiagram>
\ No newline at end of file diff --git a/samples/OAuthAuthorizationServer/Code/DataClasses.designer.cs b/samples/OAuthAuthorizationServer/Code/DataClasses.designer.cs index b6d070d..c8d1b19 100644 --- a/samples/OAuthAuthorizationServer/Code/DataClasses.designer.cs +++ b/samples/OAuthAuthorizationServer/Code/DataClasses.designer.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:4.0.30319.1 +// Runtime Version:4.0.30319.225 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -42,6 +42,9 @@ namespace OAuthAuthorizationServer.Code partial void InsertNonce(Nonce instance); partial void UpdateNonce(Nonce instance); partial void DeleteNonce(Nonce instance); + partial void InsertSymmetricCryptoKey(SymmetricCryptoKey instance); + partial void UpdateSymmetricCryptoKey(SymmetricCryptoKey instance); + partial void DeleteSymmetricCryptoKey(SymmetricCryptoKey instance); #endregion public DataClassesDataContext() : @@ -105,6 +108,14 @@ namespace OAuthAuthorizationServer.Code return this.GetTable<Nonce>(); } } + + public System.Data.Linq.Table<SymmetricCryptoKey> SymmetricCryptoKeys + { + get + { + return this.GetTable<SymmetricCryptoKey>(); + } + } } [global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.[User]")] @@ -804,5 +815,139 @@ namespace OAuthAuthorizationServer.Code } } } + + [global::System.Data.Linq.Mapping.TableAttribute(Name="")] + public partial class SymmetricCryptoKey : INotifyPropertyChanging, INotifyPropertyChanged + { + + private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty); + + private string _Bucket; + + private string _Handle; + + private System.DateTime _ExpiresUtc; + + private byte[] _Secret; + + #region Extensibility Method Definitions + partial void OnLoaded(); + partial void OnValidate(System.Data.Linq.ChangeAction action); + partial void OnCreated(); + partial void OnBucketChanging(string value); + partial void OnBucketChanged(); + partial void OnHandleChanging(string value); + partial void OnHandleChanged(); + partial void OnExpiresUtcChanging(System.DateTime value); + partial void OnExpiresUtcChanged(); + partial void OnSecretChanging(byte[] value); + partial void OnSecretChanged(); + #endregion + + public SymmetricCryptoKey() + { + OnCreated(); + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Bucket", CanBeNull=false, IsPrimaryKey=true, UpdateCheck=UpdateCheck.Never)] + public string Bucket + { + get + { + return this._Bucket; + } + set + { + if ((this._Bucket != value)) + { + this.OnBucketChanging(value); + this.SendPropertyChanging(); + this._Bucket = value; + this.SendPropertyChanged("Bucket"); + this.OnBucketChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Handle", CanBeNull=false, IsPrimaryKey=true, UpdateCheck=UpdateCheck.Never)] + public string Handle + { + get + { + return this._Handle; + } + set + { + if ((this._Handle != value)) + { + this.OnHandleChanging(value); + this.SendPropertyChanging(); + this._Handle = value; + this.SendPropertyChanged("Handle"); + this.OnHandleChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ExpiresUtc", UpdateCheck=UpdateCheck.Never)] + public System.DateTime ExpiresUtc + { + get + { + return this._ExpiresUtc; + } + set + { + if ((this._ExpiresUtc != value)) + { + this.OnExpiresUtcChanging(value); + this.SendPropertyChanging(); + this._ExpiresUtc = value; + this.SendPropertyChanged("ExpiresUtc"); + this.OnExpiresUtcChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Secret", CanBeNull=false, UpdateCheck=UpdateCheck.Never)] + public byte[] Secret + { + get + { + return this._Secret; + } + set + { + if ((this._Secret != value)) + { + this.OnSecretChanging(value); + this.SendPropertyChanging(); + this._Secret = value; + this.SendPropertyChanged("Secret"); + this.OnSecretChanged(); + } + } + } + + public event PropertyChangingEventHandler PropertyChanging; + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void SendPropertyChanging() + { + if ((this.PropertyChanging != null)) + { + this.PropertyChanging(this, emptyChangingEventArgs); + } + } + + protected virtual void SendPropertyChanged(String propertyName) + { + if ((this.PropertyChanged != null)) + { + this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + } } #pragma warning restore 1591 diff --git a/samples/OAuthAuthorizationServer/Code/DatabaseNonceStore.cs b/samples/OAuthAuthorizationServer/Code/DatabaseKeyNonceStore.cs index a0ce19e..0a1d8af 100644 --- a/samples/OAuthAuthorizationServer/Code/DatabaseNonceStore.cs +++ b/samples/OAuthAuthorizationServer/Code/DatabaseKeyNonceStore.cs @@ -1,16 +1,18 @@ namespace OAuthAuthorizationServer.Code { using System; + using System.Collections.Generic; using System.Data.SqlClient; + using System.Linq; using DotNetOpenAuth.Messaging.Bindings; /// <summary> /// A database-persisted nonce store. /// </summary> - public class DatabaseNonceStore : INonceStore { + public class DatabaseKeyNonceStore : INonceStore, ICryptoKeyStore { /// <summary> - /// Initializes a new instance of the <see cref="DatabaseNonceStore"/> class. + /// Initializes a new instance of the <see cref="DatabaseKeyNonceStore"/> class. /// </summary> - public DatabaseNonceStore() { + public DatabaseKeyNonceStore() { } #region INonceStore Members @@ -51,5 +53,44 @@ } #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<KeyValuePair<string, CryptoKey>> GetKeys(string bucket) { + return from key in MvcApplication.DataContext.SymmetricCryptoKeys + where key.Bucket == bucket + orderby key.ExpiresUtc descending + select new KeyValuePair<string, CryptoKey>(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 } }
\ No newline at end of file diff --git a/samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs b/samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs index 7e4dba6..d2583a2 100644 --- a/samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs +++ b/samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs @@ -13,68 +13,14 @@ internal class OAuth2AuthorizationServer : IAuthorizationServer { private static readonly RSAParameters AsymmetricTokenSigningPrivateKey = CreateRSAKey(); - private static readonly byte[] secret = CreateSecret(); - - private readonly INonceStore nonceStore = new DatabaseNonceStore(); - - /// <summary> - /// Creates a symmetric secret used to sign and encrypt authorization server refresh tokens. - /// </summary> - /// <returns>A cryptographically strong symmetric key.</returns> - private static byte[] CreateSecret() { - // TODO: Replace this sample code with real code. - // For this sample, we just generate random secrets. - RandomNumberGenerator crypto = new RNGCryptoServiceProvider(); - var secret = new byte[16]; - crypto.GetBytes(secret); - return secret; - } - - /// <summary> - /// Creates the RSA key used by all the crypto service provider instances we create. - /// </summary> - /// <returns>RSA data that includes the private key.</returns> - private static RSAParameters CreateRSAKey() { -#if SAMPLESONLY - // Since the sample authorization server and the sample resource server must work together, - // we hard-code a FOR SAMPLE USE ONLY key pair. The matching public key information is hard-coded into the OAuthResourceServer sample. - // In a real app, the RSA parameters would typically come from a certificate that may already exist. It may simply be the HTTPS certificate for the auth server. - return new RSAParameters { - Exponent = new byte[] { 1, 0, 1 }, - Modulus = new byte[] { 210, 95, 53, 12, 203, 114, 150, 23, 23, 88, 4, 200, 47, 219, 73, 54, 146, 253, 126, 121, 105, 91, 118, 217, 182, 167, 140, 6, 67, 112, 97, 183, 66, 112, 245, 103, 136, 222, 205, 28, 196, 45, 6, 223, 192, 76, 56, 180, 90, 120, 144, 19, 31, 193, 37, 129, 186, 214, 36, 53, 204, 53, 108, 133, 112, 17, 133, 244, 3, 12, 230, 29, 243, 51, 79, 253, 10, 111, 185, 23, 74, 230, 99, 94, 78, 49, 209, 39, 95, 213, 248, 212, 22, 4, 222, 145, 77, 190, 136, 230, 134, 70, 228, 241, 194, 216, 163, 234, 52, 1, 64, 181, 139, 128, 90, 255, 214, 60, 168, 233, 254, 110, 31, 102, 58, 67, 201, 33 }, - P = new byte[] { 237, 238, 79, 75, 29, 57, 145, 201, 57, 177, 215, 108, 40, 77, 232, 237, 113, 38, 157, 195, 174, 134, 188, 175, 121, 28, 11, 236, 80, 146, 12, 38, 8, 12, 104, 46, 6, 247, 14, 149, 196, 23, 130, 116, 141, 137, 225, 74, 84, 111, 44, 163, 55, 10, 246, 154, 195, 158, 186, 241, 162, 11, 217, 77 }, - Q = new byte[] { 226, 89, 29, 67, 178, 205, 30, 152, 184, 165, 15, 152, 131, 245, 141, 80, 150, 3, 224, 136, 188, 248, 149, 36, 200, 250, 207, 156, 224, 79, 150, 191, 84, 214, 233, 173, 95, 192, 55, 123, 124, 255, 53, 85, 11, 233, 156, 66, 14, 27, 27, 163, 108, 199, 90, 37, 118, 38, 78, 171, 80, 26, 101, 37 }, - DP = new byte[] { 108, 176, 122, 132, 131, 187, 50, 191, 203, 157, 84, 29, 82, 100, 20, 205, 178, 236, 195, 17, 10, 254, 253, 222, 226, 226, 79, 8, 10, 222, 76, 178, 106, 230, 208, 8, 134, 162, 1, 133, 164, 232, 96, 109, 193, 226, 132, 138, 33, 252, 15, 86, 23, 228, 232, 54, 86, 186, 130, 7, 179, 208, 217, 217 }, - DQ = new byte[] { 175, 63, 252, 46, 140, 99, 208, 138, 194, 123, 218, 101, 101, 214, 91, 65, 199, 196, 220, 182, 66, 73, 221, 128, 11, 180, 85, 198, 202, 206, 20, 147, 179, 102, 106, 170, 247, 245, 229, 127, 81, 58, 111, 218, 151, 76, 154, 213, 114, 2, 127, 21, 187, 133, 102, 64, 151, 7, 245, 229, 34, 50, 45, 153 }, - InverseQ = new byte[] { 137, 156, 11, 248, 118, 201, 135, 145, 134, 121, 14, 162, 149, 14, 98, 84, 108, 160, 27, 91, 230, 116, 216, 181, 200, 49, 34, 254, 119, 153, 179, 52, 231, 234, 36, 148, 71, 161, 182, 171, 35, 182, 46, 164, 179, 100, 226, 71, 119, 23, 0, 16, 240, 4, 30, 57, 76, 109, 89, 131, 56, 219, 71, 206 }, - D = new byte[] { 108, 15, 123, 176, 150, 208, 197, 72, 23, 53, 159, 63, 53, 85, 238, 197, 153, 187, 156, 187, 192, 226, 186, 170, 26, 168, 245, 196, 65, 223, 248, 81, 170, 79, 91, 191, 83, 15, 31, 77, 39, 119, 249, 143, 245, 183, 49, 105, 115, 15, 122, 242, 87, 221, 94, 230, 196, 146, 59, 7, 103, 94, 9, 223, 146, 180, 189, 86, 190, 94, 242, 59, 32, 54, 23, 181, 124, 170, 63, 172, 90, 158, 169, 140, 6, 102, 170, 0, 135, 199, 35, 196, 212, 238, 196, 56, 14, 0, 140, 197, 169, 240, 156, 43, 182, 123, 102, 79, 89, 20, 120, 171, 43, 223, 58, 190, 230, 166, 185, 162, 186, 226, 31, 206, 196, 188, 104, 1 }, - }; -#else - // This is how you could generate your own public/private key pair. - // As we generate a new random key, we need to set the UseMachineKeyStore flag so that this doesn't - // crash on IIS. For more information: - // http://social.msdn.microsoft.com/Forums/en-US/clr/thread/7ea48fd0-8d6b-43ed-b272-1a0249ae490f?prof=required - var cspParameters = new CspParameters(); - cspParameters.Flags = CspProviderFlags.UseArchivableKey | CspProviderFlags.UseMachineKeyStore; - var keyPair = new RSACryptoServiceProvider(cspParameters); - - // After exporting the private/public key information, read the information out and store it somewhere - var privateKey = keyPair.ExportParameters(true); - var publicKey = keyPair.ExportParameters(false); - - // Ultimately the private key information must be what is returned through the AccessTokenSigningPrivateKey property. - return privateKey; -#endif - } - #region Implementation of IAuthorizationServer - public byte[] Secret { - get { return secret; } + public ICryptoKeyStore CryptoKeyStore { + get { return MvcApplication.KeyNonceStore; } } public INonceStore VerificationCodeNonceStore { - get { return this.nonceStore; } + get { return MvcApplication.KeyNonceStore; } } public RSACryptoServiceProvider CreateAccessTokenSigningCryptoServiceProvider() { @@ -107,7 +53,7 @@ // NEVER issue an auto-approval to a client that would end up getting an access token immediately // (without a client secret), as that would allow ANY client to spoof an approved client's identity // and obtain unauthorized access to user data. - if (authorizationRequest.ResponseType == EndUserAuthorizationResponseType.AuthorizationCode) { + if (EndUserAuthorizationRequest.ResponseType == EndUserAuthorizationResponseTypes.AuthorizationCode) { // Never issue auto-approval if the client secret is blank, since that too makes it easy to spoof // a client's identity and obtain unauthorized access. var requestingClient = MvcApplication.DataContext.Clients.First(c => c.ClientIdentifier == authorizationRequest.ClientIdentifier); @@ -124,14 +70,55 @@ return false; } + /// <summary> + /// Creates the RSA key used by all the crypto service provider instances we create. + /// </summary> + /// <returns>RSA data that includes the private key.</returns> + private static RSAParameters CreateRSAKey() { +#if SAMPLESONLY + // Since the sample authorization server and the sample resource server must work together, + // we hard-code a FOR SAMPLE USE ONLY key pair. The matching public key information is hard-coded into the OAuthResourceServer sample. + // In a real app, the RSA parameters would typically come from a certificate that may already exist. It may simply be the HTTPS certificate for the auth server. + return new RSAParameters { + Exponent = new byte[] { 1, 0, 1 }, + Modulus = new byte[] { 210, 95, 53, 12, 203, 114, 150, 23, 23, 88, 4, 200, 47, 219, 73, 54, 146, 253, 126, 121, 105, 91, 118, 217, 182, 167, 140, 6, 67, 112, 97, 183, 66, 112, 245, 103, 136, 222, 205, 28, 196, 45, 6, 223, 192, 76, 56, 180, 90, 120, 144, 19, 31, 193, 37, 129, 186, 214, 36, 53, 204, 53, 108, 133, 112, 17, 133, 244, 3, 12, 230, 29, 243, 51, 79, 253, 10, 111, 185, 23, 74, 230, 99, 94, 78, 49, 209, 39, 95, 213, 248, 212, 22, 4, 222, 145, 77, 190, 136, 230, 134, 70, 228, 241, 194, 216, 163, 234, 52, 1, 64, 181, 139, 128, 90, 255, 214, 60, 168, 233, 254, 110, 31, 102, 58, 67, 201, 33 }, + P = new byte[] { 237, 238, 79, 75, 29, 57, 145, 201, 57, 177, 215, 108, 40, 77, 232, 237, 113, 38, 157, 195, 174, 134, 188, 175, 121, 28, 11, 236, 80, 146, 12, 38, 8, 12, 104, 46, 6, 247, 14, 149, 196, 23, 130, 116, 141, 137, 225, 74, 84, 111, 44, 163, 55, 10, 246, 154, 195, 158, 186, 241, 162, 11, 217, 77 }, + Q = new byte[] { 226, 89, 29, 67, 178, 205, 30, 152, 184, 165, 15, 152, 131, 245, 141, 80, 150, 3, 224, 136, 188, 248, 149, 36, 200, 250, 207, 156, 224, 79, 150, 191, 84, 214, 233, 173, 95, 192, 55, 123, 124, 255, 53, 85, 11, 233, 156, 66, 14, 27, 27, 163, 108, 199, 90, 37, 118, 38, 78, 171, 80, 26, 101, 37 }, + DP = new byte[] { 108, 176, 122, 132, 131, 187, 50, 191, 203, 157, 84, 29, 82, 100, 20, 205, 178, 236, 195, 17, 10, 254, 253, 222, 226, 226, 79, 8, 10, 222, 76, 178, 106, 230, 208, 8, 134, 162, 1, 133, 164, 232, 96, 109, 193, 226, 132, 138, 33, 252, 15, 86, 23, 228, 232, 54, 86, 186, 130, 7, 179, 208, 217, 217 }, + DQ = new byte[] { 175, 63, 252, 46, 140, 99, 208, 138, 194, 123, 218, 101, 101, 214, 91, 65, 199, 196, 220, 182, 66, 73, 221, 128, 11, 180, 85, 198, 202, 206, 20, 147, 179, 102, 106, 170, 247, 245, 229, 127, 81, 58, 111, 218, 151, 76, 154, 213, 114, 2, 127, 21, 187, 133, 102, 64, 151, 7, 245, 229, 34, 50, 45, 153 }, + InverseQ = new byte[] { 137, 156, 11, 248, 118, 201, 135, 145, 134, 121, 14, 162, 149, 14, 98, 84, 108, 160, 27, 91, 230, 116, 216, 181, 200, 49, 34, 254, 119, 153, 179, 52, 231, 234, 36, 148, 71, 161, 182, 171, 35, 182, 46, 164, 179, 100, 226, 71, 119, 23, 0, 16, 240, 4, 30, 57, 76, 109, 89, 131, 56, 219, 71, 206 }, + D = new byte[] { 108, 15, 123, 176, 150, 208, 197, 72, 23, 53, 159, 63, 53, 85, 238, 197, 153, 187, 156, 187, 192, 226, 186, 170, 26, 168, 245, 196, 65, 223, 248, 81, 170, 79, 91, 191, 83, 15, 31, 77, 39, 119, 249, 143, 245, 183, 49, 105, 115, 15, 122, 242, 87, 221, 94, 230, 196, 146, 59, 7, 103, 94, 9, 223, 146, 180, 189, 86, 190, 94, 242, 59, 32, 54, 23, 181, 124, 170, 63, 172, 90, 158, 169, 140, 6, 102, 170, 0, 135, 199, 35, 196, 212, 238, 196, 56, 14, 0, 140, 197, 169, 240, 156, 43, 182, 123, 102, 79, 89, 20, 120, 171, 43, 223, 58, 190, 230, 166, 185, 162, 186, 226, 31, 206, 196, 188, 104, 1 }, + }; +#else + // This is how you could generate your own public/private key pair. + // As we generate a new random key, we need to set the UseMachineKeyStore flag so that this doesn't + // crash on IIS. For more information: + // http://social.msdn.microsoft.com/Forums/en-US/clr/thread/7ea48fd0-8d6b-43ed-b272-1a0249ae490f?prof=required + var cspParameters = new CspParameters(); + cspParameters.Flags = CspProviderFlags.UseArchivableKey | CspProviderFlags.UseMachineKeyStore; + var keyPair = new RSACryptoServiceProvider(cspParameters); + + // After exporting the private/public key information, read the information out and store it somewhere + var privateKey = keyPair.ExportParameters(true); + var publicKey = keyPair.ExportParameters(false); + + // Ultimately the private key information must be what is returned through the AccessTokenSigningPrivateKey property. + return privateKey; +#endif + } + private bool IsAuthorizationValid(HashSet<string> requestedScopes, string clientIdentifier, DateTime issuedUtc, string username) { + // If db precision exceeds token time precision (which is common), the following query would + // often disregard a token that is minted immediately after the authorization record is stored in the db. + // To compensate for this, we'll increase the timestamp on the token's issue date by 1 second. + issuedUtc += TimeSpan.FromSeconds(1); var grantedScopeStrings = from auth in MvcApplication.DataContext.ClientAuthorizations - where - auth.Client.ClientIdentifier == clientIdentifier && - auth.CreatedOnUtc <= issuedUtc && - (!auth.ExpirationDateUtc.HasValue || auth.ExpirationDateUtc.Value >= DateTime.UtcNow) && - auth.User.OpenIDClaimedIdentifier == username - select auth.Scope; + where + auth.Client.ClientIdentifier == clientIdentifier && + auth.CreatedOnUtc <= issuedUtc && + (!auth.ExpirationDateUtc.HasValue || auth.ExpirationDateUtc.Value >= DateTime.UtcNow) && + auth.User.OpenIDClaimedIdentifier == username + select auth.Scope; if (!grantedScopeStrings.Any()) { // No granted authorizations prior to the issuance of this token, so it must have been revoked. diff --git a/samples/OAuthAuthorizationServer/Code/Utilities.cs b/samples/OAuthAuthorizationServer/Code/Utilities.cs new file mode 100644 index 0000000..c9109bd --- /dev/null +++ b/samples/OAuthAuthorizationServer/Code/Utilities.cs @@ -0,0 +1,21 @@ +namespace OAuthAuthorizationServer.Code { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web; + + internal static class Utilities { + /// <summary> + /// Ensures that local times are converted to UTC times. Unspecified kinds are recast to UTC with no conversion. + /// </summary> + /// <param name="value">The date-time to convert.</param> + /// <returns>The date-time in UTC time.</returns> + internal static DateTime AsUtc(this DateTime value) { + if (value.Kind == DateTimeKind.Unspecified) { + return new DateTime(value.Ticks, DateTimeKind.Utc); + } + + return value.ToUniversalTime(); + } + } +}
\ No newline at end of file |