summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOpenAuth')
-rw-r--r--src/DotNetOpenAuth/GlobalSuppressions.cs1
-rw-r--r--src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs14
-rw-r--r--src/DotNetOpenAuth/InfoCard/SupportingScript.js206
-rw-r--r--src/DotNetOpenAuth/InfoCard/Token/Token.cs5
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs44
-rw-r--r--src/DotNetOpenAuth/Messaging/ErrorUtilities.cs3
-rw-r--r--src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs4
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs11
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.resx3
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs38
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/IMessagePartEncoder.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs6
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs5
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs76
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs33
-rw-r--r--src/DotNetOpenAuth/OAuth/ConsumerBase.cs94
-rw-r--r--src/DotNetOpenAuth/OAuth/ServiceProvider.cs31
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs28
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs11
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs5
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs9
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs15
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs2
29 files changed, 444 insertions, 220 deletions
diff --git a/src/DotNetOpenAuth/GlobalSuppressions.cs b/src/DotNetOpenAuth/GlobalSuppressions.cs
index c4c6d22..8be62f8 100644
--- a/src/DotNetOpenAuth/GlobalSuppressions.cs
+++ b/src/DotNetOpenAuth/GlobalSuppressions.cs
@@ -42,3 +42,4 @@
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "We sign it when producing drops.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.OAuth")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.UI")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.Messaging.Reflection")]
diff --git a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
index b787300..949c12b 100644
--- a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
+++ b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
@@ -553,7 +553,7 @@ namespace DotNetOpenAuth.InfoCard {
// generate the onclick script for the image
string invokeScript = string.Format(
CultureInfo.InvariantCulture,
- @"if (ActivateSelector('{0}', '{1}')) {{ {2} }}",
+ @"if (document.infoCard.activate('{0}', '{1}')) {{ {2} }}",
this.SelectorObjectId,
this.HiddenFieldName,
postback);
@@ -587,22 +587,18 @@ namespace DotNetOpenAuth.InfoCard {
cardSpaceControl.Attributes.Add(HtmlTextWriterAttribute.Type.ToString(), "application/x-informationcard");
cardSpaceControl.Attributes.Add(HtmlTextWriterAttribute.Id.ToString(), this.ClientID + "_cs");
- // issuer
- if (this.Issuer != null) {
+ if (!string.IsNullOrEmpty(this.Issuer)) {
cardSpaceControl.Controls.Add(CreateParam("issuer", this.Issuer));
}
- // issuer policy
if (!string.IsNullOrEmpty(this.IssuerPolicy)) {
cardSpaceControl.Controls.Add(CreateParam("issuerPolicy", this.IssuerPolicy));
}
- // token type
if (!string.IsNullOrEmpty(this.TokenType)) {
cardSpaceControl.Controls.Add(CreateParam("tokenType", this.TokenType));
}
- // claims
string requiredClaims, optionalClaims;
this.GetRequestedClaims(out requiredClaims, out optionalClaims);
ErrorUtilities.VerifyArgument(!string.IsNullOrEmpty(requiredClaims) || !string.IsNullOrEmpty(optionalClaims), InfoCardStrings.EmptyClaimListNotAllowed);
@@ -613,12 +609,10 @@ namespace DotNetOpenAuth.InfoCard {
cardSpaceControl.Controls.Add(CreateParam("optionalClaims", optionalClaims));
}
- // privacy URL
if (!string.IsNullOrEmpty(this.PrivacyUrl)) {
cardSpaceControl.Controls.Add(CreateParam("privacyUrl", this.PrivacyUrl));
}
- // privacy version
if (!string.IsNullOrEmpty(this.PrivacyVersion)) {
cardSpaceControl.Controls.Add(CreateParam("privacyVersion", this.PrivacyVersion));
}
@@ -668,13 +662,13 @@ namespace DotNetOpenAuth.InfoCard {
this.Page.ClientScript.RegisterStartupScript(
typeof(InfoCardSelector),
"SelectorSupportingScript_" + this.ClientID,
- string.Format(CultureInfo.InvariantCulture, "CheckStatic('{0}', '{1}');", this.infoCardSupportedPanel.ClientID, this.infoCardNotSupportedPanel.ClientID),
+ string.Format(CultureInfo.InvariantCulture, "document.infoCard.checkStatic('{0}', '{1}');", this.infoCardSupportedPanel.ClientID, this.infoCardNotSupportedPanel.ClientID),
true);
} else if (RenderMode == RenderMode.Dynamic) {
this.Page.ClientScript.RegisterStartupScript(
typeof(InfoCardSelector),
"SelectorSupportingScript_" + this.ClientID,
- string.Format(CultureInfo.InvariantCulture, "CheckDynamic('{0}', '{1}');", this.infoCardSupportedPanel.ClientID, this.infoCardNotSupportedPanel.ClientID),
+ string.Format(CultureInfo.InvariantCulture, "document.infoCard.checkDynamic('{0}', '{1}');", this.infoCardSupportedPanel.ClientID, this.infoCardNotSupportedPanel.ClientID),
true);
}
}
diff --git a/src/DotNetOpenAuth/InfoCard/SupportingScript.js b/src/DotNetOpenAuth/InfoCard/SupportingScript.js
index 60103f5..ce4b02d 100644
--- a/src/DotNetOpenAuth/InfoCard/SupportingScript.js
+++ b/src/DotNetOpenAuth/InfoCard/SupportingScript.js
@@ -1,122 +1,122 @@
-function AreCardsSupported() {
- /// <summary>
- /// Determines if information cards are supported by the
- /// browser.
- /// </summary>
- /// <returns>
- /// true-if the browser supports information cards.
- ///</returns>
+document.infoCard = {
+ isSupported: function() {
+ /// <summary>
+ /// Determines if information cards are supported by the
+ /// browser.
+ /// </summary>
+ /// <returns>
+ /// true-if the browser supports information cards.
+ ///</returns>
- var IEVer = -1;
- if (navigator.appName == 'Microsoft Internet Explorer') {
- if (new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) != null) {
- IEVer = parseFloat(RegExp.$1);
+ var IEVer = -1;
+ if (navigator.appName == 'Microsoft Internet Explorer') {
+ if (new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) != null) {
+ IEVer = parseFloat(RegExp.$1);
+ }
}
- }
- // Look for IE 7+.
- if (IEVer >= 7) {
- var embed = document.createElement("object");
- embed.type = "application/x-informationcard";
- return "" + embed.issuerPolicy != "undefined" && embed.isInstalled;
- }
-
- // not IE (any version)
- if (IEVer < 0 && navigator.mimeTypes && navigator.mimeTypes.length) {
- // check to see if there is a mimeType handler.
- x = navigator.mimeTypes['application/x-informationcard'];
- if (x && x.enabledPlugin) {
- return true;
+ // Look for IE 7+.
+ if (IEVer >= 7) {
+ var embed = document.createElement("object");
+ embed.type = "application/x-informationcard";
+ return !(embed.issuerPolicy === undefined) && embed.isInstalled;
}
- // check for the IdentitySelector event handler is there.
- if (document.addEventListener) {
- var event = document.createEvent("Events");
- event.initEvent("IdentitySelectorAvailable", true, true);
- top.dispatchEvent(event);
-
- if (top.IdentitySelectorAvailable == true) {
+ // not IE (any version)
+ if (IEVer < 0 && navigator.mimeTypes && navigator.mimeTypes.length) {
+ // check to see if there is a mimeType handler.
+ x = navigator.mimeTypes['application/x-informationcard'];
+ if (x && x.enabledPlugin) {
return true;
}
- }
- }
-
- return false;
-}
-function ActivateSelector(selectorId, hiddenFieldName) {
- var selector = document.getElementById(selectorId);
- var hiddenField = document.getElementsByName(hiddenFieldName)[0];
- try {
- hiddenField.value = selector.value;
- } catch (e) {
- // Selector was canceled
- return false;
- }
- if (hiddenField.value == 'undefined') {
- // We're dealing with a bad FireFox selector plugin.
- // Just add the control to the form by setting its name property and submit to activate.
- selector.name = hiddenFieldName;
- hiddenField.parentNode.removeChild(hiddenField);
- return true;
- }
- return true;
-};
+ // check for the IdentitySelector event handler is there.
+ if (document.addEventListener) {
+ var event = document.createEvent("Events");
+ event.initEvent("IdentitySelectorAvailable", true, true);
+ top.dispatchEvent(event);
-function HideStatic(divName) {
- var div = document.getElementById(divName);
- if (div) {
- div.style.visibility = 'hidden';
- }
-}
+ if (top.IdentitySelectorAvailable == true) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ },
-function ShowStatic(divName) {
- var div = document.getElementById(divName);
- if (div) {
- div.style.visibility = 'visible';
- }
-}
+ activate: function(selectorId, hiddenFieldName) {
+ var selector = document.getElementById(selectorId);
+ var hiddenField = document.getElementsByName(hiddenFieldName)[0];
+ try {
+ hiddenField.value = selector.value;
+ } catch (e) {
+ // Selector was canceled
+ return false;
+ }
+ if (hiddenField.value == 'undefined') { // really the string, not === undefined
+ // We're dealing with a bad FireFox selector plugin.
+ // Just add the control to the form by setting its name property and submit to activate.
+ selector.name = hiddenFieldName;
+ hiddenField.parentNode.removeChild(hiddenField);
+ return true;
+ }
+ return true;
+ },
-function HideDynamic(divName) {
- var div = document.getElementById(divName);
- if (div) {
- div.style.display = 'none';
- }
-}
+ hideStatic: function (divName) {
+ var div = document.getElementById(divName);
+ if (div) {
+ div.style.visibility = 'hidden';
+ }
+ },
-function ShowDynamic(divName) {
- var div = document.getElementById(divName);
- if (div) {
- div.style.display = '';
- }
-}
+ showStatic: function(divName) {
+ var div = document.getElementById(divName);
+ if (div) {
+ div.style.visibility = 'visible';
+ }
+ },
-function CheckDynamic(controlDiv, unsupportedDiv) {
- if (AreCardsSupported()) {
- ShowDynamic(controlDiv);
- if (unsupportedDiv != '') {
- HideDynamic(unsupportedDiv);
+ hideDynamic: function (divName) {
+ var div = document.getElementById(divName);
+ if (div) {
+ div.style.display = 'none';
}
- }
- else {
- HideDynamic(controlDiv);
- if (unsupportedDiv != '') {
- ShowDynamic(unsupportedDiv);
+ },
+
+ showDynamic: function (divName) {
+ var div = document.getElementById(divName);
+ if (div) {
+ div.style.display = '';
}
- }
-}
+ },
-function CheckStatic(controlDiv, unsupportedDiv) {
- if (AreCardsSupported()) {
- ShowStatic(controlDiv);
- if (unsupportedDiv != '') {
- HideStatic(unsupportedDiv);
+ checkDynamic: function (controlDiv, unsupportedDiv) {
+ if (this.isSupported()) {
+ this.showDynamic(controlDiv);
+ if (unsupportedDiv != '') {
+ this.hideDynamic(unsupportedDiv);
+ }
+ } else {
+ this.hideDynamic(controlDiv);
+ if (unsupportedDiv != '') {
+ this.showDynamic(unsupportedDiv);
+ }
}
- }
- else {
- HideStatic(controlDiv);
- if (unsupportedDiv != '') {
- ShowDynamic(unsupportedDiv);
+ },
+
+ checkStatic: function(controlDiv, unsupportedDiv) {
+ if (this.isSupported()) {
+ this.showStatic(controlDiv);
+ if (unsupportedDiv != '') {
+ this.hideStatic(unsupportedDiv);
+ }
+ } else {
+ this.hideStatic(controlDiv);
+ if (unsupportedDiv != '') {
+ this.showDynamic(unsupportedDiv);
+ }
}
}
-} \ No newline at end of file
+};
diff --git a/src/DotNetOpenAuth/InfoCard/Token/Token.cs b/src/DotNetOpenAuth/InfoCard/Token/Token.cs
index f07c555..5c955ed 100644
--- a/src/DotNetOpenAuth/InfoCard/Token/Token.cs
+++ b/src/DotNetOpenAuth/InfoCard/Token/Token.cs
@@ -107,8 +107,9 @@ namespace DotNetOpenAuth.InfoCard {
public string SiteSpecificId {
get {
Contract.Requires(this.Claims.ContainsKey(ClaimTypes.PPID));
- ErrorUtilities.VerifyOperation(this.Claims.ContainsKey(ClaimTypes.PPID), InfoCardStrings.PpidClaimRequired);
- return TokenUtility.CalculateSiteSpecificID(this.Claims[ClaimTypes.PPID]);
+ string ppidValue;
+ ErrorUtilities.VerifyOperation(this.Claims.TryGetValue(ClaimTypes.PPID, out ppidValue) && ppidValue != null, InfoCardStrings.PpidClaimRequired);
+ return TokenUtility.CalculateSiteSpecificID(ppidValue);
}
}
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs
index 0813d17..dd6c02c 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -447,6 +447,24 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Checks whether a given HTTP method is expected to include an entity body in its request.
+ /// </summary>
+ /// <param name="httpMethod">The HTTP method.</param>
+ /// <returns><c>true</c> if the HTTP method is supposed to have an entity; <c>false</c> otherwise.</returns>
+ protected static bool HttpMethodHasEntity(string httpMethod) {
+ if (string.Equals(httpMethod, "GET", StringComparison.Ordinal) ||
+ string.Equals(httpMethod, "HEAD", StringComparison.Ordinal) ||
+ string.Equals(httpMethod, "DELETE", StringComparison.Ordinal)) {
+ return false;
+ } else if (string.Equals(httpMethod, "POST", StringComparison.Ordinal) ||
+ string.Equals(httpMethod, "PUT", StringComparison.Ordinal)) {
+ return true;
+ } else {
+ throw ErrorUtilities.ThrowArgumentNamed("httpMethod", MessagingStrings.UnsupportedHttpVerb, httpMethod);
+ }
+ }
+
+ /// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
@@ -810,11 +828,31 @@ namespace DotNetOpenAuth.Messaging {
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
httpRequest.CachePolicy = this.CachePolicy;
httpRequest.Method = "POST";
+ this.SendParametersInEntity(httpRequest, fields);
+
+ return httpRequest;
+ }
+
+ /// <summary>
+ /// Sends the given parameters in the entity stream of an HTTP request.
+ /// </summary>
+ /// <param name="httpRequest">The HTTP request.</param>
+ /// <param name="fields">The parameters to send.</param>
+ /// <remarks>
+ /// This method calls <see cref="HttpWebRequest.GetRequestStream()"/> and closes
+ /// the request stream, but does not call <see cref="HttpWebRequest.GetResponse"/>.
+ /// </remarks>
+ protected void SendParametersInEntity(HttpWebRequest httpRequest, IDictionary<string, string> fields) {
+ Contract.Requires(httpRequest != null);
+ Contract.Requires(fields != null);
+ ErrorUtilities.VerifyArgumentNotNull(httpRequest, "httpRequest");
+ ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
+
httpRequest.ContentType = "application/x-www-form-urlencoded";
// Setting the content-encoding to "utf-8" causes Google to reply
// with a 415 UnsupportedMediaType. But adding it doesn't buy us
- // anything specific, so it's disable it until we know how to get it right.
+ // anything specific, so we disable it until we know how to get it right.
////httpRequest.Headers[HttpRequestHeader.ContentEncoding] = PostEntityEncoding.WebName;
string requestBody = MessagingUtilities.CreateQueryString(fields);
@@ -831,8 +869,6 @@ namespace DotNetOpenAuth.Messaging {
requestStream.Dispose();
}
}
-
- return httpRequest;
}
/// <summary>
@@ -939,7 +975,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The properly ordered list of elements.</returns>
/// <exception cref="ProtocolException">Thrown when the binding elements are incomplete or inconsistent with each other.</exception>
private static IEnumerable<IChannelBindingElement> ValidateAndPrepareBindingElements(IEnumerable<IChannelBindingElement> elements) {
- Contract.Requires(elements == null || Contract.ForAll(elements, e => e != null));
+ Contract.Requires(elements == null || elements.All(e => e != null));
Contract.Ensures(Contract.Result<IEnumerable<IChannelBindingElement>>() != null);
if (elements == null) {
return new IChannelBindingElement[0];
diff --git a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
index 1c27d6c..d5b2040 100644
--- a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
@@ -379,7 +379,10 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static void VerifyHttpContext() {
Contract.Ensures(HttpContext.Current != null);
+ Contract.Ensures(HttpContext.Current.Request != null);
+
ErrorUtilities.VerifyOperation(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
+ ErrorUtilities.VerifyOperation(HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
}
}
}
diff --git a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
index e84a097..ecd6d44 100644
--- a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
+++ b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
@@ -10,6 +10,7 @@ namespace DotNetOpenAuth.Messaging {
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
+ using System.Globalization;
using System.IO;
using System.Net;
using System.ServiceModel.Channels;
@@ -280,6 +281,7 @@ namespace DotNetOpenAuth.Messaging {
this.queryStringBeforeRewriting = this.QueryString;
} else {
// Rewriting detected! Recover the original request URI.
+ ErrorUtilities.VerifyInternal(this.UrlBeforeRewriting != null, "UrlBeforeRewriting is null, so the query string cannot be determined.");
this.queryStringBeforeRewriting = HttpUtility.ParseQueryString(this.UrlBeforeRewriting.Query);
}
}
@@ -336,7 +338,7 @@ namespace DotNetOpenAuth.Messaging {
string[] hostAndPort = request.ServerVariables["HTTP_HOST"].Split(new[] { ':' }, 2);
publicRequestUri.Host = hostAndPort[0];
if (hostAndPort.Length > 1) {
- publicRequestUri.Port = Convert.ToInt32(hostAndPort[1]);
+ publicRequestUri.Port = Convert.ToInt32(hostAndPort[1], CultureInfo.InvariantCulture);
} else {
publicRequestUri.Port = publicRequestUri.Scheme == Uri.UriSchemeHttps ? 443 : 80;
}
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
index fb8620b..13e83e1 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:2.0.50727.3521
+// Runtime Version:2.0.50727.4918
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -547,6 +547,15 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Looks up a localized string similar to The HTTP verb &apos;{0}&apos; is unrecognized and unsupported..
+ /// </summary>
+ internal static string UnsupportedHttpVerb {
+ get {
+ return ResourceManager.GetString("UnsupportedHttpVerb", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to &apos;{0}&apos; messages cannot be received with HTTP verb &apos;{1}&apos;..
/// </summary>
internal static string UnsupportedHttpVerbForMessageType {
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
index 0a6f6a5..4bc92a7 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
@@ -288,4 +288,7 @@
<data name="UnexpectedHttpStatusCode" xml:space="preserve">
<value>Expected direct response to use HTTP status code {0} but was {1} instead.</value>
</data>
+ <data name="UnsupportedHttpVerb" xml:space="preserve">
+ <value>The HTTP verb '{0}' is unrecognized and unsupported.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index fe1e4e8..10ddba0 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -31,6 +31,11 @@ namespace DotNetOpenAuth.Messaging {
internal static readonly RandomNumberGenerator CryptoRandomDataGenerator = new RNGCryptoServiceProvider();
/// <summary>
+ /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986.
+ /// </summary>
+ private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
+
+ /// <summary>
/// A set of escaping mappings that help secure a string from javscript execution.
/// </summary>
/// <remarks>
@@ -445,7 +450,7 @@ namespace DotNetOpenAuth.Messaging {
/// <summary>
/// Concatenates a list of name-value pairs as key=value&amp;key=value,
/// taking care to properly encode each key and value for URL
- /// transmission. No ? is prefixed to the string.
+ /// transmission according to RFC 3986. No ? is prefixed to the string.
/// </summary>
/// <param name="args">The dictionary of key/values to read from.</param>
/// <returns>The formulated querystring style string.</returns>
@@ -461,9 +466,9 @@ namespace DotNetOpenAuth.Messaging {
foreach (var p in args) {
ErrorUtilities.VerifyArgument(!string.IsNullOrEmpty(p.Key), MessagingStrings.UnexpectedNullOrEmptyKey);
ErrorUtilities.VerifyArgument(p.Value != null, MessagingStrings.UnexpectedNullValue, p.Key);
- sb.Append(HttpUtility.UrlEncode(p.Key));
+ sb.Append(EscapeUriDataStringRfc3986(p.Key));
sb.Append('=');
- sb.Append(HttpUtility.UrlEncode(p.Value));
+ sb.Append(EscapeUriDataStringRfc3986(p.Value));
sb.Append('&');
}
sb.Length--; // remove trailing &
@@ -689,6 +694,33 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Escapes a string according to the URI data string rules given in RFC 3986.
+ /// </summary>
+ /// <param name="value">The value to escape.</param>
+ /// <returns>The escaped value.</returns>
+ /// <remarks>
+ /// The <see cref="Uri.EscapeDataString"/> method is <i>supposed</i> to take on
+ /// RFC 3986 behavior if certain elements are present in a .config file. Even if this
+ /// actually worked (which in my experiments it <i>doesn't</i>), we can't rely on every
+ /// host actually having this configuration element present.
+ /// </remarks>
+ internal static string EscapeUriDataStringRfc3986(string value) {
+ // Start with RFC 2396 escaping by calling the .NET method to do the work.
+ // This MAY sometimes exhibit RFC 3986 behavior (according to the documentation).
+ // If it does, the escaping we do that follows it will be a no-op since the
+ // characters we search for to replace can't possibly exist in the string.
+ StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));
+
+ // Upgrade the escaping to RFC 3986, if necessary.
+ for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++) {
+ escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
+ }
+
+ // Return the fully-RFC3986-escaped string.
+ return escaped.ToString();
+ }
+
+ /// <summary>
/// A class to convert a <see cref="Comparison&lt;T&gt;"/> into an <see cref="IComparer&lt;T&gt;"/>.
/// </summary>
/// <typeparam name="T">The type of objects being compared.</typeparam>
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartEncoder.cs b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartEncoder.cs
index 7da25a5..d2c9596 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartEncoder.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartEncoder.cs
@@ -16,7 +16,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <remarks>
/// Implementations of this interface must include a default constructor and must be thread-safe.
/// </remarks>
- internal interface IMessagePartEncoder {
+ public interface IMessagePartEncoder {
/// <summary>
/// Encodes the specified value.
/// </summary>
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
index 7ac21f2..9af4bdf 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Messaging.Reflection {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
/// <summary>
@@ -45,11 +46,12 @@ namespace DotNetOpenAuth.Messaging.Reflection {
if (!this.reflectedMessageTypes.TryGetValue(key, out result)) {
lock (this.reflectedMessageTypes) {
if (!this.reflectedMessageTypes.TryGetValue(key, out result)) {
- this.reflectedMessageTypes[key] = result = new MessageDescription(key.Type, key.Version);
+ this.reflectedMessageTypes[key] = result = new MessageDescription(messageType, messageVersion);
}
}
}
+ Contract.Assume(result != null, "We should never assign null values to this dictionary.");
return result;
}
@@ -112,6 +114,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <summary>
/// Gets the message type.
/// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Exposes basic identity on the type.")]
internal Type Type {
get { return this.type; }
}
@@ -119,6 +122,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <summary>
/// Gets the message version.
/// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Exposes basic identity on the type.")]
internal Version Version {
get { return this.version; }
}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
index b721b63..0b5b6d0 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
@@ -95,7 +95,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
}
/// <summary>
- /// Gets the set of official OAuth keys that have non-null values associated with them.
+ /// Gets the set of official message part names that have non-null values associated with them.
/// </summary>
public ICollection<string> DeclaredKeys {
get {
diff --git a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
index 4adf3a7..733b698 100644
--- a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
@@ -33,11 +33,6 @@ namespace DotNetOpenAuth.Messaging {
/// </remarks>
public class UntrustedWebRequestHandler : IDirectWebRequestHandler {
/// <summary>
- /// Gets or sets the default cache policy to use for HTTP requests.
- /// </summary>
- internal static readonly RequestCachePolicy DefaultCachePolicy = HttpWebRequest.DefaultCachePolicy;
-
- /// <summary>
/// The set of URI schemes allowed in untrusted web requests.
/// </summary>
private ICollection<string> allowableSchemes = new List<string> { "http", "https" };
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
index b18b809..dcd4494 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
@@ -112,11 +112,13 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// a protocol request message.
/// </summary>
/// <param name="request">The HTTP request to search.</param>
- /// <returns>A dictionary of data in the request. Should never be null, but may be empty.</returns>
+ /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestInfo request) {
ErrorUtilities.VerifyArgumentNotNull(request, "request");
- // First search the Authorization header. Use it exclusively if it's present.
+ var fields = new Dictionary<string, string>();
+
+ // First search the Authorization header.
string authorization = request.Headers[HttpRequestHeader.Authorization];
if (authorization != null) {
string[] authorizationSections = authorization.Split(';'); // TODO: is this the right delimiter?
@@ -129,21 +131,28 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
if (trimmedAuth.StartsWith(oauthPrefix, StringComparison.Ordinal)) {
// We found an Authorization: OAuth header.
// Parse it according to the rules in section 5.4.1 of the V1.0 spec.
- var fields = new Dictionary<string, string>();
foreach (string stringPair in trimmedAuth.Substring(oauthPrefix.Length).Split(',')) {
string[] keyValueStringPair = stringPair.Trim().Split('=');
string key = Uri.UnescapeDataString(keyValueStringPair[0]);
string value = Uri.UnescapeDataString(keyValueStringPair[1].Trim('"'));
fields.Add(key, value);
}
-
- return (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
}
}
}
- // We didn't find an OAuth authorization header. Revert to other payload methods.
- IDirectedProtocolMessage message = base.ReadFromRequestCore(request);
+ // Scrape the entity
+ foreach (string key in request.Form) {
+ fields.Add(key, request.Form[key]);
+ }
+
+ // Scrape the query string
+ foreach (string key in request.QueryStringBeforeRewriting) {
+ fields.Add(key, request.QueryStringBeforeRewriting[key]);
+ }
+
+ // Deserialize the message using all the data we've collected.
+ var message = (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
// Add receiving HTTP transport information required for signature generation.
var signedMessage = message as ITamperResistantOAuthMessage;
@@ -239,19 +248,38 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
ErrorUtilities.VerifyArgumentNotNull(destination, "destination");
foreach (var pair in source) {
- var key = Uri.EscapeDataString(pair.Key);
- var value = Uri.EscapeDataString(pair.Value);
+ var key = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Key);
+ var value = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Value);
destination.Add(key, value);
}
}
/// <summary>
+ /// Gets the HTTP method to use for a message.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>"POST", "GET" or some other similar http verb.</returns>
+ private static string GetHttpMethod(IDirectedProtocolMessage message) {
+ Contract.Requires(message != null);
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+
+ var signedMessage = message as ITamperResistantOAuthMessage;
+ if (signedMessage != null) {
+ return signedMessage.HttpMethod;
+ } else {
+ return (message.HttpMethods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET";
+ }
+ }
+
+ /// <summary>
/// Prepares to send a request to the Service Provider via the Authorization header.
/// </summary>
/// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
/// <returns>The web request ready to send.</returns>
/// <remarks>
- /// This method implements OAuth 1.0 section 5.2, item #1 (described in section 5.4).
+ /// <para>If the message has non-empty ExtraData in it, the request stream is sent to
+ /// the server automatically. If it is empty, the request stream must be sent by the caller.</para>
+ /// <para>This method implements OAuth 1.0 section 5.2, item #1 (described in section 5.4).</para>
/// </remarks>
private HttpWebRequest InitializeRequestAsAuthHeader(IDirectedProtocolMessage requestMessage) {
var protocol = Protocol.Lookup(requestMessage.Version);
@@ -266,16 +294,22 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
fields.Add("realm", this.Realm.AbsoluteUri);
}
- UriBuilder builder = new UriBuilder(requestMessage.Recipient);
- MessagingUtilities.AppendQueryArgs(builder, requestMessage.ExtraData);
- HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(builder.Uri);
+ HttpWebRequest httpRequest;
+ UriBuilder recipientBuilder = new UriBuilder(requestMessage.Recipient);
+ bool hasEntity = HttpMethodHasEntity(GetHttpMethod(requestMessage));
+
+ if (!hasEntity) {
+ MessagingUtilities.AppendQueryArgs(recipientBuilder, requestMessage.ExtraData);
+ }
+ httpRequest = (HttpWebRequest)WebRequest.Create(recipientBuilder.Uri);
+ httpRequest.Method = GetHttpMethod(requestMessage);
StringBuilder authorization = new StringBuilder();
authorization.Append(protocol.AuthorizationHeaderScheme);
authorization.Append(" ");
foreach (var pair in fields) {
- string key = Uri.EscapeDataString(pair.Key);
- string value = Uri.EscapeDataString(pair.Value);
+ string key = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Key);
+ string value = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Value);
authorization.Append(key);
authorization.Append("=\"");
authorization.Append(value);
@@ -285,6 +319,18 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
httpRequest.Headers.Add(HttpRequestHeader.Authorization, authorization.ToString());
+ if (hasEntity) {
+ // WARNING: We only set up the request stream for the caller if there is
+ // extra data. If there isn't any extra data, the caller must do this themselves.
+ if (requestMessage.ExtraData.Count > 0) {
+ SendParametersInEntity(httpRequest, requestMessage.ExtraData);
+ } else {
+ // We'll assume the content length is zero since the caller may not have
+ // anything. They're responsible to change it when the add the payload if they have one.
+ httpRequest.ContentLength = 0;
+ }
+ }
+
return httpRequest;
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
index d5ba346..6991818 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
@@ -7,9 +7,11 @@
namespace DotNetOpenAuth.OAuth.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Collections.Specialized;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
+ using System.Web;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.Messaging.Reflection;
@@ -164,13 +166,34 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
signatureBaseStringElements.Add(message.HttpMethod.ToUpperInvariant());
+ var encodedDictionary = OAuthChannel.GetUriEscapedParameters(messageDictionary);
+
+ // An incoming message will already have included the query and form parameters
+ // in the message dictionary, but an outgoing message COULD have SOME parameters
+ // in the query that are not in the message dictionary because they were included
+ // in the receiving endpoint (the original URL).
+ // In an outgoing message, the POST entity can only contain parameters if they were
+ // in the message dictionary, so no need to pull out any parameters from there.
+ if (message.Recipient.Query != null) {
+ NameValueCollection nvc = HttpUtility.ParseQueryString(message.Recipient.Query);
+ foreach (string key in nvc) {
+ string escapedKey = MessagingUtilities.EscapeUriDataStringRfc3986(key);
+ string escapedValue = MessagingUtilities.EscapeUriDataStringRfc3986(nvc[key]);
+ string existingValue;
+ if (!encodedDictionary.TryGetValue(escapedKey, out existingValue)) {
+ encodedDictionary.Add(escapedKey, escapedValue);
+ } else {
+ ErrorUtilities.VerifyInternal(escapedValue == existingValue, "Somehow we have conflicting values for the '{0}' parameter.", escapedKey);
+ }
+ }
+ }
+ encodedDictionary.Remove("oauth_signature");
+
UriBuilder endpoint = new UriBuilder(message.Recipient);
endpoint.Query = null;
endpoint.Fragment = null;
signatureBaseStringElements.Add(endpoint.Uri.AbsoluteUri);
- var encodedDictionary = OAuthChannel.GetUriEscapedParameters(messageDictionary);
- encodedDictionary.Remove("oauth_signature");
var sortedKeyValueList = new List<KeyValuePair<string, string>>(encodedDictionary);
sortedKeyValueList.Sort(SignatureBaseStringParameterComparer);
StringBuilder paramBuilder = new StringBuilder();
@@ -192,7 +215,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
signatureBaseString.Append("&");
}
- signatureBaseString.Append(Uri.EscapeDataString(element));
+ signatureBaseString.Append(MessagingUtilities.EscapeUriDataStringRfc3986(element));
}
Logger.Bindings.DebugFormat("Constructed signature base string: {0}", signatureBaseString);
@@ -207,11 +230,11 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
protected static string GetConsumerAndTokenSecretString(ITamperResistantOAuthMessage message) {
StringBuilder builder = new StringBuilder();
if (!string.IsNullOrEmpty(message.ConsumerSecret)) {
- builder.Append(Uri.EscapeDataString(message.ConsumerSecret));
+ builder.Append(MessagingUtilities.EscapeUriDataStringRfc3986(message.ConsumerSecret));
}
builder.Append("&");
if (!string.IsNullOrEmpty(message.TokenSecret)) {
- builder.Append(Uri.EscapeDataString(message.TokenSecret));
+ builder.Append(MessagingUtilities.EscapeUriDataStringRfc3986(message.TokenSecret));
}
return builder.ToString();
}
diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
index 58f8f60..3abb794 100644
--- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
+++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
@@ -72,13 +72,63 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
/// <param name="accessToken">The access token that permits access to the protected resource.</param>
/// <returns>The initialized WebRequest object.</returns>
- public WebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken) {
+ public HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken) {
+ Contract.Requires(endpoint != null);
+ Contract.Requires(!String.IsNullOrEmpty(accessToken));
+ ErrorUtilities.VerifyArgumentNotNull(endpoint, "endpoint");
+ ErrorUtilities.VerifyNonZeroLength(accessToken, "accessToken");
+
+ return this.PrepareAuthorizedRequest(endpoint, accessToken, EmptyDictionary<string, string>.Instance);
+ }
+
+ /// <summary>
+ /// Creates a web request prepared with OAuth authorization
+ /// that may be further tailored by adding parameters by the caller.
+ /// </summary>
+ /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
+ /// <param name="accessToken">The access token that permits access to the protected resource.</param>
+ /// <param name="extraData">Extra parameters to include in the message. Must not be null, but may be empty.</param>
+ /// <returns>The initialized WebRequest object.</returns>
+ public HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken, IDictionary<string, string> extraData) {
+ Contract.Requires(endpoint != null);
+ Contract.Requires(!String.IsNullOrEmpty(accessToken));
+ Contract.Requires(extraData != null);
+ ErrorUtilities.VerifyArgumentNotNull(endpoint, "endpoint");
+ ErrorUtilities.VerifyNonZeroLength(accessToken, "accessToken");
+ ErrorUtilities.VerifyArgumentNotNull(extraData, "extraData");
+
IDirectedProtocolMessage message = this.CreateAuthorizingMessage(endpoint, accessToken);
+ foreach (var pair in extraData) {
+ message.ExtraData.Add(pair);
+ }
+
HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message);
return wr;
}
/// <summary>
+ /// Prepares an HTTP request that has OAuth authorization already attached to it.
+ /// </summary>
+ /// <param name="message">The OAuth authorization message to attach to the HTTP request.</param>
+ /// <returns>
+ /// The HttpWebRequest that can be used to send the HTTP request to the remote service provider.
+ /// </returns>
+ /// <remarks>
+ /// If <see cref="IDirectedProtocolMessage.HttpMethods"/> property on the
+ /// <paramref name="message"/> has the
+ /// <see cref="HttpDeliveryMethods.AuthorizationHeaderRequest"/> flag set and
+ /// <see cref="ITamperResistantOAuthMessage.HttpMethod"/> is set to an HTTP method
+ /// that includes an entity body, the request stream is automatically sent
+ /// if and only if the <see cref="IMessage.ExtraData"/> dictionary is non-empty.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Type of parameter forces the method to apply only to specific scenario.")]
+ public HttpWebRequest PrepareAuthorizedRequest(AccessProtectedResourceRequest message) {
+ Contract.Requires(message != null);
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ return this.OAuthChannel.InitializeRequest(message);
+ }
+
+ /// <summary>
/// Creates a web request prepared with OAuth authorization
/// that may be further tailored by adding parameters by the caller.
/// </summary>
@@ -105,6 +155,27 @@ namespace DotNetOpenAuth.OAuth {
#endregion
/// <summary>
+ /// Creates a web request prepared with OAuth authorization
+ /// that may be further tailored by adding parameters by the caller.
+ /// </summary>
+ /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
+ /// <param name="accessToken">The access token that permits access to the protected resource.</param>
+ /// <returns>The initialized WebRequest object.</returns>
+ protected internal AccessProtectedResourceRequest CreateAuthorizingMessage(MessageReceivingEndpoint endpoint, string accessToken) {
+ Contract.Requires(endpoint != null);
+ Contract.Requires(!String.IsNullOrEmpty(accessToken));
+ ErrorUtilities.VerifyArgumentNotNull(endpoint, "endpoint");
+ ErrorUtilities.VerifyNonZeroLength(accessToken, "accessToken");
+
+ AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint) {
+ AccessToken = accessToken,
+ ConsumerKey = this.ConsumerKey,
+ };
+
+ return message;
+ }
+
+ /// <summary>
/// Prepares an OAuth message that begins an authorization request that will
/// redirect the user to the Service Provider to provide that authorization.
/// </summary>
@@ -139,27 +210,6 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
- /// Creates a web request prepared with OAuth authorization
- /// that may be further tailored by adding parameters by the caller.
- /// </summary>
- /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
- /// <param name="accessToken">The access token that permits access to the protected resource.</param>
- /// <returns>The initialized WebRequest object.</returns>
- protected internal AccessProtectedResourceRequest CreateAuthorizingMessage(MessageReceivingEndpoint endpoint, string accessToken) {
- Contract.Requires(endpoint != null);
- Contract.Requires(!String.IsNullOrEmpty(accessToken));
- ErrorUtilities.VerifyArgumentNotNull(endpoint, "endpoint");
- ErrorUtilities.VerifyNonZeroLength(accessToken, "accessToken");
-
- AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint) {
- AccessToken = accessToken,
- ConsumerKey = this.ConsumerKey,
- };
-
- return message;
- }
-
- /// <summary>
/// Exchanges a given request token for access token.
/// </summary>
/// <param name="requestToken">The request token that the user has authorized.</param>
diff --git a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
index 78ee252..886e5b3 100644
--- a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
+++ b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
@@ -271,23 +271,44 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="request">The Consumer's original authorization request.</param>
/// <returns>
/// The message to send to the Consumer using <see cref="Channel"/> if one is necessary.
- /// Null if the Consumer did not request a callback.
+ /// Null if the Consumer did not request a callback as part of the authorization request.
/// </returns>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Consistent user experience with instance.")]
public UserAuthorizationResponse PrepareAuthorizationResponse(UserAuthorizationRequest request) {
+ Contract.Requires(request != null);
ErrorUtilities.VerifyArgumentNotNull(request, "request");
if (request.Callback != null) {
- var authorization = new UserAuthorizationResponse(request.Callback) {
- RequestToken = request.RequestToken,
- };
- return authorization;
+ return this.PrepareAuthorizationResponse(request, request.Callback);
} else {
return null;
}
}
/// <summary>
+ /// Prepares the message to send back to the consumer following proper authorization of
+ /// a token by an interactive user at the Service Provider's web site.
+ /// </summary>
+ /// <param name="request">The Consumer's original authorization request.</param>
+ /// <param name="callback">The callback URI the consumer has previously registered
+ /// with this service provider.</param>
+ /// <returns>
+ /// The message to send to the Consumer using <see cref="Channel"/>.
+ /// </returns>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Consistent user experience with instance.")]
+ public UserAuthorizationResponse PrepareAuthorizationResponse(UserAuthorizationRequest request, Uri callback) {
+ Contract.Requires(request != null);
+ Contract.Requires(callback != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ ErrorUtilities.VerifyArgumentNotNull(callback, "callback");
+
+ var authorization = new UserAuthorizationResponse(request.Callback) {
+ RequestToken = request.RequestToken,
+ };
+ return authorization;
+ }
+
+ /// <summary>
/// Gets the incoming request to exchange an authorized token for an access token.
/// </summary>
/// <returns>The incoming request, or null if no OAuth message was attached.</returns>
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs
index f23da51..de9cda9 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs
@@ -20,13 +20,13 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// The name of the callback parameter that stores the Provider Endpoint URL
/// to tack onto the return_to URI.
/// </summary>
- private const string ProviderEndpointParameterName = "dnoi.op_endpoint";
+ private const string ProviderEndpointParameterName = OpenIdUtilities.CustomParameterPrefix + "op_endpoint";
/// <summary>
/// The name of the callback parameter that stores the Claimed Identifier
/// to tack onto the return_to URI.
/// </summary>
- private const string ClaimedIdentifierParameterName = "dnoi.claimed_id";
+ private const string ClaimedIdentifierParameterName = OpenIdUtilities.CustomParameterPrefix + "claimed_id";
#region IChannelBindingElement Members
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
index cdb571b..d9c244f 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
@@ -23,39 +23,13 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// </summary>
internal class ExtensionsBindingElement : IChannelBindingElement {
/// <summary>
- /// The security settings on the Relying Party that is hosting this binding element.
- /// </summary>
- private RelyingPartySecuritySettings rpSecuritySettings;
-
- /// <summary>
- /// The security settings on the Provider that is hosting this binding element.
- /// </summary>
- private ProviderSecuritySettings opSecuritySettings;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ExtensionsBindingElement"/> class.
- /// </summary>
- /// <param name="extensionFactory">The extension factory.</param>
- /// <param name="securitySettings">The security settings to apply.</param>
- internal ExtensionsBindingElement(IOpenIdExtensionFactory extensionFactory, RelyingPartySecuritySettings securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(extensionFactory, "extensionFactory");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
-
- this.ExtensionFactory = extensionFactory;
- this.rpSecuritySettings = securitySettings;
- }
-
- /// <summary>
/// Initializes a new instance of the <see cref="ExtensionsBindingElement"/> class.
/// </summary>
/// <param name="extensionFactory">The extension factory.</param>
- /// <param name="securitySettings">The security settings to apply.</param>
- internal ExtensionsBindingElement(IOpenIdExtensionFactory extensionFactory, ProviderSecuritySettings securitySettings) {
+ internal ExtensionsBindingElement(IOpenIdExtensionFactory extensionFactory) {
ErrorUtilities.VerifyArgumentNotNull(extensionFactory, "extensionFactory");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
this.ExtensionFactory = extensionFactory;
- this.opSecuritySettings = securitySettings;
}
#region IChannelBindingElement Members
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
index c7d4ac7..1efcdf2 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
@@ -339,9 +339,9 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
var extensionFactory = OpenIdExtensionFactoryAggregator.LoadFromConfiguration();
List<IChannelBindingElement> elements = new List<IChannelBindingElement>(8);
+ elements.Add(new ExtensionsBindingElement(extensionFactory));
if (isRelyingPartyRole) {
elements.Add(new RelyingPartySecurityOptions(rpSecuritySettings));
- elements.Add(new ExtensionsBindingElement(extensionFactory, rpSecuritySettings));
elements.Add(new BackwardCompatibilityBindingElement());
ReturnToNonceBindingElement requestNonceElement = null;
@@ -361,8 +361,6 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
ErrorUtilities.VerifyOperation(!rpSecuritySettings.RejectUnsolicitedAssertions || requestNonceElement != null, OpenIdStrings.UnsolicitedAssertionRejectionRequiresNonceStore);
} else {
- elements.Add(new ExtensionsBindingElement(extensionFactory, opSecuritySettings));
-
// Providers must always have a nonce store.
ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
index bd9c28b..9c4c46d 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
@@ -50,7 +50,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// The parameter of the callback parameter we tack onto the return_to URL
/// to store the replay-detection nonce.
/// </summary>
- internal const string NonceParameter = "dnoi.request_nonce";
+ internal const string NonceParameter = OpenIdUtilities.CustomParameterPrefix + "request_nonce";
/// <summary>
/// The length of the generated nonce's random part.
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
index 75d86cf..a760c0d 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
@@ -33,13 +33,13 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// The name of the callback parameter we'll tack onto the return_to value
/// to store our signature on the return_to parameter.
/// </summary>
- private const string ReturnToSignatureParameterName = "dnoi.return_to_sig";
+ private const string ReturnToSignatureParameterName = OpenIdUtilities.CustomParameterPrefix + "return_to_sig";
/// <summary>
/// The name of the callback parameter we'll tack onto the return_to value
/// to store the handle of the association we use to sign the return_to parameter.
/// </summary>
- private const string ReturnToSignatureHandleParameterName = "dnoi.return_to_sig_handle";
+ private const string ReturnToSignatureHandleParameterName = OpenIdUtilities.CustomParameterPrefix + "return_to_sig_handle";
/// <summary>
/// The hashing algorithm used to generate the private signature on the return_to parameter.
diff --git a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
index d35dcd1..16d8f74 100644
--- a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
+++ b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using DotNetOpenAuth.Messaging;
@@ -148,13 +149,19 @@ namespace DotNetOpenAuth.OpenId {
ErrorUtilities.VerifyNonZeroLength(associationType, "associationType");
ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ int secretLength = GetSecretLength(protocol, associationType);
+
// Generate the handle. It must be unique, and preferably unpredictable,
// so we use a time element and a random data element to generate it.
string uniq = MessagingUtilities.GetCryptoRandomDataAsBase64(4);
- string handle = "{" + associationType + "}{" + DateTime.UtcNow.Ticks + "}{" + uniq + "}";
+ string handle = string.Format(
+ CultureInfo.InvariantCulture,
+ "{{{0}}}{{{1}}}{{{2}}}",
+ DateTime.UtcNow.Ticks,
+ uniq,
+ secretLength);
// Generate the secret that will be used for signing
- int secretLength = GetSecretLength(protocol, associationType);
byte[] secret = MessagingUtilities.GetCryptoRandomData(secretLength);
TimeSpan lifetime;
diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
index f9f638a..6a5c0a8 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
@@ -40,7 +40,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// </summary>
/// <value>Value: A valid association session type from Section 8.4 (Association Session Types). </value>
/// <remarks>Note: Unless using transport layer encryption, "no-encryption" MUST NOT be used. </remarks>
- [MessagePart("openid.session_type", IsRequired = true, AllowEmpty = true)]
+ [MessagePart("openid.session_type", IsRequired = false, AllowEmpty = true)]
[MessagePart("openid.session_type", IsRequired = true, AllowEmpty = false, MinVersion = "2.0")]
internal string SessionType { get; set; }
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
index f09bbe1..3cee968 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
@@ -24,6 +24,11 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
internal static class OpenIdUtilities {
/// <summary>
+ /// The prefix to designate this library's proprietary parameters added to the protocol.
+ /// </summary>
+ internal const string CustomParameterPrefix = "dnoa.";
+
+ /// <summary>
/// Gets the OpenID protocol instance for the version in a message.
/// </summary>
/// <param name="message">The message.</param>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
index d806eeb..b3a9020 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
@@ -24,7 +24,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// The name of the internal callback parameter to use to store the user-supplied identifier.
/// </summary>
- internal const string UserSuppliedIdentifierParameterName = "dnoi.userSuppliedIdentifier";
+ internal const string UserSuppliedIdentifierParameterName = OpenIdUtilities.CustomParameterPrefix + "userSuppliedIdentifier";
/// <summary>
/// The relying party that created this request object.
@@ -250,6 +250,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
Contract.Requires(userSuppliedIdentifier != null);
Contract.Requires(relyingParty != null);
Contract.Requires(realm != null);
+ Contract.Ensures(Contract.Result<IEnumerable<AuthenticationRequest>>() != null);
// We have a long data validation and preparation process
ErrorUtilities.VerifyArgumentNotNull(userSuppliedIdentifier, "userSuppliedIdentifier");
@@ -323,6 +324,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// before calling this method.
/// </remarks>
private static IEnumerable<AuthenticationRequest> CreateInternal(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, IEnumerable<ServiceEndpoint> serviceEndpoints, bool createNewAssociationsAsNeeded) {
+ Contract.Requires(userSuppliedIdentifier != null);
+ Contract.Requires(relyingParty != null);
+ Contract.Requires(realm != null);
+ Contract.Requires(serviceEndpoints != null);
+ Contract.Ensures(Contract.Result<IEnumerable<AuthenticationRequest>>() != null);
+
Logger.Yadis.InfoFormat("Performing discovery on user-supplied identifier: {0}", userSuppliedIdentifier);
IEnumerable<ServiceEndpoint> endpoints = FilterAndSortEndpoints(serviceEndpoints, relyingParty);
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
index 0b400da..1c82098 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
+ using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web;
@@ -511,7 +512,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
internal static bool IsOpenIdSupportingParameter(string parameterName) {
Protocol protocol = Protocol.Default;
return parameterName.StartsWith(protocol.openid.Prefix, StringComparison.OrdinalIgnoreCase)
- || parameterName.StartsWith("dnoi.", StringComparison.Ordinal);
+ || parameterName.StartsWith(OpenIdUtilities.CustomParameterPrefix, StringComparison.Ordinal);
}
/// <summary>
@@ -529,6 +530,18 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
return rp;
}
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(this.SecuritySettings != null);
+ Contract.Invariant(this.Channel != null);
+ }
+#endif
+
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
index 4cccc3d..a24b968 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
@@ -982,7 +982,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Enables a server control to perform final clean up before it is released from memory.
/// </summary>
- [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Poor base class implementation mandates that we call its Dispose() method.")]
+ [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Base class doesn't implement virtual Dispose(bool), so we must call its Dispose() method.")]
public sealed override void Dispose() {
this.Dispose(true);
base.Dispose();