summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj3
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/DuplicateRequestedHostsComparer.cs74
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs314
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs11
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs20
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs90
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartyUtilities.cs171
7 files changed, 422 insertions, 261 deletions
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index 4db7029..4847b37 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -529,8 +529,9 @@ http://opensource.org/licenses/ms-pl.html
<Compile Include="OpenId\RelyingParty\AssociationPreference.cs" />
<Compile Include="OpenId\RelyingParty\AuthenticationRequest.cs" />
<Compile Include="OpenId\RelyingParty\AuthenticationRequestMode.cs" />
+ <Compile Include="OpenId\RelyingParty\DuplicateRequestedHostsComparer.cs" />
<Compile Include="OpenId\RelyingParty\IProviderEndpoint.cs" />
- <Compile Include="OpenId\RelyingParty\RelyingPartyUtilities.cs" />
+ <Compile Include="OpenId\RelyingParty\OpenIdAjaxRelyingParty.cs" />
<Compile Include="OpenId\RelyingParty\SelectorButtonContract.cs" />
<Compile Include="OpenId\RelyingParty\SelectorProviderButton.cs" />
<Compile Include="OpenId\RelyingParty\SelectorOpenIdButton.cs" />
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/DuplicateRequestedHostsComparer.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/DuplicateRequestedHostsComparer.cs
new file mode 100644
index 0000000..94eb5ba
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/DuplicateRequestedHostsComparer.cs
@@ -0,0 +1,74 @@
+//-----------------------------------------------------------------------
+// <copyright file="DuplicateRequestedHostsComparer.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// An authentication request comparer that judges equality solely on the OP endpoint hostname.
+ /// </summary>
+ internal class DuplicateRequestedHostsComparer : IEqualityComparer<IAuthenticationRequest> {
+ /// <summary>
+ /// The singleton instance of this comparer.
+ /// </summary>
+ private static IEqualityComparer<IAuthenticationRequest> instance = new DuplicateRequestedHostsComparer();
+
+ /// <summary>
+ /// Prevents a default instance of the <see cref="DuplicateRequestedHostsComparer"/> class from being created.
+ /// </summary>
+ private DuplicateRequestedHostsComparer() {
+ }
+
+ /// <summary>
+ /// Gets the singleton instance of this comparer.
+ /// </summary>
+ internal static IEqualityComparer<IAuthenticationRequest> Instance {
+ get { return instance; }
+ }
+
+ #region IEqualityComparer<IAuthenticationRequest> Members
+
+ /// <summary>
+ /// Determines whether the specified objects are equal.
+ /// </summary>
+ /// <param name="x">The first object to compare.</param>
+ /// <param name="y">The second object to compare.</param>
+ /// <returns>
+ /// true if the specified objects are equal; otherwise, false.
+ /// </returns>
+ public bool Equals(IAuthenticationRequest x, IAuthenticationRequest y) {
+ if (x == null && y == null) {
+ return true;
+ }
+
+ if (x == null || y == null) {
+ return false;
+ }
+
+ // We'll distinguish based on the host name only, which
+ // admittedly is only a heuristic, but if we remove one that really wasn't a duplicate, well,
+ // this multiple OP attempt thing was just a convenience feature anyway.
+ return string.Equals(x.Provider.Uri.Host, y.Provider.Uri.Host, StringComparison.OrdinalIgnoreCase);
+ }
+
+ /// <summary>
+ /// Returns a hash code for the specified object.
+ /// </summary>
+ /// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param>
+ /// <returns>A hash code for the specified object.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.
+ /// </exception>
+ public int GetHashCode(IAuthenticationRequest obj) {
+ return obj.Provider.Uri.Host.GetHashCode();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs
new file mode 100644
index 0000000..92ce7e1
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs
@@ -0,0 +1,314 @@
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdAjaxRelyingParty.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.Linq;
+ using System.Net;
+ using System.Net.Mime;
+ using System.Text;
+ using System.Web;
+ using System.Web.Script.Serialization;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Extensions;
+ using DotNetOpenAuth.OpenId.Extensions.UI;
+
+ /// <summary>
+ /// Provides the programmatic facilities to act as an AJAX-enabled OpenID relying party.
+ /// </summary>
+ public class OpenIdAjaxRelyingParty : OpenIdRelyingParty {
+ /// <summary>
+ /// The <see cref="OpenIdRelyingParty"/> instance used to process authentication responses
+ /// without verifying the assertion or consuming nonces.
+ /// </summary>
+ private OpenIdRelyingParty nonVerifyingRelyingParty = OpenIdRelyingParty.CreateNonVerifying();
+
+ /// <summary>
+ /// A dictionary of extension response types and the javascript member
+ /// name to map them to on the user agent.
+ /// </summary>
+ private Dictionary<Type, string> clientScriptExtensions = new Dictionary<Type, string>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OpenIdAjaxRelyingParty"/> class.
+ /// </summary>
+ public OpenIdAjaxRelyingParty() {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OpenIdAjaxRelyingParty"/> class.
+ /// </summary>
+ /// <param name="applicationStore">The application store. If <c>null</c>, the relying party will always operate in "dumb mode".</param>
+ public OpenIdAjaxRelyingParty(IRelyingPartyApplicationStore applicationStore)
+ : base(applicationStore) {
+ }
+
+ /// <summary>
+ /// Performs discovery on some identifier on behalf of Javascript running on the browser.
+ /// </summary>
+ /// <param name="requests">The identifier discovery results to serialize as a JSON response.</param>
+ /// <returns>
+ /// The JSON result to return to the user agent.
+ /// </returns>
+ /// <remarks>
+ /// We prepare a JSON object with this interface:
+ /// <code>
+ /// class jsonResponse {
+ /// string claimedIdentifier;
+ /// Array requests; // never null
+ /// string error; // null if no error
+ /// }
+ /// </code>
+ /// Each element in the requests array looks like this:
+ /// <code>
+ /// class jsonAuthRequest {
+ /// string endpoint; // URL to the OP endpoint
+ /// string immediate; // URL to initiate an immediate request
+ /// string setup; // URL to initiate a setup request.
+ /// }
+ /// </code>
+ /// </remarks>
+ public OutgoingWebResponse AsAjaxDiscoveryResult(IEnumerable<IAuthenticationRequest> requests) {
+ Contract.Requires<ArgumentNullException>(requests != null);
+
+ string json = this.AsJsonDiscoveryResult(this.AsPopups(requests));
+ OutgoingWebResponse response = new OutgoingWebResponse();
+ response.Body = json;
+ return response;
+ }
+
+ /// <summary>
+ /// Allows an OpenID extension to read data out of an unverified positive authentication assertion
+ /// and send it down to the client browser so that Javascript running on the page can perform
+ /// some preprocessing on the extension data.
+ /// </summary>
+ /// <typeparam name="T">The extension <i>response</i> type that will read data from the assertion.</typeparam>
+ /// <param name="propertyName">The property name on the openid_identifier input box object that will be used to store the extension data. For example: sreg</param>
+ /// <remarks>
+ /// This method should be called before <see cref="ProcessAjaxOpenIdResponse()"/>.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "By design")]
+ public void RegisterClientScriptExtension<T>(string propertyName) where T : IClientScriptExtensionResponse {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(propertyName));
+ ErrorUtilities.VerifyArgumentNamed(!this.clientScriptExtensions.ContainsValue(propertyName), "propertyName", OpenIdStrings.ClientScriptExtensionPropertyNameCollision, propertyName);
+ foreach (var ext in this.clientScriptExtensions.Keys) {
+ ErrorUtilities.VerifyArgument(ext != typeof(T), OpenIdStrings.ClientScriptExtensionTypeCollision, typeof(T).FullName);
+ }
+ this.clientScriptExtensions.Add(typeof(T), propertyName);
+ }
+
+ /// <summary>
+ /// Processes the response received in a popup window or iframe to an AJAX-directed OpenID authentication.
+ /// </summary>
+ /// <returns>The HTTP response to send to this HTTP request.</returns>
+ /// <remarks>
+ /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para>
+ /// </remarks>
+ public OutgoingWebResponse ProcessAjaxOpenIdResponse() {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
+ return this.ProcessAjaxOpenIdResponse(this.Channel.GetRequestFromContext());
+ }
+
+ /// <summary>
+ /// Processes the response received in a popup window or iframe to an AJAX-directed OpenID authentication.
+ /// </summary>
+ /// <param name="request">The incoming HTTP request that is expected to carry an OpenID authentication response.</param>
+ /// <returns>The HTTP response to send to this HTTP request.</returns>
+ public OutgoingWebResponse ProcessAjaxOpenIdResponse(HttpRequestInfo request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+
+ string extensionsJson = null;
+ var authResponse = this.nonVerifyingRelyingParty.GetResponse();
+ ErrorUtilities.VerifyProtocol(authResponse != null, "OpenID popup window or iframe did not recognize an OpenID response in the request.");
+ Logger.OpenId.DebugFormat("AJAX (iframe) callback from OP: {0}", request.Url);
+ Logger.Controls.DebugFormat(
+ "The MVC controller checked for an authentication response from a popup window or iframe using a non-verifying RP and found: {0}",
+ authResponse.Status);
+ if (authResponse.Status == AuthenticationStatus.Authenticated) {
+ var extensionsDictionary = new Dictionary<string, string>();
+ foreach (var pair in this.clientScriptExtensions) {
+ IClientScriptExtensionResponse extension = (IClientScriptExtensionResponse)authResponse.GetExtension(pair.Key);
+ if (extension == null) {
+ continue;
+ }
+ var positiveResponse = (PositiveAuthenticationResponse)authResponse;
+ string js = extension.InitializeJavaScriptData(positiveResponse.Response);
+ if (!string.IsNullOrEmpty(js)) {
+ extensionsDictionary[pair.Value] = js;
+ }
+ }
+
+ extensionsJson = MessagingUtilities.CreateJsonObject(extensionsDictionary, true);
+ }
+
+ string payload = "document.URL";
+ if (request.HttpMethod == "POST") {
+ // Promote all form variables to the query string, but since it won't be passed
+ // to any server (this is a javascript window-to-window transfer) the length of
+ // it can be arbitrarily long, whereas it was POSTed here probably because it
+ // was too long for HTTP transit.
+ UriBuilder payloadUri = new UriBuilder(request.Url);
+ payloadUri.AppendQueryArgs(request.Form.ToDictionary());
+ payload = MessagingUtilities.GetSafeJavascriptValue(payloadUri.Uri.AbsoluteUri);
+ }
+
+ if (!string.IsNullOrEmpty(extensionsJson)) {
+ payload += ", " + extensionsJson;
+ }
+
+ return InvokeParentPageScript("dnoa_internal.processAuthorizationResult(" + payload + ")");
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected override void Dispose(bool disposing) {
+ this.nonVerifyingRelyingParty.Dispose();
+ base.Dispose(disposing);
+ }
+
+ /// <summary>
+ /// Invokes a method on a parent frame or window and closes the calling popup window if applicable.
+ /// </summary>
+ /// <param name="methodCall">The method to call on the parent window, including
+ /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param>
+ /// <returns>The entire HTTP response to send to the popup window or iframe to perform the invocation.</returns>
+ private static OutgoingWebResponse InvokeParentPageScript(string methodCall) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(methodCall));
+
+ Logger.OpenId.DebugFormat("Sending Javascript callback: {0}", methodCall);
+ StringBuilder builder = new StringBuilder();
+ builder.AppendLine("<html><body><script type='text/javascript' language='javascript'><!--");
+ builder.AppendLine("//<![CDATA[");
+ builder.Append(@" var inPopup = !window.frameElement;
+ var objSrc = inPopup ? window.opener : window.frameElement;
+");
+
+ // Something about calling objSrc.{0} can somehow cause FireFox to forget about the inPopup variable,
+ // so we have to actually put the test for it ABOVE the call to objSrc.{0} so that it already
+ // whether to call window.self.close() after the call.
+ string htmlFormat = @" if (inPopup) {{
+ objSrc.{0};
+ window.self.close();
+ }} else {{
+ objSrc.{0};
+ }}";
+ builder.AppendFormat(CultureInfo.InvariantCulture, htmlFormat, methodCall);
+ builder.AppendLine("//]]>--></script>");
+ builder.AppendLine("</body></html>");
+
+ var response = new OutgoingWebResponse();
+ response.Body = builder.ToString();
+ response.Headers.Add(HttpResponseHeader.ContentType, new ContentType("text/html").ToString());
+ return response;
+ }
+
+ /// <summary>
+ /// Prepares authentication requests for use as AJAX-initiated logins.
+ /// </summary>
+ /// <param name="requests">The authentication requests to prepare for AJAX.</param>
+ /// <returns>The AJAX-ified authentication requests</returns>
+ /// <remarks>
+ /// The original requests are altered as part of executing this method.
+ /// </remarks>
+ private IEnumerable<IAuthenticationRequest> AsPopups(IEnumerable<IAuthenticationRequest> requests) {
+ Contract.Requires<ArgumentNullException>(requests != null);
+
+ // Some OPs may be listed multiple times (one with HTTPS and the other with HTTP, for example).
+ // Since we're gathering OPs to try one after the other, just take the first choice of each OP
+ // and don't try it multiple times.
+ requests = requests.Distinct(DuplicateRequestedHostsComparer.Instance);
+
+ // Configure each generated request.
+ foreach (var req in requests) {
+ // Inform ourselves in return_to that we're in a popup.
+ req.SetUntrustedCallbackArgument(OpenIdRelyingPartyControlBase.UIPopupCallbackKey, "1");
+
+ if (req.DiscoveryResult.IsExtensionSupported<UIRequest>()) {
+ // Inform the OP that we'll be using a popup window consistent with the UI extension.
+ req.AddExtension(new UIRequest());
+
+ // Provide a hint for the client javascript about whether the OP supports the UI extension.
+ // This is so the window can be made the correct size for the extension.
+ // If the OP doesn't advertise support for the extension, the javascript will use
+ // a bigger popup window.
+ req.SetUntrustedCallbackArgument(OpenIdRelyingPartyControlBase.PopupUISupportedJSHint, "1");
+ }
+
+ yield return req;
+ }
+ }
+
+ /// <summary>
+ /// Converts a sequence of authentication requests to a JSON object for seeding an AJAX-enabled login page.
+ /// </summary>
+ /// <param name="requests">The authentication requests.</param>
+ /// <returns>A JSON string.</returns>
+ private string AsJsonDiscoveryResult(IEnumerable<IAuthenticationRequest> requests) {
+ requests = requests.CacheGeneratedResults();
+
+ // Configure each generated request.
+ int reqIndex = 0;
+ foreach (var req in requests) {
+ req.SetUntrustedCallbackArgument("index", (reqIndex++).ToString(CultureInfo.InvariantCulture));
+
+ // If the ReturnToUrl was explicitly set, we'll need to reset our first parameter
+ if (string.IsNullOrEmpty(HttpUtility.ParseQueryString(req.ReturnToUrl.Query)[AuthenticationRequest.UserSuppliedIdentifierParameterName])) {
+ Identifier userSuppliedIdentifier = ((AuthenticationRequest)req).DiscoveryResult.UserSuppliedIdentifier;
+ req.SetUntrustedCallbackArgument(AuthenticationRequest.UserSuppliedIdentifierParameterName, userSuppliedIdentifier.OriginalString);
+ }
+
+ // Our javascript needs to let the user know which endpoint responded. So we force it here.
+ // This gives us the info even for 1.0 OPs and 2.0 setup_required responses.
+ req.SetUntrustedCallbackArgument(OpenIdRelyingPartyAjaxControlBase.OPEndpointParameterName, req.Provider.Uri.AbsoluteUri);
+ req.SetUntrustedCallbackArgument(OpenIdRelyingPartyAjaxControlBase.ClaimedIdParameterName, (string)req.ClaimedIdentifier ?? string.Empty);
+
+ // Inform ourselves in return_to that we're in a popup or iframe.
+ req.SetUntrustedCallbackArgument(OpenIdRelyingPartyAjaxControlBase.UIPopupCallbackKey, "1");
+ }
+
+ JavaScriptSerializer serializer = new JavaScriptSerializer();
+ string json;
+ if (requests.Any()) {
+ json = serializer.Serialize(new {
+ claimedIdentifier = requests.First().ClaimedIdentifier,
+ requests = requests.Select(req => new {
+ endpoint = req.Provider.Uri.AbsoluteUri,
+ immediate = this.GetRedirectUrl(req, true),
+ setup = this.GetRedirectUrl(req, false),
+ }).ToArray()
+ });
+ } else {
+ json = serializer.Serialize(new {
+ requests = new object[0],
+ error = OpenIdStrings.OpenIdEndpointNotFound,
+ });
+ }
+
+ return json;
+ }
+
+ /// <summary>
+ /// Gets the full URL that carries an OpenID message, even if it exceeds the normal maximum size of a URL,
+ /// for purposes of sending to an AJAX component running in the browser.
+ /// </summary>
+ /// <param name="request">The authentication request.</param>
+ /// <param name="immediate"><c>true</c>to create a checkid_immediate request;
+ /// <c>false</c> to create a checkid_setup request.</param>
+ /// <returns>The absolute URL that carries the entire OpenID message.</returns>
+ private Uri GetRedirectUrl(IAuthenticationRequest request, bool immediate) {
+ Contract.Requires<ArgumentNullException>(request != null);
+
+ request.Mode = immediate ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup;
+ return request.RedirectingResponse.GetDirectUriRequest(this.Channel);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
index a0a0fb9..052f4b0 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -33,10 +33,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
public delegate bool EndpointSelector(IProviderEndpoint endpoint);
/// <summary>
- /// Provides the programmatic facilities to act as an OpenId consumer.
+ /// Provides the programmatic facilities to act as an OpenID relying party.
/// </summary>
[ContractVerification(true)]
- public sealed class OpenIdRelyingParty : IDisposable {
+ public class OpenIdRelyingParty : IDisposable {
/// <summary>
/// The name of the key to use in the HttpApplication cache to store the
/// instance of <see cref="StandardRelyingPartyApplicationStore"/> to use.
@@ -78,7 +78,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Initializes a new instance of the <see cref="OpenIdRelyingParty"/> class.
/// </summary>
- /// <param name="applicationStore">The application store. If null, the relying party will always operate in "dumb mode".</param>
+ /// <param name="applicationStore">The application store. If <c>null</c>, the relying party will always operate in "dumb mode".</param>
public OpenIdRelyingParty(IRelyingPartyApplicationStore applicationStore)
: this(applicationStore, applicationStore) {
}
@@ -395,7 +395,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
Contract.Requires<ArgumentNullException>(returnToUrl != null);
Contract.Ensures(Contract.Result<IEnumerable<IAuthenticationRequest>>() != null);
- return AuthenticationRequest.Create(userSuppliedIdentifier, this, realm, returnToUrl, true).Cast<IAuthenticationRequest>();
+ return AuthenticationRequest.Create(userSuppliedIdentifier, this, realm, returnToUrl, true).Cast<IAuthenticationRequest>().CacheGeneratedResults();
}
/// <summary>
@@ -487,6 +487,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para>
/// </remarks>
public IAuthenticationResponse GetResponse() {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
return this.GetResponse(this.Channel.GetRequestFromContext());
}
@@ -619,7 +620,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- private void Dispose(bool disposing) {
+ protected virtual void Dispose(bool disposing) {
if (disposing) {
// Tear off the instance member as a local variable for thread safety.
IDisposable disposableChannel = this.channel as IDisposable;
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
index 12d676b..1de2fc2 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
@@ -31,6 +31,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
internal const string EmbeddedAjaxJavascriptResource = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdRelyingPartyAjaxControlBase.js";
/// <summary>
+ /// The "dnoa.op_endpoint" string.
+ /// </summary>
+ internal const string OPEndpointParameterName = OpenIdUtilities.CustomParameterPrefix + "op_endpoint";
+
+ /// <summary>
+ /// The "dnoa.claimed_id" string.
+ /// </summary>
+ internal const string ClaimedIdParameterName = OpenIdUtilities.CustomParameterPrefix + "claimed_id";
+
+ /// <summary>
/// The name of the javascript function that will initiate a synchronous callback.
/// </summary>
protected const string CallbackJSFunction = "window.dnoa_internal.callback";
@@ -46,16 +56,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
private const string MaxPositiveAssertionLifetimeJsName = "window.dnoa_internal.maxPositiveAssertionLifetime";
- /// <summary>
- /// The "dnoa.op_endpoint" string.
- /// </summary>
- private const string OPEndpointParameterName = OpenIdUtilities.CustomParameterPrefix + "op_endpoint";
-
- /// <summary>
- /// The "dnoa.claimed_id" string.
- /// </summary>
- private const string ClaimedIdParameterName = OpenIdUtilities.CustomParameterPrefix + "claimed_id";
-
#region Property viewstate keys
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
index 2c6c59d..1031e4f 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
@@ -92,6 +92,21 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
internal const string ReturnToReceivingControlId = OpenIdUtilities.CustomParameterPrefix + "receiver";
+ #region Protected internal callback parameter names
+
+ /// <summary>
+ /// The callback parameter to use for recognizing when the callback is in a popup window or hidden iframe.
+ /// </summary>
+ protected internal const string UIPopupCallbackKey = OpenIdUtilities.CustomParameterPrefix + "uipopup";
+
+ /// <summary>
+ /// The parameter name to include in the formulated auth request so that javascript can know whether
+ /// the OP advertises support for the UI extension.
+ /// </summary>
+ protected internal const string PopupUISupportedJSHint = OpenIdUtilities.CustomParameterPrefix + "popupUISupported";
+
+ #endregion
+
#region Property category constants
/// <summary>
@@ -111,18 +126,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
- #region Callback parameter names
-
- /// <summary>
- /// The callback parameter to use for recognizing when the callback is in a popup window or hidden iframe.
- /// </summary>
- protected const string UIPopupCallbackKey = OpenIdUtilities.CustomParameterPrefix + "uipopup";
-
- /// <summary>
- /// The parameter name to include in the formulated auth request so that javascript can know whether
- /// the OP advertises support for the UI extension.
- /// </summary>
- protected const string PopupUISupportedJSHint = OpenIdUtilities.CustomParameterPrefix + "popupUISupported";
+ #region Private callback parameter names
/// <summary>
/// The callback parameter for use with persisting the <see cref="UsePersistentCookie"/> property.
@@ -1031,67 +1035,5 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
return false;
}
-
- /// <summary>
- /// An authentication request comparer that judges equality solely on the OP endpoint hostname.
- /// </summary>
- private class DuplicateRequestedHostsComparer : IEqualityComparer<IAuthenticationRequest> {
- /// <summary>
- /// The singleton instance of this comparer.
- /// </summary>
- private static IEqualityComparer<IAuthenticationRequest> instance = new DuplicateRequestedHostsComparer();
-
- /// <summary>
- /// Prevents a default instance of the <see cref="DuplicateRequestedHostsComparer"/> class from being created.
- /// </summary>
- private DuplicateRequestedHostsComparer() {
- }
-
- /// <summary>
- /// Gets the singleton instance of this comparer.
- /// </summary>
- internal static IEqualityComparer<IAuthenticationRequest> Instance {
- get { return instance; }
- }
-
- #region IEqualityComparer<IAuthenticationRequest> Members
-
- /// <summary>
- /// Determines whether the specified objects are equal.
- /// </summary>
- /// <param name="x">The first object to compare.</param>
- /// <param name="y">The second object to compare.</param>
- /// <returns>
- /// true if the specified objects are equal; otherwise, false.
- /// </returns>
- public bool Equals(IAuthenticationRequest x, IAuthenticationRequest y) {
- if (x == null && y == null) {
- return true;
- }
-
- if (x == null || y == null) {
- return false;
- }
-
- // We'll distinguish based on the host name only, which
- // admittedly is only a heuristic, but if we remove one that really wasn't a duplicate, well,
- // this multiple OP attempt thing was just a convenience feature anyway.
- return string.Equals(x.Provider.Uri.Host, y.Provider.Uri.Host, StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// Returns a hash code for the specified object.
- /// </summary>
- /// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param>
- /// <returns>A hash code for the specified object.</returns>
- /// <exception cref="T:System.ArgumentNullException">
- /// The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.
- /// </exception>
- public int GetHashCode(IAuthenticationRequest obj) {
- return obj.Provider.Uri.Host.GetHashCode();
- }
-
- #endregion
- }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartyUtilities.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartyUtilities.cs
deleted file mode 100644
index 8c3438a..0000000
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartyUtilities.cs
+++ /dev/null
@@ -1,171 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="RelyingPartyUtilities.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.RelyingParty {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.Contracts;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Text;
- using System.Web;
- using System.Web.Mvc;
- using System.Web.UI;
- using DotNetOpenAuth.Messaging;
-
- public static class RelyingPartyUtilities {
- public static string OpenIdAjaxTextBox(this HtmlHelper helper, string name) {
- Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name));
-
- var box = new OpenIdAjaxTextBox();
- box.Name = name;
-
- StringWriter sw = new StringWriter();
- HtmlTextWriter writer = new HtmlTextWriter(sw);
- box.RenderControl(writer);
- return sw.ToString();
- }
-
- /// <summary>
- /// Performs discovery on some identifier on behalf of Javascript running on the browser.
- /// </summary>
- /// <param name="identifier">The identifier on which to perform discovery.</param>
- /// <param name="realm">The realm of the relying party.</param>
- /// <param name="returnTo">The URL that receives OpenID responses.</param>
- /// <returns>The JSON result to return to the user agent.</returns>
- /// <remarks>
- /// We prepare a JSON object with this interface:
- /// class jsonResponse {
- /// string claimedIdentifier;
- /// Array requests; // never null
- /// string error; // null if no error
- /// }
- /// Each element in the requests array looks like this:
- /// class jsonAuthRequest {
- /// string endpoint; // URL to the OP endpoint
- /// string immediate; // URL to initiate an immediate request
- /// string setup; // URL to initiate a setup request.
- /// }
- /// </remarks>
- public static JsonResult AjaxDiscover(Identifier identifier, Realm realm, Uri returnTo, Action<IAuthenticationRequest> attachExtensions) {
- Contract.Requires<ArgumentNullException>(identifier != null);
- Contract.Requires<ArgumentNullException>(realm != null);
- Contract.Requires<ArgumentNullException>(returnTo != null);
-
- OpenIdSelector selector = new OpenIdSelector();
- selector.RealmUrl = realm;
- selector.ReturnToUrl = returnTo.AbsoluteUri;
- selector.ID = "MVC";
- var requests = selector.CreateRequests(identifier).CacheGeneratedResults();
- if (requests.Any()) {
- if (attachExtensions != null) {
- foreach (var request in requests) {
- attachExtensions(request);
- }
- }
- return new JsonResult {
- Data = new {
- claimedIdentifier = requests.First().ClaimedIdentifier,
- requests = requests.Select(req => new {
- endpoint = req.Provider.Uri.AbsoluteUri,
- immediate = GetRedirectUrl(selector.RelyingParty, req, true),
- setup = GetRedirectUrl(selector.RelyingParty, req, false),
- }).ToArray()
- },
- };
- } else {
- return new JsonResult {
- Data = new {
- requests = new object[0],
- error = "No OpenID endpoint found",
- }
- };
- }
- }
-
- public static ActionResult AjaxReturnTo(HttpRequestBase request) {
- Contract.Requires<ArgumentNullException>(request != null);
-
- Logger.OpenId.DebugFormat("AJAX (iframe) callback from OP: {0}", request.Url);
- string extensionsJson = null;
- var relyingPartyNonVerifying = OpenIdRelyingParty.CreateNonVerifying();
- var authResponse = relyingPartyNonVerifying.GetResponse();
- Logger.Controls.DebugFormat(
- "The MVC controller checked for an authentication response from a popup window or iframe using a non-verifying RP and found: {0}",
- authResponse.Status);
- if (authResponse.Status == AuthenticationStatus.Authenticated) {
- //this.OnUnconfirmedPositiveAssertion(); // event handler will fill the clientScriptExtensions collection.
- var extensionsDictionary = new Dictionary<string, string>();
- //foreach (var pair in this.clientScriptExtensions) {
- // IClientScriptExtensionResponse extension = (IClientScriptExtensionResponse)authResponse.GetExtension(pair.Key);
- // if (extension == null) {
- // continue;
- // }
- // var positiveResponse = (PositiveAuthenticationResponse)authResponse;
- // string js = extension.InitializeJavaScriptData(positiveResponse.Response);
- // if (!string.IsNullOrEmpty(js)) {
- // extensionsDictionary[pair.Value] = js;
- // }
- //}
-
- extensionsJson = MessagingUtilities.CreateJsonObject(extensionsDictionary, true);
- }
-
- string payload = "document.URL";
- if (request.HttpMethod == "POST") {
- // Promote all form variables to the query string, but since it won't be passed
- // to any server (this is a javascript window-to-window transfer) the length of
- // it can be arbitrarily long, whereas it was POSTed here probably because it
- // was too long for HTTP transit.
- UriBuilder payloadUri = new UriBuilder(request.Url);
- payloadUri.AppendQueryArgs(request.Form.ToDictionary());
- payload = MessagingUtilities.GetSafeJavascriptValue(payloadUri.Uri.AbsoluteUri);
- }
-
- if (!string.IsNullOrEmpty(extensionsJson)) {
- payload += ", " + extensionsJson;
- }
-
- return CallbackUserAgentMethod("dnoa_internal.processAuthorizationResult(" + payload + ")");
- }
-
- private static Uri GetRedirectUrl(OpenIdRelyingParty rp, IAuthenticationRequest request, bool immediate) {
- request.Mode = immediate ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup;
- return request.RedirectingResponse.GetDirectUriRequest(rp.Channel);
- }
-
- /// <summary>
- /// Invokes a method on a parent frame/window's OpenIdAjaxTextBox,
- /// and closes the calling popup window if applicable.
- /// </summary>
- /// <param name="methodCall">The method to call on the OpenIdAjaxTextBox, including
- /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param>
- private static ActionResult CallbackUserAgentMethod(string methodCall) {
- Logger.OpenId.DebugFormat("Sending Javascript callback: {0}", methodCall);
- StringBuilder builder = new StringBuilder();
- builder.AppendLine("<html><body><script type='text/javascript' language='javascript'><!--");
- builder.AppendLine("//<![CDATA[");
- builder.Append(@" var inPopup = !window.frameElement;
- var objSrc = inPopup ? window.opener : window.frameElement;
-");
-
- // Something about calling objSrc.{0} can somehow cause FireFox to forget about the inPopup variable,
- // so we have to actually put the test for it ABOVE the call to objSrc.{0} so that it already
- // whether to call window.self.close() after the call.
- string htmlFormat = @" if (inPopup) {{
- objSrc.{0};
- window.self.close();
- }} else {{
- objSrc.{0};
- }}";
- builder.AppendFormat(CultureInfo.InvariantCulture, htmlFormat, methodCall);
- builder.AppendLine("//]]>--></script>");
- builder.AppendLine("</body></html>");
- return new ContentResult { Content = builder.ToString(), ContentType = "text/html" };
- }
- }
-}