summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--samples/OAuthServiceProvider/App_Code/DataClasses.dbml6
-rw-r--r--samples/OAuthServiceProvider/App_Code/OAuthToken.cs4
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs6
-rw-r--r--src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs21
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs5
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs46
-rw-r--r--src/DotNetOpenAuth/OAuth/ServiceProvider.cs1
-rw-r--r--src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs2
8 files changed, 84 insertions, 7 deletions
diff --git a/samples/OAuthServiceProvider/App_Code/DataClasses.dbml b/samples/OAuthServiceProvider/App_Code/DataClasses.dbml
index 78518c2..651de9f 100644
--- a/samples/OAuthServiceProvider/App_Code/DataClasses.dbml
+++ b/samples/OAuthServiceProvider/App_Code/DataClasses.dbml
@@ -25,9 +25,9 @@
<Column Name="ConsumerId" Type="System.Int32" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" />
<Column Name="ConsumerKey" Type="System.String" DbType="NVarChar(50) NOT NULL" CanBeNull="false" />
<Column Name="ConsumerSecret" Type="System.String" DbType="NVarChar(50) NOT NULL" CanBeNull="false" />
- <Column Member="Callback" Type="System.String" CanBeNull="true" />
- <Column Member="VerificationCodeFormat" Type="DotNetOpenAuth.OAuth.VerificationCodeFormat" CanBeNull="false" />
- <Column Member="VerificationCodeLength" Type="System.Int32" CanBeNull="false" />
+ <Column Name="Callback" Type="System.String" CanBeNull="true" />
+ <Column Name="VerificationCodeFormat" Type="DotNetOpenAuth.OAuth.VerificationCodeFormat" CanBeNull="false" />
+ <Column Name="VerificationCodeLength" Type="System.Int32" CanBeNull="false" />
<Association Name="OAuthConsumer_OAuthToken" Member="OAuthTokens" ThisKey="ConsumerId" OtherKey="ConsumerId" Type="OAuthToken" />
</Type>
</Table>
diff --git a/samples/OAuthServiceProvider/App_Code/OAuthToken.cs b/samples/OAuthServiceProvider/App_Code/OAuthToken.cs
index 445e88c..ec9b31e 100644
--- a/samples/OAuthServiceProvider/App_Code/OAuthToken.cs
+++ b/samples/OAuthServiceProvider/App_Code/OAuthToken.cs
@@ -21,6 +21,10 @@ public partial class OAuthToken : IServiceProviderRequestToken {
get { return this.OAuthConsumer.ConsumerKey; }
}
+ DateTime IServiceProviderRequestToken.CreatedOn {
+ get { return this.IssueDate; }
+ }
+
Uri IServiceProviderRequestToken.Callback {
get { return new Uri(this.RequestTokenCallback); }
set { this.RequestTokenCallback = value.AbsoluteUri; }
diff --git a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs
index 5585107..6980761 100644
--- a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs
@@ -126,8 +126,14 @@ namespace DotNetOpenAuth.Test.Mocks {
}
private class TokenInfo : IServiceProviderRequestToken {
+ internal TokenInfo() {
+ this.CreatedOn = DateTime.Now;
+ }
+
public string ConsumerKey { get; set; }
+ public DateTime CreatedOn { get; set; }
+
public string Token { get; set; }
public string VerificationCode { get; set; }
diff --git a/src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs
index 92d9645..c58c023 100644
--- a/src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs
@@ -22,6 +22,11 @@ namespace DotNetOpenAuth.Configuration {
private const string MinimumRequiredOAuthVersionConfigName = "minimumRequiredOAuthVersion";
/// <summary>
+ /// Gets the name of the @maxAuthorizationTime attribute.
+ /// </summary>
+ private const string MaximumRequestTokenTimeToLiveConfigName = "maxAuthorizationTime";
+
+ /// <summary>
/// Initializes a new instance of the <see cref="OAuthServiceProviderSecuritySettingsElement"/> class.
/// </summary>
internal OAuthServiceProviderSecuritySettingsElement() {
@@ -41,6 +46,22 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets the maximum time a user can take to complete authorization.
+ /// </summary>
+ /// <remarks>
+ /// This time limit serves as a security mitigation against brute force attacks to
+ /// compromise (unauthorized or authorized) request tokens.
+ /// Longer time limits is more friendly to slow users or consumers, while shorter
+ /// time limits provide better security.
+ /// </remarks>
+ [ConfigurationProperty(MaximumRequestTokenTimeToLiveConfigName, DefaultValue = "0:05")] // 5 minutes
+ [PositiveTimeSpanValidator]
+ public TimeSpan MaximumRequestTokenTimeToLive {
+ get { return (TimeSpan)this[MaximumRequestTokenTimeToLiveConfigName]; }
+ set { this[MaximumRequestTokenTimeToLiveConfigName] = value; }
+ }
+
+ /// <summary>
/// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file.
/// </summary>
/// <returns>The newly created security settings object.</returns>
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs
index 48f39a6..6dfa416 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs
@@ -23,6 +23,11 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
string ConsumerKey { get; }
/// <summary>
+ /// Gets the (local) date that this request token was first created on.
+ /// </summary>
+ DateTime CreatedOn { get; }
+
+ /// <summary>
/// Gets or sets the callback associated specifically with this token, if any.
/// </summary>
/// <value>The callback URI; or <c>null</c> if no callback was specifically assigned to this token.</value>
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs
index b8273c3..e8acdcf 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs
@@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
+ using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth.Messages;
@@ -111,15 +112,52 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
ErrorUtilities.VerifyArgumentNotNull(message, "message");
var authorizedTokenRequest = message as AuthorizedTokenRequest;
- if (authorizedTokenRequest != null && authorizedTokenRequest.Version >= Protocol.V10a.Version) {
- string expectedVerifier = this.tokenManager.GetRequestToken(authorizedTokenRequest.RequestToken).VerificationCode;
- ErrorUtilities.VerifyProtocol(string.Equals(authorizedTokenRequest.VerificationCode, expectedVerifier, StringComparison.Ordinal), OAuthStrings.IncorrectVerifier);
- return MessageProtections.None;
+ if (authorizedTokenRequest != null) {
+ if (authorizedTokenRequest.Version >= Protocol.V10a.Version) {
+ string expectedVerifier = this.tokenManager.GetRequestToken(authorizedTokenRequest.RequestToken).VerificationCode;
+ ErrorUtilities.VerifyProtocol(string.Equals(authorizedTokenRequest.VerificationCode, expectedVerifier, StringComparison.Ordinal), OAuthStrings.IncorrectVerifier);
+ return MessageProtections.None;
+ }
+
+ this.VerifyThrowTokenTimeToLive(authorizedTokenRequest);
+ }
+
+ var userAuthorizationRequest = message as UserAuthorizationRequest;
+ if (userAuthorizationRequest != null) {
+ this.VerifyThrowTokenTimeToLive(userAuthorizationRequest);
}
return null;
}
#endregion
+
+ /// <summary>
+ /// Ensures that short-lived request tokens included in incoming messages have not expired.
+ /// </summary>
+ /// <param name="message">The incoming message.</param>
+ /// <exception cref="ProtocolException">Thrown when the token in the message has expired.</exception>
+ private void VerifyThrowTokenTimeToLive(ITokenContainingMessage message) {
+ ErrorUtilities.VerifyInternal(!(message is AccessProtectedResourceRequest), "We shouldn't be verifying TTL on access tokens.");
+ if (message == null) {
+ return;
+ }
+
+ try {
+ IServiceProviderRequestToken token = this.tokenManager.GetRequestToken(message.Token);
+ TimeSpan ttl = DotNetOpenAuthSection.Configuration.OAuth.ServiceProvider.SecuritySettings.MaximumRequestTokenTimeToLive;
+ if (DateTime.Now >= token.CreatedOn.ToLocalTime() + ttl) {
+ Logger.OAuth.ErrorFormat(
+ "OAuth token {0} rejected because it was originally issued at {1}, expired at {2}, and it is now {3}.",
+ token.Token,
+ token.CreatedOn,
+ token.CreatedOn + ttl,
+ DateTime.Now);
+ ErrorUtilities.ThrowProtocol(OAuthStrings.TokenNotFound);
+ }
+ } catch (KeyNotFoundException ex) {
+ throw ErrorUtilities.Wrap(ex, OAuthStrings.TokenNotFound);
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
index d4fe85e..69d66f9 100644
--- a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
+++ b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.OAuth {
using System;
+ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
diff --git a/src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs b/src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs
index 094f54c..b8e12fd 100644
--- a/src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs
+++ b/src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs
@@ -5,6 +5,8 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OAuth {
+ using System;
+
/// <summary>
/// Security settings that are applicable to service providers.
/// </summary>