summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.AspNet.Test/OAuth2ClientTest.cs17
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/AuthenticationOnlyCookieOAuthTokenManager.cs46
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/CookieOAuthTokenManager.cs79
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/DotNetOpenAuthWebConsumer.cs2
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/LinkedInClient.cs2
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs6
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth2/MicrosoftClient.cs4
-rw-r--r--src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj3
-rw-r--r--src/DotNetOpenAuth.Core/Loggers/Log4NetLogger.cs2
-rw-r--r--src/DotNetOpenAuth.Core/Loggers/TraceLogger.cs120
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs4
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/MessagePartAttribute.cs9
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/MessagingStrings.Designer.cs11
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/MessagingStrings.resx7
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs42
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs10
-rw-r--r--src/DotNetOpenAuth.Core/Reporting.cs8
-rw-r--r--src/DotNetOpenAuth.Core/Util.cs10
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.Designer.cs11
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.resx7
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs14
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServerAccessToken.cs1
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs8
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs2
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs4
-rw-r--r--src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs2
-rw-r--r--src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs2
-rw-r--r--src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/StandardAccessTokenAnalyzer.cs12
-rw-r--r--src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/AssociationDataBag.cs2
-rw-r--r--src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/SelectorButton.cs1
-rw-r--r--src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs5
-rw-r--r--src/DotNetOpenAuth.OpenId/OpenId/UriIdentifier.cs5
-rw-r--r--src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj1
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs41
-rw-r--r--src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs5
-rw-r--r--src/DotNetOpenAuth.Test/OAuth2/ResourceServerTests.cs110
-rw-r--r--src/version.txt4
37 files changed, 539 insertions, 80 deletions
diff --git a/src/DotNetOpenAuth.AspNet.Test/OAuth2ClientTest.cs b/src/DotNetOpenAuth.AspNet.Test/OAuth2ClientTest.cs
index 89a483c..e60df01 100644
--- a/src/DotNetOpenAuth.AspNet.Test/OAuth2ClientTest.cs
+++ b/src/DotNetOpenAuth.AspNet.Test/OAuth2ClientTest.cs
@@ -47,7 +47,16 @@ namespace DotNetOpenAuth.AspNet.Test {
var client = new MockOAuth2Client();
// Act && Assert
- Assert.Throws<ArgumentNullException>(() => client.VerifyAuthentication(null));
+ Assert.Throws<ArgumentNullException>(() => client.VerifyAuthentication(null, new Uri("http://me.com")));
+ }
+
+ [TestCase]
+ public void VerifyAuthenticationWithoutReturnUrlThrows() {
+ // Arrange
+ var client = new MockOAuth2Client();
+
+ // Act && Assert
+ Assert.Throws<InvalidOperationException>(() => client.VerifyAuthentication(new Mock<HttpContextBase>().Object));
}
[TestCase]
@@ -59,7 +68,7 @@ namespace DotNetOpenAuth.AspNet.Test {
context.Setup(c => c.Request.QueryString).Returns(queryStrings);
// Act
- AuthenticationResult result = client.VerifyAuthentication(context.Object);
+ AuthenticationResult result = client.VerifyAuthentication(context.Object, new Uri("http://me.com"));
// Assert
Assert.IsFalse(result.IsSuccessful);
@@ -75,7 +84,7 @@ namespace DotNetOpenAuth.AspNet.Test {
context.Setup(c => c.Request.QueryString).Returns(queryStrings);
// Act
- AuthenticationResult result = client.VerifyAuthentication(context.Object);
+ AuthenticationResult result = client.VerifyAuthentication(context.Object, new Uri("http://me.com"));
// Assert
Assert.IsFalse(result.IsSuccessful);
@@ -91,7 +100,7 @@ namespace DotNetOpenAuth.AspNet.Test {
context.Setup(c => c.Request.QueryString).Returns(queryStrings);
// Act
- AuthenticationResult result = client.VerifyAuthentication(context.Object);
+ AuthenticationResult result = client.VerifyAuthentication(context.Object, new Uri("http://me.com"));
// Assert
Assert.True(result.IsSuccessful);
diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/AuthenticationOnlyCookieOAuthTokenManager.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/AuthenticationOnlyCookieOAuthTokenManager.cs
index 2ec988b..efc382f 100644
--- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/AuthenticationOnlyCookieOAuthTokenManager.cs
+++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth/AuthenticationOnlyCookieOAuthTokenManager.cs
@@ -17,7 +17,7 @@ namespace DotNetOpenAuth.AspNet.Clients {
/// <summary>
/// Key used for token cookie
/// </summary>
- private const string TokenCookieKey = "OAuthTokenSecret";
+ protected const string TokenCookieKey = "OAuthTokenSecret";
/// <summary>
/// Primary request context.
@@ -41,7 +41,7 @@ namespace DotNetOpenAuth.AspNet.Clients {
/// <summary>
/// Gets the effective HttpContext object to use.
/// </summary>
- private HttpContextBase Context {
+ protected HttpContextBase Context {
get {
return this.primaryContext ?? new HttpContextWrapper(HttpContext.Current);
}
@@ -54,15 +54,13 @@ namespace DotNetOpenAuth.AspNet.Clients {
/// <returns>
/// The token's secret
/// </returns>
- public string GetTokenSecret(string token) {
+ public virtual string GetTokenSecret(string token) {
HttpCookie cookie = this.Context.Request.Cookies[TokenCookieKey];
if (cookie == null || string.IsNullOrEmpty(cookie.Values[token])) {
return null;
}
- byte[] cookieBytes = HttpServerUtility.UrlTokenDecode(cookie.Values[token]);
- byte[] clearBytes = MachineKeyUtil.Unprotect(cookieBytes, TokenCookieKey, "Token:" + token);
- string secret = Encoding.UTF8.GetString(clearBytes);
+ string secret = DecodeAndUnprotectToken(token, cookie.Values[token]);
return secret;
}
@@ -72,7 +70,7 @@ namespace DotNetOpenAuth.AspNet.Clients {
/// <param name="requestToken">The request token.</param>
/// <param name="accessToken">The access token.</param>
/// <param name="accessTokenSecret">The access token secret.</param>
- public void ReplaceRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret) {
+ public virtual void ReplaceRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret) {
var cookie = new HttpCookie(TokenCookieKey) {
Value = string.Empty,
Expires = DateTime.UtcNow.AddDays(-5)
@@ -85,7 +83,7 @@ namespace DotNetOpenAuth.AspNet.Clients {
/// </summary>
/// <param name="requestToken">The request token.</param>
/// <param name="requestTokenSecret">The request token secret.</param>
- public void StoreRequestToken(string requestToken, string requestTokenSecret) {
+ public virtual void StoreRequestToken(string requestToken, string requestTokenSecret) {
var cookie = new HttpCookie(TokenCookieKey) {
HttpOnly = true
};
@@ -94,10 +92,36 @@ namespace DotNetOpenAuth.AspNet.Clients {
cookie.Secure = true;
}
- byte[] cookieBytes = Encoding.UTF8.GetBytes(requestTokenSecret);
- var secretBytes = MachineKeyUtil.Protect(cookieBytes, TokenCookieKey, "Token:" + requestToken);
- cookie.Values[requestToken] = HttpServerUtility.UrlTokenEncode(secretBytes);
+ var encryptedToken = ProtectAndEncodeToken(requestToken, requestTokenSecret);
+ cookie.Values[requestToken] = encryptedToken;
+
this.Context.Response.Cookies.Set(cookie);
}
+
+ /// <summary>
+ /// Protect and url-encode the specified token secret.
+ /// </summary>
+ /// <param name="token">The token to be used as a key.</param>
+ /// <param name="tokenSecret">The token secret to be protected</param>
+ /// <returns>The encrypted and protected string.</returns>
+ protected static string ProtectAndEncodeToken(string token, string tokenSecret)
+ {
+ byte[] cookieBytes = Encoding.UTF8.GetBytes(tokenSecret);
+ var secretBytes = MachineKeyUtil.Protect(cookieBytes, TokenCookieKey, "Token:" + token);
+ return HttpServerUtility.UrlTokenEncode(secretBytes);
+ }
+
+ /// <summary>
+ /// Url-decode and unprotect the specified encrypted token string.
+ /// </summary>
+ /// <param name="token">The token to be used as a key.</param>
+ /// <param name="encryptedToken">The encrypted token to be decrypted</param>
+ /// <returns>The original token secret</returns>
+ protected static string DecodeAndUnprotectToken(string token, string encryptedToken)
+ {
+ byte[] cookieBytes = HttpServerUtility.UrlTokenDecode(encryptedToken);
+ byte[] clearBytes = MachineKeyUtil.Unprotect(cookieBytes, TokenCookieKey, "Token:" + token);
+ return Encoding.UTF8.GetString(clearBytes);
+ }
}
} \ No newline at end of file
diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/CookieOAuthTokenManager.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/CookieOAuthTokenManager.cs
new file mode 100644
index 0000000..398ee85
--- /dev/null
+++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth/CookieOAuthTokenManager.cs
@@ -0,0 +1,79 @@
+//-----------------------------------------------------------------------
+// <copyright file="CookieOAuthTokenManager.cs" company="Microsoft">
+// Copyright (c) Microsoft. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.AspNet.Clients {
+ using System.Web;
+ using System.Web.Security;
+
+ /// <summary>
+ /// Stores OAuth tokens in the current request's cookie.
+ /// </summary>
+ /// <remarks>
+ /// This class is different from the <see cref="AuthenticationOnlyCookieOAuthTokenManager"/> in that
+ /// it also stores the access token after the authentication has succeeded.
+ /// </remarks>
+ public class CookieOAuthTokenManager : AuthenticationOnlyCookieOAuthTokenManager {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CookieOAuthTokenManager"/> class.
+ /// </summary>
+ public CookieOAuthTokenManager() {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CookieOAuthTokenManager"/> class.
+ /// </summary>
+ /// <param name="context">The current request context.</param>
+ public CookieOAuthTokenManager(HttpContextBase context)
+ : base(context) {
+ }
+
+ /// <summary>
+ /// Gets the token secret from the specified token.
+ /// </summary>
+ /// <param name="token">The token.</param>
+ /// <returns>
+ /// The token's secret
+ /// </returns>
+ public override string GetTokenSecret(string token) {
+ string secret = base.GetTokenSecret(token);
+ if (secret != null) {
+ return secret;
+ }
+
+ // The base class checks for cookies in the Request object.
+ // Here we check in the Response object as well because we
+ // may have set it earlier in the request life cycle.
+ HttpCookie cookie = this.Context.Response.Cookies[TokenCookieKey];
+ if (cookie == null || string.IsNullOrEmpty(cookie.Values[token])) {
+ return null;
+ }
+
+ secret = DecodeAndUnprotectToken(token, cookie.Values[token]);
+ return secret;
+ }
+
+ /// <summary>
+ /// Replaces the request token with access token.
+ /// </summary>
+ /// <param name="requestToken">The request token.</param>
+ /// <param name="accessToken">The access token.</param>
+ /// <param name="accessTokenSecret">The access token secret.</param>
+ public override void ReplaceRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret) {
+ var cookie = new HttpCookie(TokenCookieKey) {
+ HttpOnly = true
+ };
+
+ if (FormsAuthentication.RequireSSL) {
+ cookie.Secure = true;
+ }
+
+ var encryptedToken = ProtectAndEncodeToken(accessToken, accessTokenSecret);
+ cookie.Values[accessToken] = encryptedToken;
+
+ this.Context.Response.Cookies.Set(cookie);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/DotNetOpenAuthWebConsumer.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/DotNetOpenAuthWebConsumer.cs
index 7eda8e4..9af6804 100644
--- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/DotNetOpenAuthWebConsumer.cs
+++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth/DotNetOpenAuthWebConsumer.cs
@@ -77,7 +77,7 @@ namespace DotNetOpenAuth.AspNet.Clients {
/// The callback.
/// </param>
public void RequestAuthentication(Uri callback) {
- var redirectParameters = new Dictionary<string, string> { { "force_login", "false" } };
+ var redirectParameters = new Dictionary<string, string>();
UserAuthorizationRequest request = this.webConsumer.PrepareRequestUserAuthorization(
callback, null, redirectParameters);
this.webConsumer.Channel.PrepareResponse(request).Send();
diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/LinkedInClient.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/LinkedInClient.cs
index 26bc88d..3c157f3 100644
--- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/LinkedInClient.cs
+++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth/LinkedInClient.cs
@@ -60,7 +60,7 @@ namespace DotNetOpenAuth.AspNet.Clients {
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope",
Justification = "We can't dispose the object because we still need it through the app lifetime.")]
public LinkedInClient(string consumerKey, string consumerSecret)
- : this(consumerKey, consumerSecret, new AuthenticationOnlyCookieOAuthTokenManager()) { }
+ : this(consumerKey, consumerSecret, new CookieOAuthTokenManager()) { }
/// <summary>
/// Initializes a new instance of the <see cref="LinkedInClient"/> class.
diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs
index 8cb5cc5..a8c121d 100644
--- a/src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs
+++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs
@@ -78,7 +78,8 @@ namespace DotNetOpenAuth.AspNet.Clients {
builder.AppendQueryArgs(
new Dictionary<string, string> {
{ "client_id", this.appId },
- { "redirect_uri", returnUrl.AbsoluteUri }
+ { "redirect_uri", returnUrl.AbsoluteUri },
+ { "scope", "email" },
});
return builder.Uri;
}
@@ -133,6 +134,7 @@ namespace DotNetOpenAuth.AspNet.Clients {
{ "redirect_uri", NormalizeHexEncoding(returnUrl.AbsoluteUri) },
{ "client_secret", this.appSecret },
{ "code", authorizationCode },
+ { "scope", "email" },
});
using (WebClient client = new WebClient()) {
@@ -170,4 +172,4 @@ namespace DotNetOpenAuth.AspNet.Clients {
#endregion
}
-} \ No newline at end of file
+}
diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth2/MicrosoftClient.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth2/MicrosoftClient.cs
index 5ac5591..faea868 100644
--- a/src/DotNetOpenAuth.AspNet/Clients/OAuth2/MicrosoftClient.cs
+++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth2/MicrosoftClient.cs
@@ -20,12 +20,12 @@ namespace DotNetOpenAuth.AspNet.Clients {
/// <summary>
/// The authorization endpoint.
/// </summary>
- private const string AuthorizationEndpoint = "https://oauth.live.com/authorize";
+ private const string AuthorizationEndpoint = "https://login.live.com/oauth20_authorize.srf";
/// <summary>
/// The token endpoint.
/// </summary>
- private const string TokenEndpoint = "https://oauth.live.com/token";
+ private const string TokenEndpoint = "https://login.live.com/oauth20_token.srf";
/// <summary>
/// The _app id.
diff --git a/src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj b/src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj
index 2f03ec7..405ac3c 100644
--- a/src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj
+++ b/src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj
@@ -28,7 +28,7 @@
<Import Project="$(ProjectRoot)tools\DotNetOpenAuth.props" />
<Import Project="$(ProjectRoot)tools\DotNetOpenAuth.Product.props" />
<PropertyGroup>
- <RootNamespace>DotNetOpenAuth.AspNet</RootNamespace>
+ <RootNamespace>DotNetOpenAuth.AspNet</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@@ -48,6 +48,7 @@
<Compile Include="Clients\OAuth\AuthenticationOnlyCookieOAuthTokenManager.cs">
<SubType>Code</SubType>
</Compile>
+ <Compile Include="Clients\OAuth\CookieOAuthTokenManager.cs" />
<Compile Include="Clients\OAuth\IOAuthTokenManager.cs" />
<Compile Include="Clients\OAuth\SimpleConsumerTokenManager.cs" />
<Compile Include="IAuthenticationClient.cs" />
diff --git a/src/DotNetOpenAuth.Core/Loggers/Log4NetLogger.cs b/src/DotNetOpenAuth.Core/Loggers/Log4NetLogger.cs
index 293a6b2..01da034 100644
--- a/src/DotNetOpenAuth.Core/Loggers/Log4NetLogger.cs
+++ b/src/DotNetOpenAuth.Core/Loggers/Log4NetLogger.cs
@@ -201,6 +201,8 @@ namespace DotNetOpenAuth.Loggers {
return IsLog4NetPresent ? CreateLogger(name) : null;
} catch (FileLoadException) { // wrong log4net.dll version
return null;
+ } catch (TargetInvocationException) { // Thrown due to some security issues on .NET 4.5.
+ return null;
} catch (TypeLoadException) { // Thrown by mono (http://stackoverflow.com/questions/10805773/error-when-pushing-dotnetopenauth-to-staging-or-production-environment)
return null;
}
diff --git a/src/DotNetOpenAuth.Core/Loggers/TraceLogger.cs b/src/DotNetOpenAuth.Core/Loggers/TraceLogger.cs
index 9b0bb0f..1b80c7d 100644
--- a/src/DotNetOpenAuth.Core/Loggers/TraceLogger.cs
+++ b/src/DotNetOpenAuth.Core/Loggers/TraceLogger.cs
@@ -77,210 +77,270 @@ namespace DotNetOpenAuth.Loggers {
/// See <see cref="ILog"/>.
/// </summary>
public void Debug(object message) {
- Trace.TraceInformation(message.ToString());
+ if (this.IsDebugEnabled) {
+ Trace.TraceInformation(message.ToString());
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void Debug(object message, Exception exception) {
- Trace.TraceInformation(message + ": " + exception.ToString());
+ if (this.IsDebugEnabled) {
+ Trace.TraceInformation(message + ": " + exception.ToString());
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void DebugFormat(string format, params object[] args) {
- Trace.TraceInformation(format, args);
+ if (this.IsDebugEnabled) {
+ Trace.TraceInformation(format, args);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void DebugFormat(string format, object arg0) {
- Trace.TraceInformation(format, arg0);
+ if (this.IsDebugEnabled) {
+ Trace.TraceInformation(format, arg0);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void DebugFormat(string format, object arg0, object arg1) {
- Trace.TraceInformation(format, arg0, arg1);
+ if (this.IsDebugEnabled) {
+ Trace.TraceInformation(format, arg0, arg1);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void DebugFormat(string format, object arg0, object arg1, object arg2) {
- Trace.TraceInformation(format, arg0, arg1, arg2);
+ if (this.IsDebugEnabled) {
+ Trace.TraceInformation(format, arg0, arg1, arg2);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void Info(object message) {
- Trace.TraceInformation(message.ToString());
+ if (this.IsInfoEnabled) {
+ Trace.TraceInformation(message.ToString());
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void Info(object message, Exception exception) {
- Trace.TraceInformation(message + ": " + exception.ToString());
+ if (this.IsInfoEnabled) {
+ Trace.TraceInformation(message + ": " + exception.ToString());
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void InfoFormat(string format, params object[] args) {
- Trace.TraceInformation(format, args);
+ if (this.IsInfoEnabled) {
+ Trace.TraceInformation(format, args);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void InfoFormat(string format, object arg0) {
- Trace.TraceInformation(format, arg0);
+ if (this.IsInfoEnabled) {
+ Trace.TraceInformation(format, arg0);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void InfoFormat(string format, object arg0, object arg1) {
- Trace.TraceInformation(format, arg0, arg1);
+ if (this.IsInfoEnabled) {
+ Trace.TraceInformation(format, arg0, arg1);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void InfoFormat(string format, object arg0, object arg1, object arg2) {
- Trace.TraceInformation(format, arg0, arg1, arg2);
+ if (this.IsInfoEnabled) {
+ Trace.TraceInformation(format, arg0, arg1, arg2);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void Warn(object message) {
- Trace.TraceWarning(message.ToString());
+ if (this.IsWarnEnabled) {
+ Trace.TraceWarning(message.ToString());
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void Warn(object message, Exception exception) {
- Trace.TraceWarning(message + ": " + exception.ToString());
+ if (this.IsWarnEnabled) {
+ Trace.TraceWarning(message + ": " + exception.ToString());
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void WarnFormat(string format, params object[] args) {
- Trace.TraceWarning(format, args);
+ if (this.IsWarnEnabled) {
+ Trace.TraceWarning(format, args);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void WarnFormat(string format, object arg0) {
- Trace.TraceWarning(format, arg0);
+ if (this.IsWarnEnabled) {
+ Trace.TraceWarning(format, arg0);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void WarnFormat(string format, object arg0, object arg1) {
- Trace.TraceWarning(format, arg0, arg1);
+ if (this.IsWarnEnabled) {
+ Trace.TraceWarning(format, arg0, arg1);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void WarnFormat(string format, object arg0, object arg1, object arg2) {
- Trace.TraceWarning(format, arg0, arg1, arg2);
+ if (this.IsWarnEnabled) {
+ Trace.TraceWarning(format, arg0, arg1, arg2);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void Error(object message) {
- Trace.TraceError(message.ToString());
+ if (this.IsErrorEnabled) {
+ Trace.TraceError(message.ToString());
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void Error(object message, Exception exception) {
- Trace.TraceError(message + ": " + exception.ToString());
+ if (this.IsErrorEnabled) {
+ Trace.TraceError(message + ": " + exception.ToString());
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void ErrorFormat(string format, params object[] args) {
- Trace.TraceError(format, args);
+ if (this.IsErrorEnabled) {
+ Trace.TraceError(format, args);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void ErrorFormat(string format, object arg0) {
- Trace.TraceError(format, arg0);
+ if (this.IsErrorEnabled) {
+ Trace.TraceError(format, arg0);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void ErrorFormat(string format, object arg0, object arg1) {
- Trace.TraceError(format, arg0, arg1);
+ if (this.IsErrorEnabled) {
+ Trace.TraceError(format, arg0, arg1);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void ErrorFormat(string format, object arg0, object arg1, object arg2) {
- Trace.TraceError(format, arg0, arg1, arg2);
+ if (this.IsErrorEnabled) {
+ Trace.TraceError(format, arg0, arg1, arg2);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void Fatal(object message) {
- Trace.TraceError(message.ToString());
+ if (this.IsFatalEnabled) {
+ Trace.TraceError(message.ToString());
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void Fatal(object message, Exception exception) {
- Trace.TraceError(message + ": " + exception.ToString());
+ if (this.IsFatalEnabled) {
+ Trace.TraceError(message + ": " + exception.ToString());
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void FatalFormat(string format, params object[] args) {
- Trace.TraceError(format, args);
+ if (this.IsFatalEnabled) {
+ Trace.TraceError(format, args);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void FatalFormat(string format, object arg0) {
- Trace.TraceError(format, arg0);
+ if (this.IsFatalEnabled) {
+ Trace.TraceError(format, arg0);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void FatalFormat(string format, object arg0, object arg1) {
- Trace.TraceError(format, arg0, arg1);
+ if (this.IsFatalEnabled) {
+ Trace.TraceError(format, arg0, arg1);
+ }
}
/// <summary>
/// See <see cref="ILog"/>.
/// </summary>
public void FatalFormat(string format, object arg0, object arg1, object arg2) {
- Trace.TraceError(format, arg0, arg1, arg2);
+ if (this.IsFatalEnabled) {
+ Trace.TraceError(format, arg0, arg1, arg2);
+ }
}
#endregion
diff --git a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs
index 69ee8dc..2452502 100644
--- a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs
@@ -217,8 +217,8 @@ namespace DotNetOpenAuth.Messaging {
if (this.signed) {
using (var dataStream = new MemoryStream(data)) {
var dataReader = new BinaryReader(dataStream);
- signature = dataReader.ReadBuffer();
- data = dataReader.ReadBuffer();
+ signature = dataReader.ReadBuffer(1024);
+ data = dataReader.ReadBuffer(8 * 1024);
}
// Verify that the verification code was issued by message authorization server.
diff --git a/src/DotNetOpenAuth.Core/Messaging/MessagePartAttribute.cs b/src/DotNetOpenAuth.Core/Messaging/MessagePartAttribute.cs
index 6fd95ee..8ef9b7e 100644
--- a/src/DotNetOpenAuth.Core/Messaging/MessagePartAttribute.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/MessagePartAttribute.cs
@@ -101,6 +101,15 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Gets or sets a value indicating whether the value contained by this property contains
+ /// sensitive information that should generally not be logged.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is security sensitive; otherwise, <c>false</c>.
+ /// </value>
+ public bool IsSecuritySensitive { get; set; }
+
+ /// <summary>
/// Gets or sets the minimum version of the protocol this attribute applies to
/// and overrides any attributes with lower values for this property.
/// </summary>
diff --git a/src/DotNetOpenAuth.Core/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth.Core/Messaging/MessagingStrings.Designer.cs
index 2fe273f..4f89589 100644
--- a/src/DotNetOpenAuth.Core/Messaging/MessagingStrings.Designer.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/MessagingStrings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:4.0.30319.239
+// Runtime Version:4.0.30319.18010
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -106,6 +106,15 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Looks up a localized string similar to Decoding failed due to data corruption..
+ /// </summary>
+ internal static string DataCorruptionDetected {
+ get {
+ return ResourceManager.GetString("DataCorruptionDetected", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to An instance of type {0} was expected, but received unexpected derived type {1}..
/// </summary>
internal static string DerivedTypeNotExpected {
diff --git a/src/DotNetOpenAuth.Core/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth.Core/Messaging/MessagingStrings.resx
index fbdb63d..15ca046 100644
--- a/src/DotNetOpenAuth.Core/Messaging/MessagingStrings.resx
+++ b/src/DotNetOpenAuth.Core/Messaging/MessagingStrings.resx
@@ -112,10 +112,10 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ArgumentPropertyMissing" xml:space="preserve">
<value>Argument's {0}.{1} property is required but is empty or null.</value>
@@ -333,4 +333,7 @@
<data name="UnexpectedBufferLength" xml:space="preserve">
<value>Unexpected buffer length.</value>
</data>
+ <data name="DataCorruptionDetected" xml:space="preserve">
+ <value>Decoding failed due to data corruption.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs
index 5390da5..7aa4469 100644
--- a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs
@@ -15,6 +15,9 @@ namespace DotNetOpenAuth.Messaging {
using System.IO.Compression;
using System.Linq;
using System.Net;
+#if CLR4
+ using System.Net.Http;
+#endif
using System.Net.Mime;
using System.Runtime.Serialization.Json;
using System.Security;
@@ -161,6 +164,29 @@ namespace DotNetOpenAuth.Messaging {
return new OutgoingWebResponseActionResult(response);
}
+#if CLR4
+ /// <summary>
+ /// Transforms an OutgoingWebResponse to a Web API-friendly HttpResponseMessage.
+ /// </summary>
+ /// <param name="outgoingResponse">The response to send to the user agent.</param>
+ /// <returns>The <see cref="HttpResponseMessage"/> instance to be returned by the Web API method.</returns>
+ public static HttpResponseMessage AsHttpResponseMessage(this OutgoingWebResponse outgoingResponse) {
+ HttpResponseMessage response = new HttpResponseMessage(outgoingResponse.Status);
+ if (outgoingResponse.ResponseStream != null) {
+ response.Content = new StreamContent(outgoingResponse.ResponseStream);
+ }
+
+ var responseHeaders = outgoingResponse.Headers;
+ foreach (var header in responseHeaders.AllKeys) {
+ if (!response.Headers.TryAddWithoutValidation(header, responseHeaders[header])) {
+ response.Content.Headers.TryAddWithoutValidation(header, responseHeaders[header]);
+ }
+ }
+
+ return response;
+ }
+#endif
+
/// <summary>
/// Gets the original request URL, as seen from the browser before any URL rewrites on the server if any.
/// Cookieless session directory (if applicable) is also included.
@@ -797,7 +823,7 @@ namespace DotNetOpenAuth.Messaging {
using (var encryptedStream = new MemoryStream(buffer)) {
var encryptedStreamReader = new BinaryReader(encryptedStream);
- byte[] encryptedPrequel = encryptedStreamReader.ReadBytes(encryptedStreamReader.ReadInt32());
+ byte[] encryptedPrequel = encryptedStreamReader.ReadBuffer(4096);
byte[] prequel = crypto.Decrypt(encryptedPrequel, false);
using (var symmetricCrypto = new RijndaelManaged()) {
@@ -989,7 +1015,7 @@ namespace DotNetOpenAuth.Messaging {
missingPaddingCharacters = 0;
break;
default:
- throw ErrorUtilities.ThrowInternal("No more than two padding characters should be present for base64.");
+ throw new ProtocolException(MessagingStrings.DataCorruptionDetected, new ArgumentException("No more than two padding characters should be present for base64."));
}
var builder = new StringBuilder(base64WebSafe, base64WebSafe.Length + missingPaddingCharacters);
builder.Replace('-', '+').Replace('_', '/');
@@ -1659,10 +1685,17 @@ namespace DotNetOpenAuth.Messaging {
/// Reads a buffer that is prefixed with its own length.
/// </summary>
/// <param name="reader">The binary reader positioned at the buffer length.</param>
+ /// <param name="maxBufferSize">
+ /// The maximum size of the buffer that should be permitted.
+ /// Although the stream will indicate the size of the buffer, this mitigates data corruption
+ /// or DoS attacks causing the web server to allocate too much memory for a small data packet.
+ /// </param>
/// <returns>The read buffer.</returns>
- internal static byte[] ReadBuffer(this BinaryReader reader) {
+ internal static byte[] ReadBuffer(this BinaryReader reader, int maxBufferSize) {
Requires.NotNull(reader, "reader");
+ Requires.InRange(maxBufferSize > 0 && maxBufferSize < 1024 * 1024, "maxBufferSize");
int length = reader.ReadInt32();
+ ErrorUtilities.VerifyProtocol(length <= maxBufferSize, MessagingStrings.DataCorruptionDetected);
byte[] buffer = new byte[length];
ErrorUtilities.VerifyProtocol(reader.Read(buffer, 0, length) == length, MessagingStrings.UnexpectedBufferLength);
return buffer;
@@ -1894,7 +1927,8 @@ namespace DotNetOpenAuth.Messaging {
// the public URL:
if (serverVariables["HTTP_HOST"] != null) {
ErrorUtilities.VerifySupported(request.Url.Scheme == Uri.UriSchemeHttps || request.Url.Scheme == Uri.UriSchemeHttp, "Only HTTP and HTTPS are supported protocols.");
- string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ?? request.Url.Scheme;
+ string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ??
+ (string.Equals(serverVariables["HTTP_FRONT_END_HTTPS"], "on", StringComparison.OrdinalIgnoreCase) ? Uri.UriSchemeHttps : request.Url.Scheme);
Uri hostAndPort = new Uri(scheme + Uri.SchemeDelimiter + serverVariables["HTTP_HOST"]);
UriBuilder publicRequestUri = new UriBuilder(request.Url);
publicRequestUri.Scheme = scheme;
diff --git a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs
index b2c4664..0f140d6 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs
@@ -115,6 +115,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
this.RequiredProtection = attribute.RequiredProtection;
this.IsRequired = attribute.IsRequired;
this.AllowEmpty = attribute.AllowEmpty;
+ this.IsSecuritySensitive = attribute.IsSecuritySensitive;
this.memberDeclaredType = (this.field != null) ? this.field.FieldType : this.property.PropertyType;
this.defaultMemberValue = DeriveDefaultValue(this.memberDeclaredType);
@@ -189,6 +190,15 @@ namespace DotNetOpenAuth.Messaging.Reflection {
internal bool IsConstantValueAvailableStatically { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether the value contained by this property contains
+ /// sensitive information that should generally not be logged.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is security sensitive; otherwise, <c>false</c>.
+ /// </value>
+ internal bool IsSecuritySensitive { get; set; }
+
+ /// <summary>
/// Gets the static constant value for this message part without a message instance.
/// </summary>
internal string StaticConstantValue {
diff --git a/src/DotNetOpenAuth.Core/Reporting.cs b/src/DotNetOpenAuth.Core/Reporting.cs
index 951bb7c..80a3374 100644
--- a/src/DotNetOpenAuth.Core/Reporting.cs
+++ b/src/DotNetOpenAuth.Core/Reporting.cs
@@ -519,8 +519,12 @@ namespace DotNetOpenAuth {
SendStats();
} catch (Exception ex) {
// Something bad and unexpected happened. Just deactivate to avoid more trouble.
- Logger.Library.Error("Error while trying to submit statistical report.", ex);
- broken = true;
+ try {
+ broken = true;
+ Logger.Library.Error("Error while trying to submit statistical report.", ex);
+ } catch (Exception) {
+ // swallow exceptions to prevent a crash.
+ }
}
});
}
diff --git a/src/DotNetOpenAuth.Core/Util.cs b/src/DotNetOpenAuth.Core/Util.cs
index e9d617a..26b7b45 100644
--- a/src/DotNetOpenAuth.Core/Util.cs
+++ b/src/DotNetOpenAuth.Core/Util.cs
@@ -16,6 +16,7 @@ namespace DotNetOpenAuth {
using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
/// <summary>
/// A grab-bag utility class.
@@ -105,9 +106,16 @@ namespace DotNetOpenAuth {
////Contract.Requires(pairs != null); // CC: anonymous method can't handle it
ErrorUtilities.VerifyArgumentNotNull(pairs, "pairs");
var dictionary = pairs as IDictionary<K, V>;
+ var messageDictionary = pairs as MessageDictionary;
StringBuilder sb = new StringBuilder(dictionary != null ? dictionary.Count * 40 : 200);
foreach (var pair in pairs) {
- sb.AppendFormat("\t{0}: {1}{2}", pair.Key, pair.Value, Environment.NewLine);
+ var key = pair.Key.ToString();
+ string value = pair.Value.ToString();
+ if (messageDictionary != null && messageDictionary.Description.Mapping.ContainsKey(key) && messageDictionary.Description.Mapping[key].IsSecuritySensitive) {
+ value = "********";
+ }
+
+ sb.AppendFormat("\t{0}: {1}{2}", key, value, Environment.NewLine);
}
return sb.ToString();
});
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.Designer.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.Designer.cs
index 4b4f830..8941a94 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.Designer.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:4.0.30319.17614
+// Runtime Version:4.0.30319.18010
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -70,6 +70,15 @@ namespace DotNetOpenAuth.OAuth2 {
}
/// <summary>
+ /// Looks up a localized string similar to The access token&apos;s private signing key must be set..
+ /// </summary>
+ internal static string AccessTokenSigningKeyMissing {
+ get {
+ return ResourceManager.GetString("AccessTokenSigningKeyMissing", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The callback URL ({0}) is not allowed for this client..
/// </summary>
internal static string ClientCallbackDisallowed {
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.resx b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.resx
index 29d841a..8aaa567 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.resx
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.resx
@@ -112,14 +112,17 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AccessScopeExceedsGrantScope" xml:space="preserve">
<value>The requested access scope exceeds the grant scope.</value>
</data>
+ <data name="AccessTokenSigningKeyMissing" xml:space="preserve">
+ <value>The access token's private signing key must be set.</value>
+ </data>
<data name="ClientCallbackDisallowed" xml:space="preserve">
<value>The callback URL ({0}) is not allowed for this client.</value>
</data>
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs
index 6a96c2d..1e404e7 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs
@@ -10,6 +10,9 @@ namespace DotNetOpenAuth.OAuth2 {
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
+#if CLR4
+ using System.Net.Http;
+#endif
using System.Security.Cryptography;
using System.Text;
using System.Web;
@@ -129,6 +132,17 @@ namespace DotNetOpenAuth.OAuth2 {
this.Channel.Respond(response);
}
+#if CLR4
+ /// <summary>
+ /// Handles an incoming request to the authorization server's token endpoint.
+ /// </summary>
+ /// <param name="request">The HTTP request.</param>
+ /// <returns>The HTTP response to send to the client.</returns>
+ public OutgoingWebResponse HandleTokenRequest(HttpRequestMessage request) {
+ return this.HandleTokenRequest(new HttpRequestInfo(request));
+ }
+#endif
+
/// <summary>
/// Handles an incoming request to the authorization server's token endpoint.
/// </summary>
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServerAccessToken.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServerAccessToken.cs
index c577a0a..a127166 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServerAccessToken.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServerAccessToken.cs
@@ -45,6 +45,7 @@ namespace DotNetOpenAuth.OAuth2 {
/// </summary>
/// <returns>A non-empty string.</returns>
protected internal override string Serialize() {
+ ErrorUtilities.VerifyHost(this.AccessTokenSigningKey != null, AuthServerStrings.AccessTokenSigningKeyMissing);
var formatter = CreateFormatter(this.AccessTokenSigningKey, this.ResourceServerEncryptionKey);
return formatter.Serialize(this);
}
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs
index 655d38f..6f0bbc4 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OAuth2.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Globalization;
using System.Linq;
using System.Text;
using System.Web;
@@ -21,10 +22,15 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
/// Gets this module's contribution to an HTTP 401 WWW-Authenticate header so the client knows what kind of authentication this module supports.
/// </summary>
public override string AuthenticateHeader {
- get { return "Basic"; }
+ get { return string.Format(CultureInfo.InvariantCulture, "Basic realm=\"{0}\"", this.Realm); }
}
/// <summary>
+ /// Gets or sets the realm that is included in an HTTP WWW-Authenticate header included in a 401 Unauthorized response.
+ /// </summary>
+ public string Realm { get; set; }
+
+ /// <summary>
/// Attempts to extract client identification/authentication information from a message.
/// </summary>
/// <param name="authorizationServerHost">The authorization server host.</param>
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
index c983f8c..49d0732 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
@@ -100,7 +100,7 @@ namespace DotNetOpenAuth.OAuth2 {
Requires.NotNull(request, "request");
Requires.NotNull(authorization, "authorization");
Requires.True(!string.IsNullOrEmpty(authorization.AccessToken), "authorization");
- ErrorUtilities.VerifyProtocol(!authorization.AccessTokenExpirationUtc.HasValue || authorization.AccessTokenExpirationUtc < DateTime.UtcNow || authorization.RefreshToken != null, ClientStrings.AuthorizationExpired);
+ ErrorUtilities.VerifyProtocol(!authorization.AccessTokenExpirationUtc.HasValue || authorization.AccessTokenExpirationUtc >= DateTime.UtcNow || authorization.RefreshToken != null, ClientStrings.AuthorizationExpired);
if (authorization.AccessTokenExpirationUtc.HasValue && authorization.AccessTokenExpirationUtc.Value < DateTime.UtcNow) {
ErrorUtilities.VerifyProtocol(authorization.RefreshToken != null, ClientStrings.AccessTokenRefreshFailed);
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs
index cc4e45f..0677f5a 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs
@@ -135,7 +135,9 @@ namespace DotNetOpenAuth.OAuth2 {
clientIdentifier);
}
- request.Credentials = this.credential ?? new NetworkCredential(clientIdentifier, this.clientSecret);
+ // HttpWebRequest ignores the Credentials property until the remote server returns a 401 Unauthorized.
+ // So we also set the HTTP Authorization request header directly.
+ OAuthUtilities.ApplyHttpBasicAuth(request.Headers, clientIdentifier, this.clientSecret);
}
}
}
diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs
index 52e65be..a5d958a 100644
--- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs
+++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs
@@ -82,7 +82,7 @@ namespace DotNetOpenAuth.OAuth2.Messages {
/// Gets or sets the user's password.
/// </summary>
/// <value>The password.</value>
- [MessagePart(Protocol.password, IsRequired = true)]
+ [MessagePart(Protocol.password, IsRequired = true, IsSecuritySensitive = true)]
internal string Password { get; set; }
/// <summary>
diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs
index 4631d83..96eecbb 100644
--- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs
+++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs
@@ -44,7 +44,7 @@ namespace DotNetOpenAuth.OAuth2.Messages {
/// <remarks>
/// REQUIRED. The client secret as described in Section 2.1 (Client Credentials). OPTIONAL if no client secret was issued.
/// </remarks>
- [MessagePart(Protocol.client_secret, IsRequired = false)]
+ [MessagePart(Protocol.client_secret, IsRequired = false, IsSecuritySensitive = true)]
public string ClientSecret { get; internal set; }
/// <summary>
diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/StandardAccessTokenAnalyzer.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/StandardAccessTokenAnalyzer.cs
index 54d86ff..33b99f9 100644
--- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/StandardAccessTokenAnalyzer.cs
+++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/StandardAccessTokenAnalyzer.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth2 {
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
+ using System.IO;
using System.Security.Cryptography;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth2.ChannelElements;
@@ -23,8 +24,7 @@ namespace DotNetOpenAuth.OAuth2 {
/// <param name="resourceServerPrivateEncryptionKey">The crypto service provider with the resource server private encryption key.</param>
public StandardAccessTokenAnalyzer(RSACryptoServiceProvider authorizationServerPublicSigningKey, RSACryptoServiceProvider resourceServerPrivateEncryptionKey) {
Requires.NotNull(authorizationServerPublicSigningKey, "authorizationServerPublicSigningKey");
- Requires.NotNull(resourceServerPrivateEncryptionKey, "resourceServerPrivateEncryptionKey");
- Requires.True(!resourceServerPrivateEncryptionKey.PublicOnly, "resourceServerPrivateEncryptionKey");
+ Requires.True(resourceServerPrivateEncryptionKey == null || !resourceServerPrivateEncryptionKey.PublicOnly, "resourceServerPrivateEncryptionKey");
this.AuthorizationServerPublicSigningKey = authorizationServerPublicSigningKey;
this.ResourceServerPrivateEncryptionKey = resourceServerPrivateEncryptionKey;
}
@@ -49,9 +49,15 @@ namespace DotNetOpenAuth.OAuth2 {
/// <returns>The deserialized, validated token.</returns>
/// <exception cref="ProtocolException">Thrown if the access token is expired, invalid, or from an untrusted authorization server.</exception>
public virtual AccessToken DeserializeAccessToken(IDirectedProtocolMessage message, string accessToken) {
+ ErrorUtilities.VerifyProtocol(!string.IsNullOrEmpty(accessToken), ResourceServerStrings.MissingAccessToken);
var accessTokenFormatter = AccessToken.CreateFormatter(this.AuthorizationServerPublicSigningKey, this.ResourceServerPrivateEncryptionKey);
var token = new AccessToken();
- accessTokenFormatter.Deserialize(token, message, accessToken, Protocol.access_token);
+ try {
+ accessTokenFormatter.Deserialize(token, message, accessToken, Protocol.access_token);
+ } catch (IOException ex) {
+ throw new ProtocolException(ResourceServerStrings.InvalidAccessToken, ex);
+ }
+
return token;
}
}
diff --git a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/AssociationDataBag.cs b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/AssociationDataBag.cs
index ee48670..f619b76 100644
--- a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/AssociationDataBag.cs
+++ b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/AssociationDataBag.cs
@@ -72,7 +72,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
public void Deserialize(Stream stream) {
var reader = new BinaryReader(stream);
this.IsPrivateAssociation = reader.ReadBoolean();
- this.Secret = reader.ReadBuffer();
+ this.Secret = reader.ReadBuffer(256);
this.ExpiresUtc = TimestampEncoder.Epoch + TimeSpan.FromSeconds(reader.ReadInt32());
}
diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/SelectorButton.cs b/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/SelectorButton.cs
index b4d0aa0..670189c 100644
--- a/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/SelectorButton.cs
+++ b/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/SelectorButton.cs
@@ -13,6 +13,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// A button that would appear in the <see cref="OpenIdSelector"/> control via its <see cref="OpenIdSelector.Buttons"/> collection.
/// </summary>
[ContractClass(typeof(SelectorButtonContract))]
+ [Serializable]
public abstract class SelectorButton {
/// <summary>
/// Initializes a new instance of the <see cref="SelectorButton"/> class.
diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
index fa7768b..912a322 100644
--- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
+++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
@@ -195,6 +195,11 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
try {
if (cryptoKey == null) {
cryptoKey = this.cryptoKeyStore.GetKey(SecretUri.AbsoluteUri, returnToParameters[ReturnToSignatureHandleParameterName]);
+ ErrorUtilities.VerifyProtocol(
+ cryptoKey != null,
+ MessagingStrings.MissingDecryptionKeyForHandle,
+ SecretUri.AbsoluteUri,
+ returnToParameters[ReturnToSignatureHandleParameterName]);
}
using (var signer = HmacAlgorithms.Create(HmacAlgorithms.HmacSha256, cryptoKey.Key)) {
diff --git a/src/DotNetOpenAuth.OpenId/OpenId/UriIdentifier.cs b/src/DotNetOpenAuth.OpenId/OpenId/UriIdentifier.cs
index 631eab6..41417de 100644
--- a/src/DotNetOpenAuth.OpenId/OpenId/UriIdentifier.cs
+++ b/src/DotNetOpenAuth.OpenId/OpenId/UriIdentifier.cs
@@ -69,6 +69,11 @@ namespace DotNetOpenAuth.OpenId {
/// </remarks>
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Some things just can't be done in a field initializer.")]
static UriIdentifier() {
+ if (Type.GetType("Mono.Runtime") != null) {
+ // Uri scheme registration doesn't work on mono.
+ return;
+ }
+
// Our first attempt to handle trailing periods in path segments is to leverage
// full trust if it's available to rewrite the rules.
// In fact this is the ONLY way in .NET 3.5 (and arguably in .NET 4.0) to send
diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
index b58aa17..189a569 100644
--- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
+++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
@@ -249,6 +249,7 @@
<Compile Include="Mocks\TestMessageFactory.cs" />
<Compile Include="OAuth2\AuthorizationServerTests.cs" />
<Compile Include="OAuth2\MessageFactoryTests.cs" />
+ <Compile Include="OAuth2\ResourceServerTests.cs" />
<Compile Include="OAuth2\UserAgentClientAuthorizeTests.cs" />
<Compile Include="OAuth2\OAuth2Coordinator.cs" />
<Compile Include="OAuth2\OAuth2TestBase.cs" />
diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
index 5c3870c..8620b93 100644
--- a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
@@ -9,13 +9,17 @@ namespace DotNetOpenAuth.Test.Messaging {
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
+ using System.Globalization;
using System.IO;
+ using System.Linq;
using System.Net;
+ using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Test.Mocks;
+ using Moq;
using NUnit.Framework;
[TestFixture]
@@ -63,6 +67,41 @@ namespace DotNetOpenAuth.Test.Messaging {
}
[Test]
+ public void AsHttpResponseMessage() {
+ var responseContent = new byte[10];
+ (new Random()).NextBytes(responseContent);
+ var responseStream = new MemoryStream(responseContent);
+ var outgoingResponse = new OutgoingWebResponse();
+ outgoingResponse.Headers.Add("X-SOME-HEADER", "value");
+ outgoingResponse.Headers.Add("Content-Length", responseContent.Length.ToString(CultureInfo.InvariantCulture));
+ outgoingResponse.ResponseStream = responseStream;
+
+ var httpResponseMessage = outgoingResponse.AsHttpResponseMessage();
+ Assert.That(httpResponseMessage, Is.Not.Null);
+ Assert.That(httpResponseMessage.Headers.GetValues("X-SOME-HEADER").ToList(), Is.EqualTo(new[] { "value" }));
+ Assert.That(
+ httpResponseMessage.Content.Headers.GetValues("Content-Length").ToList(),
+ Is.EqualTo(new[] { responseContent.Length.ToString(CultureInfo.InvariantCulture) }));
+ var actualContent = new byte[responseContent.Length + 1]; // give the opportunity to provide a bit more data than we expect.
+ var bytesRead = httpResponseMessage.Content.ReadAsStreamAsync().Result.Read(actualContent, 0, actualContent.Length);
+ Assert.That(bytesRead, Is.EqualTo(responseContent.Length)); // verify that only the data we expected came back.
+ var trimmedActualContent = new byte[bytesRead];
+ Array.Copy(actualContent, trimmedActualContent, bytesRead);
+ Assert.That(trimmedActualContent, Is.EqualTo(responseContent));
+ }
+
+ [Test]
+ public void AsHttpResponseMessageNoContent() {
+ var outgoingResponse = new OutgoingWebResponse();
+ outgoingResponse.Headers.Add("X-SOME-HEADER", "value");
+
+ var httpResponseMessage = outgoingResponse.AsHttpResponseMessage();
+ Assert.That(httpResponseMessage, Is.Not.Null);
+ Assert.That(httpResponseMessage.Headers.GetValues("X-SOME-HEADER").ToList(), Is.EqualTo(new[] { "value" }));
+ Assert.That(httpResponseMessage.Content, Is.Null);
+ }
+
+ [Test]
public void ToDictionary() {
NameValueCollection nvc = new NameValueCollection();
nvc["a"] = "b";
@@ -151,7 +190,7 @@ namespace DotNetOpenAuth.Test.Messaging {
var httpHandler = new TestWebRequestHandler();
bool callbackTriggered = false;
httpHandler.Callback = req => {
- Match m = Regex.Match(req.ContentType, "multipart/form-data; boundary=(.+)");
+ var m = Regex.Match(req.ContentType, "multipart/form-data; boundary=(.+)");
Assert.IsTrue(m.Success, "Content-Type HTTP header not set correctly.");
string boundary = m.Groups[1].Value;
boundary = boundary.Substring(0, boundary.IndexOf(';')); // trim off charset
diff --git a/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs b/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs
index f43a349..b9e32fe 100644
--- a/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs
+++ b/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.Test.OAuth2 {
using System;
using System.Collections.Generic;
using System.Linq;
+ using System.Security.Cryptography;
using System.Text;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
@@ -29,6 +30,8 @@ namespace DotNetOpenAuth.Test.OAuth2 {
protected static readonly Uri ClientCallback = new Uri("http://client/callback");
+ protected static readonly RSACryptoServiceProvider AsymmetricKey = new RSACryptoServiceProvider(512);
+
protected static readonly AuthorizationServerDescription AuthorizationServerDescription = new AuthorizationServerDescription {
AuthorizationEndpoint = new Uri("https://authserver/authorize"),
TokenEndpoint = new Uri("https://authserver/token"),
@@ -55,7 +58,7 @@ namespace DotNetOpenAuth.Test.OAuth2 {
MessagingUtilities.AreEquivalent(d.Scope, TestScopes)))).Returns(true);
string canonicalUserName = ResourceOwnerUsername;
authHostMock.Setup(m => m.TryAuthorizeResourceOwnerCredentialGrant(ResourceOwnerUsername, ResourceOwnerPassword, It.IsAny<IAccessTokenRequest>(), out canonicalUserName)).Returns(true);
- authHostMock.Setup(m => m.CreateAccessToken(It.IsAny<IAccessTokenRequest>())).Returns(new AccessTokenResult(new AuthorizationServerAccessToken()));
+ authHostMock.Setup(m => m.CreateAccessToken(It.IsAny<IAccessTokenRequest>())).Returns(new AccessTokenResult(new AuthorizationServerAccessToken() { AccessTokenSigningKey = AsymmetricKey }));
return authHostMock;
}
}
diff --git a/src/DotNetOpenAuth.Test/OAuth2/ResourceServerTests.cs b/src/DotNetOpenAuth.Test/OAuth2/ResourceServerTests.cs
new file mode 100644
index 0000000..a4d09de
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OAuth2/ResourceServerTests.cs
@@ -0,0 +1,110 @@
+//-----------------------------------------------------------------------
+// <copyright file="ResourceServerTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OAuth2 {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Linq;
+ using System.Security.Cryptography;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth2;
+ using DotNetOpenAuth.OAuth2.ChannelElements;
+ using DotNetOpenAuth.OAuth2.Messages;
+ using Moq;
+ using NUnit.Framework;
+
+ [TestFixture]
+ public class ResourceServerTests : OAuth2TestBase {
+ [Test]
+ public void GetAccessTokenWithMissingAccessToken() {
+ var resourceServer = new ResourceServer(new StandardAccessTokenAnalyzer(AsymmetricKey, null));
+
+ var requestHeaders = new NameValueCollection {
+ { "Authorization", "Bearer " },
+ };
+ var request = new HttpRequestInfo("GET", new Uri("http://localhost/resource"), headers: requestHeaders);
+ Assert.That(() => resourceServer.GetAccessToken(request), Throws.InstanceOf<ProtocolException>());
+ }
+
+ [Test]
+ public void GetPrincipalWithMissingAccessToken() {
+ var resourceServer = new ResourceServer(new StandardAccessTokenAnalyzer(AsymmetricKey, null));
+
+ var requestHeaders = new NameValueCollection {
+ { "Authorization", "Bearer " },
+ };
+ var request = new HttpRequestInfo("GET", new Uri("http://localhost/resource"), headers: requestHeaders);
+ Assert.That(() => resourceServer.GetPrincipal(request), Throws.InstanceOf<ProtocolException>());
+ }
+
+ [Test]
+ public void GetAccessTokenWithTotallyFakeToken() {
+ var resourceServer = new ResourceServer(new StandardAccessTokenAnalyzer(AsymmetricKey, null));
+
+ var requestHeaders = new NameValueCollection {
+ { "Authorization", "Bearer foobar" },
+ };
+ var request = new HttpRequestInfo("GET", new Uri("http://localhost/resource"), headers: requestHeaders);
+ Assert.That(() => resourceServer.GetAccessToken(request), Throws.InstanceOf<ProtocolException>());
+ }
+
+ [Test]
+ public void GetAccessTokenWithCorruptedToken() {
+ var accessToken = this.ObtainValidAccessToken();
+
+ var resourceServer = new ResourceServer(new StandardAccessTokenAnalyzer(AsymmetricKey, null));
+
+ var requestHeaders = new NameValueCollection {
+ { "Authorization", "Bearer " + accessToken.Substring(0, accessToken.Length - 1) + "zzz" },
+ };
+ var request = new HttpRequestInfo("GET", new Uri("http://localhost/resource"), headers: requestHeaders);
+ Assert.That(() => resourceServer.GetAccessToken(request), Throws.InstanceOf<ProtocolException>());
+ }
+
+ [Test]
+ public void GetAccessTokenWithValidToken() {
+ var accessToken = this.ObtainValidAccessToken();
+
+ var resourceServer = new ResourceServer(new StandardAccessTokenAnalyzer(AsymmetricKey, null));
+
+ var requestHeaders = new NameValueCollection {
+ { "Authorization", "Bearer " + accessToken },
+ };
+ var request = new HttpRequestInfo("GET", new Uri("http://localhost/resource"), headers: requestHeaders);
+ var resourceServerDecodedToken = resourceServer.GetAccessToken(request);
+ Assert.That(resourceServerDecodedToken, Is.Not.Null);
+ }
+
+ private string ObtainValidAccessToken() {
+ string accessToken = null;
+ var authServer = CreateAuthorizationServerMock();
+ authServer.Setup(
+ a => a.IsAuthorizationValid(It.Is<IAuthorizationDescription>(d => d.User == null && d.ClientIdentifier == ClientId && MessagingUtilities.AreEquivalent(d.Scope, TestScopes))))
+ .Returns(true);
+ authServer.Setup(
+ a => a.TryAuthorizeClientCredentialsGrant(It.Is<IAccessTokenRequest>(d => d.ClientIdentifier == ClientId && MessagingUtilities.AreEquivalent(d.Scope, TestScopes))))
+ .Returns(true);
+ var coordinator = new OAuth2Coordinator<WebServerClient>(
+ AuthorizationServerDescription,
+ authServer.Object,
+ new WebServerClient(AuthorizationServerDescription),
+ client => {
+ var authState = client.GetClientAccessToken(TestScopes);
+ Assert.That(authState.AccessToken, Is.Not.Null.And.Not.Empty);
+ Assert.That(authState.RefreshToken, Is.Null);
+ accessToken = authState.AccessToken;
+ },
+ server => {
+ server.HandleTokenRequest().Respond();
+ });
+ coordinator.Run();
+
+ return accessToken;
+ }
+ }
+}
diff --git a/src/version.txt b/src/version.txt
index 17ee6ea..53b95c0 100644
--- a/src/version.txt
+++ b/src/version.txt
@@ -1,3 +1,3 @@
-4.1.2
+4.1.5
-0.25.0-draft3
+0.25.0-draft5