summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOpenAuth')
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj6
-rw-r--r--src/DotNetOpenAuth/GlobalSuppressions.cs1
-rw-r--r--src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs53
-rw-r--r--src/DotNetOpenAuth/InfoCard/InfoCardStrings.Designer.cs11
-rw-r--r--src/DotNetOpenAuth/InfoCard/InfoCardStrings.resx3
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs8
-rw-r--r--src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs91
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs19
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs33
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/IOpenIdOAuthTokenManager.cs30
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth/ConsumerBase.cs4
-rw-r--r--src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs18
-rw-r--r--src/DotNetOpenAuth/OAuth/OAuthStrings.resx6
-rw-r--r--src/DotNetOpenAuth/OAuth/ServiceProvider.cs71
-rw-r--r--src/DotNetOpenAuth/OAuth/WebConsumer.cs71
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs133
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs10
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreResponse.cs10
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs21
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationApprovedResponse.cs48
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationDeclinedResponse.cs34
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationRequest.cs57
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/OAuth/Constants.cs22
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs10
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs10
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/StandardOpenIdExtensionFactory.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs9
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs14
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs17
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.resx13
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs75
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs75
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs73
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs75
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs78
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs14
-rw-r--r--src/DotNetOpenAuth/Yadis/Yadis.cs4
46 files changed, 1134 insertions, 122 deletions
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index 5dad6f0..309b731 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -229,8 +229,10 @@
<Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" />
<Compile Include="Messaging\Reflection\IMessagePartNullEncoder.cs" />
<Compile Include="Messaging\Reflection\MessageDescriptionCollection.cs" />
+ <Compile Include="OAuth\ChannelElements\ICombinedOpenIdProviderTokenManager.cs" />
<Compile Include="OAuth\ChannelElements\IConsumerDescription.cs" />
<Compile Include="OAuth\ChannelElements\IConsumerTokenManager.cs" />
+ <Compile Include="OAuth\ChannelElements\IOpenIdOAuthTokenManager.cs" />
<Compile Include="OAuth\ChannelElements\IServiceProviderTokenManager.cs" />
<Compile Include="OAuth\ChannelElements\OAuthConsumerMessageFactory.cs" />
<Compile Include="OAuth\ChannelElements\ITokenGenerator.cs" />
@@ -343,6 +345,10 @@
<Compile Include="OpenId\Extensions\ExtensionBase.cs" />
<Compile Include="OpenId\Extensions\ExtensionArgumentsManager.cs" />
<Compile Include="OpenId\Extensions\IClientScriptExtensionResponse.cs" />
+ <Compile Include="OpenId\Extensions\OAuth\AuthorizationRequest.cs" />
+ <Compile Include="OpenId\Extensions\OAuth\AuthorizationApprovedResponse.cs" />
+ <Compile Include="OpenId\Extensions\OAuth\Constants.cs" />
+ <Compile Include="OpenId\Extensions\OAuth\AuthorizationDeclinedResponse.cs" />
<Compile Include="OpenId\Extensions\OpenIdExtensionFactoryAggregator.cs" />
<Compile Include="OpenId\Extensions\StandardOpenIdExtensionFactory.cs" />
<Compile Include="OpenId\Extensions\ProviderAuthenticationPolicy\AuthenticationPolicies.cs" />
diff --git a/src/DotNetOpenAuth/GlobalSuppressions.cs b/src/DotNetOpenAuth/GlobalSuppressions.cs
index 48c749f..f5268d4 100644
--- a/src/DotNetOpenAuth/GlobalSuppressions.cs
+++ b/src/DotNetOpenAuth/GlobalSuppressions.cs
@@ -40,6 +40,7 @@
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Messages")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy")]
[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.Messaging.Reflection")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "oauthverifier", Scope = "resource", Target = "DotNetOpenAuth.OAuth.OAuthStrings.resources")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "whitelist", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")]
diff --git a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
index efc8896..fe2c2a2 100644
--- a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
+++ b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
@@ -16,6 +16,8 @@ namespace DotNetOpenAuth.InfoCard {
using System.Drawing.Design;
using System.Globalization;
using System.Linq;
+ using System.Text.RegularExpressions;
+ using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
@@ -192,6 +194,13 @@ namespace DotNetOpenAuth.InfoCard {
private bool audienceSet;
/// <summary>
+ /// Initializes a new instance of the <see cref="InfoCardSelector"/> class.
+ /// </summary>
+ public InfoCardSelector() {
+ this.ToolTip = InfoCardStrings.SelectorClickPrompt;
+ }
+
+ /// <summary>
/// Occurs when an InfoCard has been submitted but not decoded yet.
/// </summary>
[Category(InfoCardCategory)]
@@ -255,9 +264,30 @@ namespace DotNetOpenAuth.InfoCard {
/// </summary>
[Description("The URL to this site's privacy policy.")]
[Category(InfoCardCategory), DefaultValue(PrivacyUrlDefault)]
+ [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "We construct a Uri to validate the format of the string.")]
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "That overload is NOT the same.")]
public string PrivacyUrl {
- get { return (string)this.ViewState[PrivacyUrlViewStateKey] ?? PrivacyUrlDefault; }
- set { this.ViewState[PrivacyUrlViewStateKey] = value; }
+ get {
+ return (string)this.ViewState[PrivacyUrlViewStateKey] ?? PrivacyUrlDefault;
+ }
+
+ set {
+ if (this.Page != null && !this.DesignMode) {
+ // Validate new value by trying to construct a Uri based on it.
+ new Uri(new HttpRequestInfo(HttpContext.Current.Request).UrlBeforeRewriting, this.Page.ResolveUrl(value)); // throws an exception on failure.
+ } else {
+ // We can't fully test it, but it should start with either ~/ or a protocol.
+ if (Regex.IsMatch(value, @"^https?://")) {
+ new Uri(value); // make sure it's fully-qualified, but ignore wildcards
+ } else if (value.StartsWith("~/", StringComparison.Ordinal)) {
+ // this is valid too
+ } else {
+ throw new UriFormatException();
+ }
+ }
+
+ this.ViewState[PrivacyUrlViewStateKey] = value;
+ }
}
/// <summary>
@@ -491,6 +521,20 @@ namespace DotNetOpenAuth.InfoCard {
}
/// <summary>
+ /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
+ /// </summary>
+ /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ protected override void OnPreRender(EventArgs e) {
+ base.OnPreRender(e);
+
+ if (!this.DesignMode) {
+ // The Cardspace selector will display an ugly error to the user if
+ // the privacy URL is present but the privacy version is not.
+ ErrorUtilities.VerifyOperation(string.IsNullOrEmpty(this.PrivacyUrl) || !string.IsNullOrEmpty(this.PrivacyVersion), InfoCardStrings.PrivacyVersionRequiredWithPrivacyUrl);
+ }
+ }
+
+ /// <summary>
/// Creates a control that renders to &lt;Param Name="{0}" Value="{1}" /&gt;
/// </summary>
/// <param name="name">The parameter name.</param>
@@ -527,7 +571,7 @@ namespace DotNetOpenAuth.InfoCard {
Image image = new Image();
image.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(typeof(InfoCardSelector), InfoCardImage.GetImageManifestResourceStreamName(this.ImageSize));
image.AlternateText = InfoCardStrings.SelectorClickPrompt;
- image.ToolTip = InfoCardStrings.SelectorClickPrompt;
+ image.ToolTip = this.ToolTip;
image.Style[HtmlTextWriterStyle.Cursor] = "hand";
image.Attributes["onclick"] = this.GetInfoCardSelectorActivationScript(false);
@@ -618,7 +662,8 @@ namespace DotNetOpenAuth.InfoCard {
}
if (!string.IsNullOrEmpty(this.PrivacyUrl)) {
- cardSpaceControl.Controls.Add(CreateParam("privacyUrl", this.PrivacyUrl));
+ string privacyUrl = this.DesignMode ? this.PrivacyUrl : new Uri(Page.Request.Url, Page.ResolveUrl(this.PrivacyUrl)).AbsoluteUri;
+ cardSpaceControl.Controls.Add(CreateParam("privacyUrl", privacyUrl));
}
if (!string.IsNullOrEmpty(this.PrivacyVersion)) {
diff --git a/src/DotNetOpenAuth/InfoCard/InfoCardStrings.Designer.cs b/src/DotNetOpenAuth/InfoCard/InfoCardStrings.Designer.cs
index 4b1dc60..00eb1af 100644
--- a/src/DotNetOpenAuth/InfoCard/InfoCardStrings.Designer.cs
+++ b/src/DotNetOpenAuth/InfoCard/InfoCardStrings.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.
@@ -97,6 +97,15 @@ namespace DotNetOpenAuth.InfoCard {
}
/// <summary>
+ /// Looks up a localized string similar to The PrivacyVersion property must be set whenever the PrivacyUrl property is set..
+ /// </summary>
+ internal static string PrivacyVersionRequiredWithPrivacyUrl {
+ get {
+ return ResourceManager.GetString("PrivacyVersionRequiredWithPrivacyUrl", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Click here to select your Information Card..
/// </summary>
internal static string SelectorClickPrompt {
diff --git a/src/DotNetOpenAuth/InfoCard/InfoCardStrings.resx b/src/DotNetOpenAuth/InfoCard/InfoCardStrings.resx
index e82e8cd..956b321 100644
--- a/src/DotNetOpenAuth/InfoCard/InfoCardStrings.resx
+++ b/src/DotNetOpenAuth/InfoCard/InfoCardStrings.resx
@@ -129,6 +129,9 @@
<data name="PpidClaimRequired" xml:space="preserve">
<value>This operation requires the PPID claim to be included in the InfoCard token.</value>
</data>
+ <data name="PrivacyVersionRequiredWithPrivacyUrl" xml:space="preserve">
+ <value>The PrivacyVersion property must be set whenever the PrivacyUrl property is set.</value>
+ </data>
<data name="SelectorClickPrompt" xml:space="preserve">
<value>Click here to select your Information Card.</value>
</data>
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs
index 7435a90..2e0f1a8 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -394,6 +394,8 @@ namespace DotNetOpenAuth.Messaging {
public TResponse Request<TResponse>(IDirectedProtocolMessage requestMessage)
where TResponse : class, IProtocolMessage {
Contract.Requires(requestMessage != null);
+ Contract.Ensures(Contract.Result<TResponse>() != null);
+
IProtocolMessage response = this.Request(requestMessage);
ErrorUtilities.VerifyProtocol(response != null, MessagingStrings.ExpectedMessageNotReceived, typeof(TResponse));
@@ -565,13 +567,13 @@ namespace DotNetOpenAuth.Messaging {
Contract.Requires(request != null);
ErrorUtilities.VerifyArgumentNotNull(request, "request");
- Logger.Channel.DebugFormat("Incoming HTTP request: {0}", request.Url.AbsoluteUri);
+ Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.UrlBeforeRewriting.AbsoluteUri);
// Search Form data first, and if nothing is there search the QueryString
- Contract.Assume(request.Form != null && request.QueryString != null);
+ Contract.Assume(request.Form != null && request.QueryStringBeforeRewriting != null);
var fields = request.Form.ToDictionary();
if (fields.Count == 0 && request.HttpMethod != "POST") { // OpenID 2.0 section 4.1.2
- fields = request.QueryString.ToDictionary();
+ fields = request.QueryStringBeforeRewriting.ToDictionary();
}
return (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
diff --git a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
index d13da7b..9e9deb4 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;
@@ -54,6 +55,7 @@ namespace DotNetOpenAuth.Messaging {
this.HttpMethod = request.HttpMethod;
this.Url = request.Url;
+ this.UrlBeforeRewriting = GetPublicFacingUrl(request);
this.RawUrl = request.RawUrl;
this.Headers = GetHeaderCollection(request.Headers);
this.InputStream = request.InputStream;
@@ -87,6 +89,7 @@ namespace DotNetOpenAuth.Messaging {
this.HttpMethod = httpMethod;
this.Url = requestUrl;
+ this.UrlBeforeRewriting = requestUrl;
this.RawUrl = rawUrl;
this.Headers = headers;
this.InputStream = inputStream;
@@ -102,6 +105,7 @@ namespace DotNetOpenAuth.Messaging {
this.HttpMethod = listenerRequest.HttpMethod;
this.Url = listenerRequest.Url;
+ this.UrlBeforeRewriting = listenerRequest.Url;
this.RawUrl = listenerRequest.RawUrl;
this.Headers = new WebHeaderCollection();
foreach (string key in listenerRequest.Headers) {
@@ -125,6 +129,7 @@ namespace DotNetOpenAuth.Messaging {
this.HttpMethod = request.Method;
this.Headers = request.Headers;
this.Url = requestUri;
+ this.UrlBeforeRewriting = requestUri;
this.RawUrl = MakeUpRawUrlFromUrl(requestUri);
}
@@ -146,6 +151,7 @@ namespace DotNetOpenAuth.Messaging {
this.HttpMethod = request.Method;
this.Url = request.RequestUri;
+ this.UrlBeforeRewriting = request.RequestUri;
this.RawUrl = MakeUpRawUrlFromUrl(request.RequestUri);
this.Headers = GetHeaderCollection(request.Headers);
this.InputStream = null;
@@ -190,27 +196,13 @@ namespace DotNetOpenAuth.Messaging {
internal string RawUrl { get; set; }
/// <summary>
- /// Gets the full URL of a request before rewriting.
+ /// Gets or sets the full public URL used by the remote client to initiate this request,
+ /// before any URL rewriting and before any changes made by web farm load distributors.
/// </summary>
- internal Uri UrlBeforeRewriting {
- get {
- if (this.Url == null || this.RawUrl == null) {
- return null;
- }
-
- // We use Request.Url for the full path to the server, and modify it
- // with Request.RawUrl to capture both the cookieless session "directory" if it exists
- // and the original path in case URL rewriting is going on. We don't want to be
- // fooled by URL rewriting because we're comparing the actual URL with what's in
- // the return_to parameter in some cases.
- // Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless
- // session, but not the URL rewriting problem.
- return new Uri(this.Url, this.RawUrl);
- }
- }
+ internal Uri UrlBeforeRewriting { get; set; }
/// <summary>
- /// Gets the query part of the URL (The ? and everything after it).
+ /// Gets the query part of the URL (The ? and everything after it), after URL rewriting.
/// </summary>
internal string Query {
get { return this.Url != null ? this.Url.Query : null; }
@@ -297,7 +289,56 @@ namespace DotNetOpenAuth.Messaging {
/// <c>true</c> if this request's URL was rewritten; otherwise, <c>false</c>.
/// </value>
internal bool IsUrlRewritten {
- get { return this.Url.PathAndQuery != this.RawUrl; }
+ get { return this.Url != this.UrlBeforeRewriting; }
+ }
+
+ /// <summary>
+ /// Gets the public facing URL for the given incoming HTTP request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="serverVariables">The server variables to consider part of the request.</param>
+ /// <returns>
+ /// The URI that the outside world used to create this request.
+ /// </returns>
+ /// <remarks>
+ /// Although the <paramref name="serverVariables"/> value can be obtained from
+ /// <see cref="HttpRequest.ServerVariables"/>, it's useful to be able to pass them
+ /// in so we can simulate injected values from our unit tests since the actual property
+ /// is a read-only kind of <see cref="NameValueCollection"/>.
+ /// </remarks>
+ internal static Uri GetPublicFacingUrl(HttpRequest request, NameValueCollection serverVariables) {
+ Contract.Requires(request != null);
+ Contract.Requires(serverVariables != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ ErrorUtilities.VerifyArgumentNotNull(serverVariables, "serverVariables");
+
+ // Due to URL rewriting, cloud computing (i.e. Azure)
+ // and web farms, etc., we have to be VERY careful about what
+ // we consider the incoming URL. We want to see the URL as it would
+ // appear on the public-facing side of the hosting web site.
+ // HttpRequest.Url gives us the internal URL in a cloud environment,
+ // So we use a variable that (at least from what I can tell) gives us
+ // 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;
+ Uri hostAndPort = new Uri(scheme + Uri.SchemeDelimiter + serverVariables["HTTP_HOST"]);
+ UriBuilder publicRequestUri = new UriBuilder(request.Url);
+ publicRequestUri.Scheme = scheme;
+ publicRequestUri.Host = hostAndPort.Host;
+ publicRequestUri.Port = hostAndPort.Port;
+ return publicRequestUri.Uri;
+ } else {
+ // Failover to the method that works for non-web farm enviroments.
+ // We use Request.Url for the full path to the server, and modify it
+ // with Request.RawUrl to capture both the cookieless session "directory" if it exists
+ // and the original path in case URL rewriting is going on. We don't want to be
+ // fooled by URL rewriting because we're comparing the actual URL with what's in
+ // the return_to parameter in some cases.
+ // Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless
+ // session, but not the URL rewriting problem.
+ return new Uri(request.Url, request.RawUrl);
+ }
}
/// <summary>
@@ -316,6 +357,18 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Gets the public facing URL for the given incoming HTTP request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>The URI that the outside world used to create this request.</returns>
+ private static Uri GetPublicFacingUrl(HttpRequest request) {
+ Contract.Requires(request != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ return GetPublicFacingUrl(request, request.ServerVariables);
+ }
+
+ /// <summary>
/// Makes up a reasonable guess at the raw URL from the possibly rewritten URL.
/// </summary>
/// <param name="url">A full URL.</param>
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index 1cb2177..463b556 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -577,7 +577,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="request">The request to get recipient information from.</param>
/// <returns>The recipient.</returns>
internal static MessageReceivingEndpoint GetRecipient(this HttpRequestInfo request) {
- return new MessageReceivingEndpoint(request.Url, request.HttpMethod == "GET" ? HttpDeliveryMethods.GetRequest : HttpDeliveryMethods.PostRequest);
+ return new MessageReceivingEndpoint(request.UrlBeforeRewriting, request.HttpMethod == "GET" ? HttpDeliveryMethods.GetRequest : HttpDeliveryMethods.PostRequest);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
index 9da4fee..8979693 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
@@ -114,7 +114,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <summary>
/// Ensures the message parts pass basic validation.
/// </summary>
- /// <param name="parts">The key/value pairs of the serialzied message.</param>
+ /// <param name="parts">The key/value pairs of the serialized message.</param>
internal void EnsureMessagePartsPassBasicValidation(IDictionary<string, string> parts) {
try {
this.EnsureRequiredMessagePartsArePresent(parts.Keys);
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
index 911093d..db5903e 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
@@ -258,7 +258,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
MessagePart part;
if (this.description.Mapping.TryGetValue(key, out part)) {
value = part.GetValue(this.message);
- return true;
+ return value != null;
}
return this.message.ExtraData.TryGetValue(key, out value);
}
@@ -279,13 +279,28 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <summary>
/// Removes all values in the message.
/// </summary>
- public void Clear() {
+ public void ClearValues() {
foreach (string key in this.Keys) {
this.Remove(key);
}
}
/// <summary>
+ /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
+ /// </summary>
+ /// <exception cref="T:System.NotSupportedException">
+ /// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
+ /// </exception>
+ /// <remarks>
+ /// This method cannot be implemented because keys are not guaranteed to be removed
+ /// since some are inherent to the type of message that this dictionary provides
+ /// access to.
+ /// </remarks>
+ public void Clear() {
+ throw new NotSupportedException();
+ }
+
+ /// <summary>
/// Checks whether a named value has been set on the message.
/// </summary>
/// <param name="item">The name/value pair.</param>
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
index 0732bb2..a8f04ec 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
@@ -188,7 +188,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
this.field.SetValue(message, this.ToValue(value));
}
}
- } catch (FormatException ex) {
+ } catch (Exception ex) {
throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartReadFailure, message.GetType(), this.Name, value);
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs
new file mode 100644
index 0000000..ff004cb
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs
@@ -0,0 +1,33 @@
+//-----------------------------------------------------------------------
+// <copyright file="ICombinedOpenIdProviderTokenManager.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using DotNetOpenAuth.OpenId;
+
+ /// <summary>
+ /// An interface that providers that play a dual role as OpenID Provider
+ /// and OAuth Service Provider should implement on their token manager classes.
+ /// </summary>
+ /// <remarks>
+ /// This interface should be implemented by the same class that implements
+ /// <see cref="ITokenManager"/> in order to enable the OpenID+OAuth extension.
+ /// </remarks>
+ public interface ICombinedOpenIdProviderTokenManager {
+ /// <summary>
+ /// Gets the OAuth consumer key for a given OpenID relying party realm.
+ /// </summary>
+ /// <param name="realm">The relying party's OpenID realm.</param>
+ /// <returns>The OAuth consumer key for a given OpenID realm.</returns>
+ /// <para>This is a security-critical function. Since OpenID requests
+ /// and OAuth extensions for those requests can be formulated by ANYONE
+ /// (no signing is required by the relying party), and since the response to
+ /// the authentication will include access the user is granted to the
+ /// relying party who CLAIMS to be from some realm, it is of paramount
+ /// importance that the realm is recognized as belonging to the consumer
+ /// key by the host service provider in order to protect against phishers.</para>
+ string GetConsumerKey(Realm realm);
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IOpenIdOAuthTokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IOpenIdOAuthTokenManager.cs
new file mode 100644
index 0000000..b3ee320
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IOpenIdOAuthTokenManager.cs
@@ -0,0 +1,30 @@
+//-----------------------------------------------------------------------
+// <copyright file="IOpenIdOAuthTokenManager.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Extensions.OAuth;
+
+ /// <summary>
+ /// Additional methods an <see cref="ITokenManager"/> implementing class
+ /// may implement to support the OpenID+OAuth extension.
+ /// </summary>
+ public interface IOpenIdOAuthTokenManager {
+ /// <summary>
+ /// Stores a new request token obtained over an OpenID request.
+ /// </summary>
+ /// <param name="consumerKey">The consumer key.</param>
+ /// <param name="authorization">The authorization message carrying the request token and authorized access scope.</param>
+ /// <remarks>
+ /// <para>The token secret is the empty string.</para>
+ /// <para>Tokens stored by this method should be short-lived to mitigate
+ /// possible security threats. Their lifetime should be sufficient for the
+ /// relying party to receive the positive authentication assertion and immediately
+ /// send a follow-up request for the access token.</para>
+ /// </remarks>
+ void StoreOpenIdAuthorizedRequestToken(string consumerKey, AuthorizationApprovedResponse authorization);
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
index ce4c610..d325825 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
@@ -163,7 +163,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
// Add receiving HTTP transport information required for signature generation.
var signedMessage = message as ITamperResistantOAuthMessage;
if (signedMessage != null) {
- signedMessage.Recipient = request.Url;
+ signedMessage.Recipient = request.UrlBeforeRewriting;
signedMessage.HttpMethod = request.HttpMethod;
}
diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
index 8c4484b..55b40ac 100644
--- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
+++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
@@ -233,6 +233,10 @@ namespace DotNetOpenAuth.OAuth {
/// The access token assigned by the Service Provider.
/// </returns>
protected AuthorizedTokenResponse ProcessUserAuthorization(string requestToken, string verifier) {
+ Contract.Requires(!String.IsNullOrEmpty(requestToken));
+ Contract.Ensures(Contract.Result<AuthorizedTokenResponse>() != null);
+ ErrorUtilities.VerifyNonZeroLength(requestToken, "requestToken");
+
var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, this.ServiceProvider.Version) {
RequestToken = requestToken,
VerificationCode = verifier,
diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
index 689998a..3593446 100644
--- a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
+++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
@@ -115,6 +115,24 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
+ /// Looks up a localized string similar to Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface..
+ /// </summary>
+ internal static string OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface {
+ get {
+ return ResourceManager.GetString("OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The OpenID Relying Party&apos;s realm is not recognized as belonging to the OAuth Consumer identified by the consumer key given..
+ /// </summary>
+ internal static string OpenIdOAuthRealmConsumerKeyDoNotMatch {
+ get {
+ return ResourceManager.GetString("OpenIdOAuthRealmConsumerKeyDoNotMatch", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}..
/// </summary>
internal static string MinimumConsumerVersionRequirementNotMet {
diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
index a40b35d..bbeeda9 100644
--- a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
+++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
@@ -138,6 +138,12 @@
<data name="MinimumConsumerVersionRequirementNotMet" xml:space="preserve">
<value>This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.</value>
</data>
+ <data name="OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface" xml:space="preserve">
+ <value>Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.</value>
+ </data>
+ <data name="OpenIdOAuthRealmConsumerKeyDoNotMatch" xml:space="preserve">
+ <value>The OpenID Relying Party's realm is not recognized as belonging to the OAuth Consumer identified by the consumer key given.</value>
+ </data>
<data name="RequestUrlMustNotHaveOAuthParameters" xml:space="preserve">
<value>The request URL query MUST NOT contain any OAuth Protocol Parameters.</value>
</data>
diff --git a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
index 70ecf75..40f7a5e 100644
--- a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
+++ b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
@@ -18,6 +18,10 @@ namespace DotNetOpenAuth.OAuth {
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Extensions.OAuth;
+ using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.OpenId.Provider;
/// <summary>
/// A web application that allows access via OAuth.
@@ -285,6 +289,73 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
+ /// Gets the OAuth authorization request included with an OpenID authentication
+ /// request.
+ /// </summary>
+ /// <param name="openIdAuthenticationRequest">The OpenID authentication request.</param>
+ /// <returns>
+ /// The scope of access the relying party is requesting.
+ /// </returns>
+ /// <remarks>
+ /// <para>Call this method rather than simply extracting the OAuth extension
+ /// out from the authentication request directly to ensure that the additional
+ /// security measures that are required are taken.</para>
+ /// </remarks>
+ public AuthorizationRequest ReadAuthorizationRequest(IAuthenticationRequest openIdAuthenticationRequest) {
+ Contract.Requires(openIdAuthenticationRequest != null);
+ Contract.Requires(this.TokenManager is ICombinedOpenIdProviderTokenManager);
+ ErrorUtilities.VerifyArgumentNotNull(openIdAuthenticationRequest, "openIdAuthenticationRequest");
+ var openidTokenManager = this.TokenManager as ICombinedOpenIdProviderTokenManager;
+ ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName);
+
+ var authzRequest = openIdAuthenticationRequest.GetExtension<AuthorizationRequest>();
+ if (authzRequest == null) {
+ return null;
+ }
+
+ // OpenID+OAuth spec section 9:
+ // The Combined Provider SHOULD verify that the consumer key passed in the
+ // request is authorized to be used for the realm passed in the request.
+ string expectedConsumerKey = openidTokenManager.GetConsumerKey(openIdAuthenticationRequest.Realm);
+ ErrorUtilities.VerifyProtocol(
+ string.Equals(expectedConsumerKey, authzRequest.Consumer, StringComparison.Ordinal),
+ OAuthStrings.OpenIdOAuthRealmConsumerKeyDoNotMatch);
+
+ return authzRequest;
+ }
+
+ /// <summary>
+ /// Attaches the authorization response to an OpenID authentication response.
+ /// </summary>
+ /// <param name="openIdAuthenticationRequest">The OpenID authentication request.</param>
+ /// <param name="consumerKey">The consumer key. May and should be <c>null</c> if and only if <paramref name="scope"/> is null.</param>
+ /// <param name="scope">The approved access scope. Use <c>null</c> to indicate no access was granted. The empty string will be interpreted as some default level of access is granted.</param>
+ [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "We want to take IAuthenticationRequest because that's the only supported use case.")]
+ public void AttachAuthorizationResponse(IAuthenticationRequest openIdAuthenticationRequest, string consumerKey, string scope) {
+ Contract.Requires(openIdAuthenticationRequest != null);
+ Contract.Requires((consumerKey == null) == (scope == null));
+ Contract.Requires(this.TokenManager is IOpenIdOAuthTokenManager);
+ ErrorUtilities.VerifyArgumentNotNull(openIdAuthenticationRequest, "openIdAuthenticationRequest");
+ var openidTokenManager = this.TokenManager as IOpenIdOAuthTokenManager;
+ ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName);
+
+ IOpenIdMessageExtension response;
+ if (scope != null) {
+ // Generate an authorized request token to return to the relying party.
+ var approvedResponse = new AuthorizationApprovedResponse {
+ RequestToken = this.TokenGenerator.GenerateRequestToken(consumerKey),
+ Scope = scope,
+ };
+ openidTokenManager.StoreOpenIdAuthorizedRequestToken(consumerKey, approvedResponse);
+ response = approvedResponse;
+ } else {
+ response = new AuthorizationDeclinedResponse();
+ }
+
+ openIdAuthenticationRequest.AddResponseExtension(response);
+ }
+
+ /// <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>
diff --git a/src/DotNetOpenAuth/OAuth/WebConsumer.cs b/src/DotNetOpenAuth/OAuth/WebConsumer.cs
index 11f3da8..d86444d 100644
--- a/src/DotNetOpenAuth/OAuth/WebConsumer.cs
+++ b/src/DotNetOpenAuth/OAuth/WebConsumer.cs
@@ -7,10 +7,13 @@
namespace DotNetOpenAuth.OAuth {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Web;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
+ using DotNetOpenAuth.OpenId.Extensions.OAuth;
+ using DotNetOpenAuth.OpenId.RelyingParty;
/// <summary>
/// A website or application that uses OAuth to access the Service Provider on behalf of the User.
@@ -71,11 +74,77 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
+ /// Attaches an OAuth authorization request to an outgoing OpenID authentication request.
+ /// </summary>
+ /// <param name="openIdAuthenticationRequest">The OpenID authentication request.</param>
+ /// <param name="scope">The scope of access that is requested of the service provider.</param>
+ public void AttachAuthorizationRequest(IAuthenticationRequest openIdAuthenticationRequest, string scope) {
+ Contract.Requires(openIdAuthenticationRequest != null);
+ ErrorUtilities.VerifyArgumentNotNull(openIdAuthenticationRequest, "openIdAuthenticationRequest");
+
+ var authorizationRequest = new AuthorizationRequest {
+ Consumer = this.ConsumerKey,
+ Scope = scope,
+ };
+
+ openIdAuthenticationRequest.AddExtension(authorizationRequest);
+ }
+
+ /// <summary>
+ /// Processes an incoming authorization-granted message from an SP and obtains an access token.
+ /// </summary>
+ /// <param name="openIdAuthenticationResponse">The OpenID authentication response that may be carrying an authorized request token.</param>
+ /// <returns>
+ /// The access token, or null if OAuth authorization was denied by the user or service provider.
+ /// </returns>
+ /// <remarks>
+ /// The access token, if granted, is automatically stored in the <see cref="ConsumerBase.TokenManager"/>.
+ /// The token manager instance must implement <see cref="IOpenIdOAuthTokenManager"/>.
+ /// </remarks>
+ public AuthorizedTokenResponse ProcessUserAuthorization(IAuthenticationResponse openIdAuthenticationResponse) {
+ Contract.Requires(openIdAuthenticationResponse != null);
+ Contract.Requires(this.TokenManager is IOpenIdOAuthTokenManager);
+ ErrorUtilities.VerifyArgumentNotNull(openIdAuthenticationResponse, "openIdAuthenticationResponse");
+ var openidTokenManager = this.TokenManager as IOpenIdOAuthTokenManager;
+ ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName);
+
+ // The OAuth extension is only expected in positive assertion responses.
+ if (openIdAuthenticationResponse.Status != AuthenticationStatus.Authenticated) {
+ return null;
+ }
+
+ // Retrieve the OAuth extension
+ var positiveAuthorization = openIdAuthenticationResponse.GetExtension<AuthorizationApprovedResponse>();
+ if (positiveAuthorization == null) {
+ return null;
+ }
+
+ // Prepare a message to exchange the request token for an access token.
+ // We are careful to use a v1.0 message version so that the oauth_verifier is not required.
+ var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, Protocol.V10.Version) {
+ RequestToken = positiveAuthorization.RequestToken,
+ ConsumerKey = this.ConsumerKey,
+ };
+
+ // Retrieve the access token and store it in the token manager.
+ openidTokenManager.StoreOpenIdAuthorizedRequestToken(this.ConsumerKey, positiveAuthorization);
+ var grantAccess = this.Channel.Request<AuthorizedTokenResponse>(requestAccess);
+ this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, positiveAuthorization.RequestToken, grantAccess.AccessToken, grantAccess.TokenSecret);
+
+ // Provide the caller with the access token so it may be associated with the user
+ // that is logging in.
+ return grantAccess;
+ }
+
+ /// <summary>
/// Processes an incoming authorization-granted message from an SP and obtains an access token.
/// </summary>
/// <param name="request">The incoming HTTP request.</param>
/// <returns>The access token, or null if no incoming authorization message was recognized.</returns>
- internal AuthorizedTokenResponse ProcessUserAuthorization(HttpRequestInfo request) {
+ public AuthorizedTokenResponse ProcessUserAuthorization(HttpRequestInfo request) {
+ Contract.Requires(request != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
UserAuthorizationResponse authorizationMessage;
if (this.Channel.TryReadFromRequest<UserAuthorizationResponse>(request, out authorizationMessage)) {
string requestToken = authorizationMessage.RequestToken;
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
index 035f389..40ed463 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
@@ -118,7 +92,12 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
// OpenID 2.0 Section 12 forbids two extensions with the same TypeURI in the same message.
ErrorUtilities.VerifyProtocol(!extensionManager.ContainsExtension(extension.TypeUri), OpenIdStrings.ExtensionAlreadyAddedWithSameTypeURI, extension.TypeUri);
- var extensionDictionary = this.Channel.MessageDescriptions.GetAccessor(extension).Serialize();
+ // Ensure that we're sending out a valid extension.
+ var extensionDescription = this.Channel.MessageDescriptions.Get(extension);
+ var extensionDictionary = extensionDescription.GetDictionary(extension).Serialize();
+ extensionDescription.EnsureMessagePartsPassBasicValidation(extensionDictionary);
+
+ // Add the extension to the outgoing message payload.
extensionManager.AddExtensionArguments(extension.TypeUri, extensionDictionary);
} else {
Logger.OpenId.WarnFormat("Unexpected extension type {0} did not implement {1}.", protocolExtension.GetType(), typeof(IOpenIdMessageExtension).Name);
@@ -159,17 +138,62 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) {
var extendableMessage = message as IProtocolMessageWithExtensions;
if (extendableMessage != null) {
- // We have a helper class that will do all the heavy-lifting of organizing
- // all the extensions, their aliases, and their parameters.
- bool isAtProvider = extendableMessage is SignedResponseRequest;
- var extensionManager = ExtensionArgumentsManager.CreateIncomingExtensions(this.GetExtensionsDictionary(message));
- foreach (string typeUri in extensionManager.GetExtensionTypeUris()) {
- var extensionData = extensionManager.GetExtensionArguments(typeUri);
+ // First add the extensions that are signed by the Provider.
+ foreach (IOpenIdMessageExtension signedExtension in this.GetExtensions(extendableMessage, true, null)) {
+ signedExtension.IsSignedByRemoteParty = true;
+ extendableMessage.Extensions.Add(signedExtension);
+ }
- // Initialize this particular extension.
- IOpenIdMessageExtension extension = this.ExtensionFactory.Create(typeUri, extensionData, extendableMessage, isAtProvider);
- if (extension != null) {
- MessageDictionary extensionDictionary = this.Channel.MessageDescriptions.GetAccessor(extension);
+ // Now search again, considering ALL extensions whether they are signed or not,
+ // skipping the signed ones and adding the new ones as unsigned extensions.
+ Func<string, bool> isNotSigned = typeUri => !extendableMessage.Extensions.Cast<IOpenIdMessageExtension>().Any(ext => ext.TypeUri == typeUri);
+ foreach (IOpenIdMessageExtension unsignedExtension in this.GetExtensions(extendableMessage, false, isNotSigned)) {
+ unsignedExtension.IsSignedByRemoteParty = false;
+ extendableMessage.Extensions.Add(unsignedExtension);
+ }
+
+ return MessageProtections.None;
+ }
+
+ return null;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the extensions on a message.
+ /// </summary>
+ /// <param name="message">The carrier of the extensions.</param>
+ /// <param name="ignoreUnsigned">If set to <c>true</c> only signed extensions will be available.</param>
+ /// <param name="extensionFilter">A optional filter that takes an extension type URI and
+ /// returns a value indicating whether that extension should be deserialized and
+ /// returned in the sequence. May be null.</param>
+ /// <returns>A sequence of extensions in the message.</returns>
+ private IEnumerable<IOpenIdMessageExtension> GetExtensions(IProtocolMessageWithExtensions message, bool ignoreUnsigned, Func<string, bool> extensionFilter) {
+ bool isAtProvider = message is SignedResponseRequest;
+
+ // We have a helper class that will do all the heavy-lifting of organizing
+ // all the extensions, their aliases, and their parameters.
+ var extensionManager = ExtensionArgumentsManager.CreateIncomingExtensions(this.GetExtensionsDictionary(message, ignoreUnsigned));
+ foreach (string typeUri in extensionManager.GetExtensionTypeUris()) {
+ // Our caller may have already obtained a signed version of this extension,
+ // so skip it if they don't want this one.
+ if (extensionFilter != null && !extensionFilter(typeUri)) {
+ continue;
+ }
+
+ var extensionData = extensionManager.GetExtensionArguments(typeUri);
+
+ // Initialize this particular extension.
+ IOpenIdMessageExtension extension = this.ExtensionFactory.Create(typeUri, extensionData, message, isAtProvider);
+ if (extension != null) {
+ try {
+ // Make sure the extension fulfills spec requirements before deserializing it.
+ MessageDescription messageDescription = this.Channel.MessageDescriptions.Get(extension);
+ messageDescription.EnsureMessagePartsPassBasicValidation(extensionData);
+
+ // Deserialize the extension.
+ MessageDictionary extensionDictionary = messageDescription.GetDictionary(extension);
foreach (var pair in extensionData) {
extensionDictionary[pair.Key] = pair.Value;
}
@@ -179,39 +203,34 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
if (customSerializingExtension != null) {
customSerializingExtension.OnReceiving();
}
+ } catch (ProtocolException ex) {
+ Logger.OpenId.ErrorFormat(OpenIdStrings.BadExtension, extension.GetType(), ex);
+ extension = null;
+ }
- extendableMessage.Extensions.Add(extension);
- } else {
- Logger.OpenId.WarnFormat("Extension with type URI '{0}' ignored because it is not a recognized extension.", typeUri);
+ if (extension != null) {
+ yield return extension;
}
+ } else {
+ Logger.OpenId.WarnFormat("Extension with type URI '{0}' ignored because it is not a recognized extension.", typeUri);
}
-
- return MessageProtections.None;
}
-
- return null;
}
- #endregion
-
/// <summary>
/// Gets the dictionary of message parts that should be deserialized into extensions.
/// </summary>
/// <param name="message">The message.</param>
- /// <returns>A dictionary of message parts, including only signed parts when appropriate.</returns>
- private IDictionary<string, string> GetExtensionsDictionary(IProtocolMessage message) {
+ /// <param name="ignoreUnsigned">If set to <c>true</c> only signed extensions will be available.</param>
+ /// <returns>
+ /// A dictionary of message parts, including only signed parts when appropriate.
+ /// </returns>
+ private IDictionary<string, string> GetExtensionsDictionary(IProtocolMessage message, bool ignoreUnsigned) {
Contract.Requires(this.Channel != null);
ErrorUtilities.VerifyOperation(this.Channel != null, "Channel property has not been set.");
- // An IndirectSignedResponse message (the only one we care to filter parts for)
- // can be received both by RPs and OPs (during check_auth).
- // Whichever party is reading the extensions, apply their security policy regarding
- // signing. (Although OPs have no reason to deserialize extensions during check_auth)
- // so that scenario might be optimized away eventually.
- bool extensionsShouldBeSigned = this.rpSecuritySettings != null ? !this.rpSecuritySettings.AllowUnsignedIncomingExtensions : this.opSecuritySettings.SignOutgoingExtensions;
-
IndirectSignedResponse signedResponse = message as IndirectSignedResponse;
- if (signedResponse != null && extensionsShouldBeSigned) {
+ if (signedResponse != null && ignoreUnsigned) {
return signedResponse.GetSignedMessageParts(this.Channel);
} else {
return this.Channel.MessageDescriptions.GetAccessor(message);
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
index 1ddd1eb..bc86259 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
@@ -341,8 +341,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
var extensionFactory = OpenIdExtensionFactoryAggregator.LoadFromConfiguration();
List<IChannelBindingElement> elements = new List<IChannelBindingElement>(7);
+ elements.Add(new ExtensionsBindingElement(extensionFactory));
if (isRelyingPartyRole) {
- elements.Add(new ExtensionsBindingElement(extensionFactory, rpSecuritySettings));
elements.Add(new BackwardCompatibilityBindingElement());
if (associationStore != null) {
@@ -358,8 +358,6 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
elements.Add(new ReturnToSignatureBindingElement(rpAssociationStore, rpSecuritySettings));
}
} else {
- elements.Add(new ExtensionsBindingElement(extensionFactory, opSecuritySettings));
-
// Providers must always have a nonce store.
ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs
index 73039d4..9413e2f 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs
@@ -71,6 +71,16 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
public Uri UpdateUrl { get; set; }
/// <summary>
+ /// Gets a value indicating whether this extension is signed by the Provider.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is signed by the Provider; otherwise, <c>false</c>.
+ /// </value>
+ public bool IsSignedByProvider {
+ get { return this.IsSignedByRemoteParty; }
+ }
+
+ /// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
/// </summary>
/// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreResponse.cs
index ae6ea5b..ba7f091 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreResponse.cs
@@ -74,6 +74,16 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
public string FailureReason { get; set; }
/// <summary>
+ /// Gets a value indicating whether this extension is signed by the Provider.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is signed by the Provider; otherwise, <c>false</c>.
+ /// </value>
+ public bool IsSignedByProvider {
+ get { return this.IsSignedByRemoteParty; }
+ }
+
+ /// <summary>
/// Gets or sets the mode argument.
/// </summary>
/// <value>One of 'store_response_success' or 'store_response_failure'.</value>
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs
index 3ca979d..108ac52 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs
@@ -72,6 +72,18 @@ namespace DotNetOpenAuth.OpenId.Extensions {
get { return this.AdditionalSupportedTypeUris; }
}
+ /// <summary>
+ /// Gets or sets a value indicating whether this extension was
+ /// signed by the OpenID Provider.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is signed by the provider; otherwise, <c>false</c>.
+ /// </value>
+ bool IOpenIdMessageExtension.IsSignedByRemoteParty {
+ get { return this.IsSignedByRemoteParty; }
+ set { this.IsSignedByRemoteParty = value; }
+ }
+
#endregion
#region IMessage Properties
@@ -101,6 +113,15 @@ namespace DotNetOpenAuth.OpenId.Extensions {
}
/// <summary>
+ /// Gets or sets a value indicating whether this extension was
+ /// signed by the OpenID Provider.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is signed by the provider; otherwise, <c>false</c>.
+ /// </value>
+ protected bool IsSignedByRemoteParty { get; set; }
+
+ /// <summary>
/// Gets the additional TypeURIs that are supported by this extension, in preferred order.
/// May be empty if none other than <see cref="IOpenIdMessageExtension.TypeUri"/> is supported, but
/// should not be null.
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationApprovedResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationApprovedResponse.cs
new file mode 100644
index 0000000..5e7bc49
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationApprovedResponse.cs
@@ -0,0 +1,48 @@
+//-----------------------------------------------------------------------
+// <copyright file="AuthorizationApprovedResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.OAuth {
+ using System;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The OAuth response that a Provider may include with a positive
+ /// OpenID identity assertion with an approved request token.
+ /// </summary>
+ [Serializable]
+ public class AuthorizationApprovedResponse : ExtensionBase {
+ /// <summary>
+ /// The factory method that may be used in deserialization of this message.
+ /// </summary>
+ internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => {
+ if (typeUri == Constants.TypeUri && !isProviderRole && data.ContainsKey(Constants.RequestTokenParameter)) {
+ return new AuthorizationApprovedResponse();
+ }
+
+ return null;
+ };
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthorizationApprovedResponse"/> class.
+ /// </summary>
+ public AuthorizationApprovedResponse()
+ : base(new Version(1, 0), Constants.TypeUri, null) {
+ }
+
+ /// <summary>
+ /// Gets or sets the user-approved request token.
+ /// </summary>
+ /// <value>The request token.</value>
+ [MessagePart(Constants.RequestTokenParameter, IsRequired = true, AllowEmpty = false)]
+ public string RequestToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets a string that encodes, in a way possibly specific to the Combined Provider, one or more scopes that the returned request token is valid for. This will typically indicate a subset of the scopes requested in Section 8.
+ /// </summary>
+ [MessagePart("scope", IsRequired = false, AllowEmpty = true)]
+ public string Scope { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationDeclinedResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationDeclinedResponse.cs
new file mode 100644
index 0000000..7c3a5ad
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationDeclinedResponse.cs
@@ -0,0 +1,34 @@
+//-----------------------------------------------------------------------
+// <copyright file="AuthorizationDeclinedResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.OAuth {
+ using System;
+
+ /// <summary>
+ /// The OAuth response that a Provider should include with a positive
+ /// OpenID identity assertion when OAuth authorization was declined.
+ /// </summary>
+ [Serializable]
+ public class AuthorizationDeclinedResponse : ExtensionBase {
+ /// <summary>
+ /// The factory method that may be used in deserialization of this message.
+ /// </summary>
+ internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => {
+ if (typeUri == Constants.TypeUri && !isProviderRole && !data.ContainsKey(Constants.RequestTokenParameter)) {
+ return new AuthorizationDeclinedResponse();
+ }
+
+ return null;
+ };
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthorizationDeclinedResponse"/> class.
+ /// </summary>
+ public AuthorizationDeclinedResponse()
+ : base(new Version(1, 0), Constants.TypeUri, null) {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationRequest.cs
new file mode 100644
index 0000000..99f0880
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/OAuth/AuthorizationRequest.cs
@@ -0,0 +1,57 @@
+//-----------------------------------------------------------------------
+// <copyright file="AuthorizationRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.OAuth {
+ using System;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// An extension to include with an authentication request in order to also
+ /// obtain authorization to access user data at the combined OpenID Provider
+ /// and Service Provider.
+ /// </summary>
+ /// <remarks>
+ /// <para>When requesting OpenID Authentication via the protocol mode "checkid_setup"
+ /// or "checkid_immediate", this extension can be used to request that the end
+ /// user authorize an OAuth access token at the same time as an OpenID
+ /// authentication. This is done by sending the following parameters as part
+ /// of the OpenID request. (Note that the use of "oauth" as part of the parameter
+ /// names here and in subsequent sections is just an example. See Section 5 for details.)</para>
+ /// <para>See section 8.</para>
+ /// </remarks>
+ [Serializable]
+ public class AuthorizationRequest : ExtensionBase {
+ /// <summary>
+ /// The factory method that may be used in deserialization of this message.
+ /// </summary>
+ internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => {
+ if (typeUri == Constants.TypeUri && isProviderRole) {
+ return new AuthorizationRequest();
+ }
+
+ return null;
+ };
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthorizationRequest"/> class.
+ /// </summary>
+ public AuthorizationRequest()
+ : base(new Version(1, 0), Constants.TypeUri, null) {
+ }
+
+ /// <summary>
+ /// Gets or sets the consumer key agreed upon between the Consumer and Service Provider.
+ /// </summary>
+ [MessagePart("consumer", IsRequired = true, AllowEmpty = false)]
+ public string Consumer { get; set; }
+
+ /// <summary>
+ /// Gets or sets a string that encodes, in a way possibly specific to the Combined Provider, one or more scopes for the OAuth token expected in the authentication response.
+ /// </summary>
+ [MessagePart("scope", IsRequired = false)]
+ public string Scope { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/OAuth/Constants.cs b/src/DotNetOpenAuth/OpenId/Extensions/OAuth/Constants.cs
new file mode 100644
index 0000000..32efee9
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/OAuth/Constants.cs
@@ -0,0 +1,22 @@
+//-----------------------------------------------------------------------
+// <copyright file="Constants.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.OAuth {
+ /// <summary>
+ /// Constants used in the OpenID OAuth extension.
+ /// </summary>
+ internal static class Constants {
+ /// <summary>
+ /// The TypeURI for the OpenID OAuth extension.
+ /// </summary>
+ internal const string TypeUri = "http://specs.openid.net/extensions/oauth/1.0";
+
+ /// <summary>
+ /// The name of the parameter that carries the request token in the response.
+ /// </summary>
+ internal const string RequestTokenParameter = "request_token";
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs
index 8cf493c..b476cf7 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs
@@ -140,6 +140,16 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
/// </remarks>
public IDictionary<string, string> AssuranceLevels { get; private set; }
+ /// <summary>
+ /// Gets a value indicating whether this extension is signed by the Provider.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is signed by the Provider; otherwise, <c>false</c>.
+ /// </value>
+ public bool IsSignedByProvider {
+ get { return this.IsSignedByRemoteParty; }
+ }
+
#region IMessageWithEvents Members
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs
index f17df46..a58c754 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs
@@ -217,6 +217,16 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
}
/// <summary>
+ /// Gets a value indicating whether this extension is signed by the Provider.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is signed by the Provider; otherwise, <c>false</c>.
+ /// </value>
+ public bool IsSignedByProvider {
+ get { return this.IsSignedByRemoteParty; }
+ }
+
+ /// <summary>
/// Tests equality of two <see cref="ClaimsResponse"/> objects.
/// </summary>
/// <param name="one">One instance to compare.</param>
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/StandardOpenIdExtensionFactory.cs b/src/DotNetOpenAuth/OpenId/Extensions/StandardOpenIdExtensionFactory.cs
index 9dda6ad..a669672 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/StandardOpenIdExtensionFactory.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/StandardOpenIdExtensionFactory.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.ChannelElements;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
+ using DotNetOpenAuth.OpenId.Extensions.OAuth;
using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using DotNetOpenAuth.OpenId.Messages;
@@ -35,6 +36,9 @@ namespace DotNetOpenAuth.OpenId.Extensions {
this.RegisterExtension(StoreResponse.Factory);
this.RegisterExtension(PolicyRequest.Factory);
this.RegisterExtension(PolicyResponse.Factory);
+ this.RegisterExtension(AuthorizationRequest.Factory);
+ this.RegisterExtension(AuthorizationApprovedResponse.Factory);
+ this.RegisterExtension(AuthorizationDeclinedResponse.Factory);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs b/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs
index 278a70f..e2bb365 100644
--- a/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs
+++ b/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs
@@ -156,7 +156,7 @@ namespace DotNetOpenAuth.OpenId.Interop {
/// <returns>The Provider's response to a previous authentication request, or null if no response is present.</returns>
public AuthenticationResponseShim ProcessAuthentication(string url, string form) {
OpenIdRelyingParty rp = new OpenIdRelyingParty(null);
- HttpRequestInfo requestInfo = new HttpRequestInfo { Url = new Uri(url) };
+ HttpRequestInfo requestInfo = new HttpRequestInfo { UrlBeforeRewriting = new Uri(url) };
if (!string.IsNullOrEmpty(form)) {
requestInfo.HttpMethod = "POST";
requestInfo.InputStream = new MemoryStream(Encoding.Unicode.GetBytes(form));
diff --git a/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs b/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs
index fb984fe..95080e6 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs
@@ -41,5 +41,14 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// The <see cref="Extensions.SimpleRegistration.ClaimsRequest.CreateResponse"/> for an example.
/// </remarks>
IEnumerable<string> AdditionalSupportedTypeUris { get; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this extension was
+ /// signed by the sender.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is signed by the sender; otherwise, <c>false</c>.
+ /// </value>
+ bool IsSignedByRemoteParty { get; set; }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
index 107046d..9462d21 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
@@ -238,6 +238,20 @@ namespace DotNetOpenAuth.OpenId.Messages {
internal bool ReturnToParametersSignatureValidated { get; set; }
/// <summary>
+ /// Gets the signed extensions on this message.
+ /// </summary>
+ internal IEnumerable<IOpenIdMessageExtension> SignedExtensions {
+ get { return this.extensions.OfType<IOpenIdMessageExtension>().Where(ext => ext.IsSignedByRemoteParty); }
+ }
+
+ /// <summary>
+ /// Gets the unsigned extensions on this message.
+ /// </summary>
+ internal IEnumerable<IOpenIdMessageExtension> UnsignedExtensions {
+ get { return this.extensions.OfType<IOpenIdMessageExtension>().Where(ext => !ext.IsSignedByRemoteParty); }
+ }
+
+ /// <summary>
/// Gets or sets the nonce that will protect the message from replay attacks.
/// </summary>
/// <value>
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
index a5802d8..d3181f0 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
@@ -133,6 +133,15 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Looks up a localized string similar to The {0} extension failed to deserialize and will be skipped. {1}.
+ /// </summary>
+ internal static string BadExtension {
+ get {
+ return ResourceManager.GetString("BadExtension", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Callback arguments are only supported when a {0} is provided to the {1}..
/// </summary>
internal static string CallbackArgumentsRequireSecretStore {
@@ -322,7 +331,7 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Looks up a localized string similar to The OpenId Provider issued an assertion for an Identifier whose discovery information did not match.
+ /// Looks up a localized string similar to The OpenID Provider issued an assertion for an Identifier whose discovery information did not match.
///Assertion endpoint info:
///{0}
///Discovered endpoint info:
@@ -389,7 +398,7 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Looks up a localized string similar to No XRDS document containing OpenId relying party endpoint information could be found at {0}..
+ /// Looks up a localized string similar to No XRDS document containing OpenID relying party endpoint information could be found at {0}..
/// </summary>
internal static string NoRelyingPartyEndpointDiscovered {
get {
@@ -416,7 +425,7 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Looks up a localized string similar to No OpenId endpoint found..
+ /// Looks up a localized string similar to No OpenID endpoint found..
/// </summary>
internal static string OpenIdEndpointNotFound {
get {
@@ -425,7 +434,7 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Looks up a localized string similar to No OpenId url is provided..
+ /// Looks up a localized string similar to No OpenID url is provided..
/// </summary>
internal static string OpenIdTextBoxEmpty {
get {
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
index 40a0d0b..18c8f2a 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
@@ -181,7 +181,7 @@
<value>Not a recognized XRI format: '{0}'.</value>
</data>
<data name="IssuedAssertionFailsIdentifierDiscovery" xml:space="preserve">
- <value>The OpenId Provider issued an assertion for an Identifier whose discovery information did not match.
+ <value>The OpenID Provider issued an assertion for an Identifier whose discovery information did not match.
Assertion endpoint info:
{0}
Discovered endpoint info:
@@ -206,7 +206,7 @@ Discovered endpoint info:
<value>Diffie-Hellman session type '{0}' not found for OpenID {1}.</value>
</data>
<data name="OpenIdEndpointNotFound" xml:space="preserve">
- <value>No OpenId endpoint found.</value>
+ <value>No OpenID endpoint found.</value>
</data>
<data name="OperationOnlyValidForSetupRequiredState" xml:space="preserve">
<value>This operation is only allowed when IAuthenticationResponse.State == AuthenticationStatus.SetupRequired.</value>
@@ -263,7 +263,7 @@ Discovered endpoint info:
<value>An authentication request has already been created using CreateRequest().</value>
</data>
<data name="OpenIdTextBoxEmpty" xml:space="preserve">
- <value>No OpenId url is provided.</value>
+ <value>No OpenID url is provided.</value>
</data>
<data name="ClientScriptExtensionPropertyNameCollision" xml:space="preserve">
<value>An extension with this property name ('{0}') has already been registered.</value>
@@ -278,7 +278,7 @@ Discovered endpoint info:
<value>This operation is not supported by serialized authentication responses. Try this operation from the LoggedIn event handler.</value>
</data>
<data name="NoRelyingPartyEndpointDiscovered" xml:space="preserve">
- <value>No XRDS document containing OpenId relying party endpoint information could be found at {0}.</value>
+ <value>No XRDS document containing OpenID relying party endpoint information could be found at {0}.</value>
</data>
<data name="AbsoluteUriRequired" xml:space="preserve">
<value>An absolute URI is required for this value.</value>
@@ -301,7 +301,10 @@ Discovered endpoint info:
<data name="RequireSslNotSatisfiedByAssertedClaimedId" xml:space="preserve">
<value>Sorry. This site only accepts OpenIDs that are HTTPS-secured, but {0} is not a secure Identifier.</value>
</data>
+ <data name="BadExtension" xml:space="preserve">
+ <value>The {0} extension failed to deserialize and will be skipped. {1}</value>
+ </data>
<data name="PositiveAssertionFromNonWhitelistedProvider" xml:space="preserve">
<value>An positive OpenID assertion was received from OP endpoint {0} that is not on this relying party's whitelist.</value>
</data>
-</root> \ No newline at end of file
+</root>
diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
index 6f0e7bf..1fed8a6 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
@@ -192,7 +192,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
// If the incoming request does not resemble an OpenID message at all,
// it's probably a user who just navigated to this URL, and we should
// just return null so the host can display a message to the user.
- if (httpRequestInfo.HttpMethod == "GET" && !httpRequestInfo.Url.QueryStringContainPrefixedParameters(Protocol.Default.openid.Prefix)) {
+ if (httpRequestInfo.HttpMethod == "GET" && !httpRequestInfo.UrlBeforeRewriting.QueryStringContainPrefixedParameters(Protocol.Default.openid.Prefix)) {
return null;
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
index 635a3c0..43e666d 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
@@ -114,7 +114,12 @@ namespace DotNetOpenAuth.OpenId.Provider {
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
- if (this.Enabled) {
+ // There is the unusual scenario that this control is hosted by
+ // an ASP.NET web page that has other UI on it to that the user
+ // might see, including controls that cause a postback to occur.
+ // We definitely want to ignore postbacks, since any openid messages
+ // they contain will be old.
+ if (this.Enabled && !this.Page.IsPostBack) {
// Use the explicitly given state store on this control if there is one.
// Then try the configuration file specified one. Finally, use the default
// in-memory one that's built into OpenIdProvider.
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs
index f70bbaa..5ab7ec4 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs
@@ -109,6 +109,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <returns>
/// The extension, if it is found. Null otherwise.
/// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension&lt;T&gt;"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
public T GetExtension<T>() where T : IOpenIdMessageExtension {
throw new NotSupportedException(OpenIdStrings.NotSupportedByAuthenticationSnapshot);
}
@@ -120,11 +133,73 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <returns>
/// The extension, if it is found. Null otherwise.
/// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
public IOpenIdMessageExtension GetExtension(Type extensionType) {
throw new NotSupportedException(OpenIdStrings.NotSupportedByAuthenticationSnapshot);
}
/// <summary>
+ /// Tries to get an OpenID extension that may be present in the response, without
+ /// requiring it to be signed by the Provider.
+ /// </summary>
+ /// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension&lt;T&gt;"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public T GetUntrustedExtension<T>() where T : IOpenIdMessageExtension {
+ throw new NotSupportedException(OpenIdStrings.NotSupportedByAuthenticationSnapshot);
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response.
+ /// </summary>
+ /// <param name="extensionType">Type of the extension to look for in the response.</param>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public IOpenIdMessageExtension GetUntrustedExtension(Type extensionType) {
+ throw new NotSupportedException(OpenIdStrings.NotSupportedByAuthenticationSnapshot);
+ }
+
+ /// <summary>
/// Gets all the callback arguments that were previously added using
/// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
/// of the return_to URL.
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs
index 391aa6e..0dc21bb 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs
@@ -143,6 +143,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <returns>
/// The extension, if it is found. Null otherwise.
/// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension&lt;T&gt;"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
public T GetExtension<T>() where T : IOpenIdMessageExtension {
return default(T);
}
@@ -154,10 +167,72 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <returns>
/// The extension, if it is found. Null otherwise.
/// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
public IOpenIdMessageExtension GetExtension(Type extensionType) {
return null;
}
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response, without
+ /// requiring it to be signed by the Provider.
+ /// </summary>
+ /// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension&lt;T&gt;"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public T GetUntrustedExtension<T>() where T : IOpenIdMessageExtension {
+ return default(T);
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response.
+ /// </summary>
+ /// <param name="extensionType">Type of the extension to look for in the response.</param>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public IOpenIdMessageExtension GetUntrustedExtension(Type extensionType) {
+ return null;
+ }
+
#endregion
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs
index 7df17b8..afca13d 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs
@@ -122,6 +122,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <returns>
/// The extension, if it is found. Null otherwise.
/// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension&lt;T&gt;"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "No parameter at all is required. T is used for return type.")]
T GetExtension<T>() where T : IOpenIdMessageExtension;
@@ -132,6 +145,66 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <returns>
/// The extension, if it is found. Null otherwise.
/// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
IOpenIdMessageExtension GetExtension(Type extensionType);
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response, without
+ /// requiring it to be signed by the Provider.
+ /// </summary>
+ /// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension&lt;T&gt;"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "No parameter at all is required. T is used for return type.")]
+ T GetUntrustedExtension<T>() where T : IOpenIdMessageExtension;
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response, without
+ /// requiring it to be signed by the Provider.
+ /// </summary>
+ /// <param name="extensionType">Type of the extension to look for in the response.</param>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ IOpenIdMessageExtension GetUntrustedExtension(Type extensionType);
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs
index 0a335c8..cd68a81 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs
@@ -168,6 +168,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <returns>
/// The extension, if it is found. Null otherwise.
/// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension&lt;T&gt;"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
public T GetExtension<T>() where T : IOpenIdMessageExtension {
return default(T);
}
@@ -179,10 +192,72 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <returns>
/// The extension, if it is found. Null otherwise.
/// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
public IOpenIdMessageExtension GetExtension(Type extensionType) {
return null;
}
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response, without
+ /// requiring it to be signed by the Provider.
+ /// </summary>
+ /// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension&lt;T&gt;"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public T GetUntrustedExtension<T>() where T : IOpenIdMessageExtension {
+ return default(T);
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response.
+ /// </summary>
+ /// <param name="extensionType">Type of the extension to look for in the response.</param>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public IOpenIdMessageExtension GetUntrustedExtension(Type extensionType) {
+ return null;
+ }
+
#endregion
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
index cf66de3..0ffe007 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
@@ -356,7 +356,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
Uri authUri = new Uri(formAuthData);
HttpRequestInfo clientResponseInfo = new HttpRequestInfo {
- Url = authUri,
+ UrlBeforeRewriting = authUri,
};
this.authenticationResponse = this.RelyingParty.GetResponse(clientResponseInfo);
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
index a065bcd..a7cf801 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
@@ -209,8 +209,21 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <returns>
/// The extension, if it is found. Null otherwise.
/// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension&lt;T&gt;"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
public T GetExtension<T>() where T : IOpenIdMessageExtension {
- return this.response.Extensions.OfType<T>().FirstOrDefault();
+ return this.response.SignedExtensions.OfType<T>().FirstOrDefault();
}
/// <summary>
@@ -220,8 +233,71 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <returns>
/// The extension, if it is found. Null otherwise.
/// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
public IOpenIdMessageExtension GetExtension(Type extensionType) {
ErrorUtilities.VerifyArgumentNotNull(extensionType, "extensionType");
+ return this.response.SignedExtensions.OfType<IOpenIdMessageExtension>().Where(ext => extensionType.IsInstanceOfType(ext)).FirstOrDefault();
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response, without
+ /// requiring it to be signed by the Provider.
+ /// </summary>
+ /// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension&lt;T&gt;"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public T GetUntrustedExtension<T>() where T : IOpenIdMessageExtension {
+ return this.response.Extensions.OfType<T>().FirstOrDefault();
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response.
+ /// </summary>
+ /// <param name="extensionType">Type of the extension to look for in the response.</param>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public IOpenIdMessageExtension GetUntrustedExtension(Type extensionType) {
+ ErrorUtilities.VerifyArgumentNotNull(extensionType, "extensionType");
return this.response.Extensions.OfType<IOpenIdMessageExtension>().Where(ext => extensionType.IsInstanceOfType(ext)).FirstOrDefault();
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
index 64c6099..f7ac3c2 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
@@ -86,20 +86,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
public TimeSpan PrivateSecretMaximumAge { get; set; }
/// <summary>
- /// Gets or sets a value indicating whether unsigned extension responses will be deserialized.
- /// </summary>
- /// <value>
- /// <c>false</c> to ignore unsigned extension responses; <c>true</c> to accept them.
- /// Default is <c>false</c>.
- /// </value>
- /// <remarks>
- /// This is an internal-only property because not requiring signed extensions is
- /// potentially dangerous. It is included here as an internal option primarily
- /// to enable testing.
- /// </remarks>
- internal bool AllowUnsignedIncomingExtensions { get; set; }
-
- /// <summary>
/// Fires the <see cref="RequireSslChanged"/> event.
/// </summary>
private void OnRequireSslChanged() {
diff --git a/src/DotNetOpenAuth/Yadis/Yadis.cs b/src/DotNetOpenAuth/Yadis/Yadis.cs
index 151aa63..14aea62 100644
--- a/src/DotNetOpenAuth/Yadis/Yadis.cs
+++ b/src/DotNetOpenAuth/Yadis/Yadis.cs
@@ -28,7 +28,11 @@ namespace DotNetOpenAuth.Yadis {
/// <summary>
/// Gets or sets the cache that can be used for HTTP requests made during identifier discovery.
/// </summary>
+#if DEBUG
+ internal static readonly RequestCachePolicy IdentifierDiscoveryCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.BypassCache);
+#else
internal static readonly RequestCachePolicy IdentifierDiscoveryCachePolicy = new HttpRequestCachePolicy(DotNetOpenAuthSection.Configuration.OpenId.CacheDiscovery ? HttpRequestCacheLevel.CacheIfAvailable : HttpRequestCacheLevel.BypassCache);
+#endif
/// <summary>
/// The maximum number of bytes to read from an HTTP response