summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs149
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs185
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs206
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs69
4 files changed, 280 insertions, 329 deletions
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs
index 84d5c0f..e2ddf16 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs
@@ -25,18 +25,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </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() {
@@ -112,6 +100,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// Inform ourselves in return_to that we're in a popup or iframe.
req.SetUntrustedCallbackArgument(OpenIdRelyingPartyAjaxControlBase.UIPopupCallbackKey, "1");
+ // We append a # at the end so that if the OP happens to support it,
+ // the OpenID response "query string" is appended after the hash rather than before, resulting in the
+ // browser being super-speedy in closing the popup window since it doesn't try to pull a newer version
+ // of the static resource down from the server merely because of a changed URL.
+ // http://www.nabble.com/Re:-Defining-how-OpenID-should-behave-with-fragments-in-the-return_to-url-p22694227.html
+ ////TODO:
+
yield return req;
}
}
@@ -150,136 +145,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <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);
- Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
-
- 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);
- Contract.Ensures(Contract.Result<OutgoingWebResponse>() != 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>
/// 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>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
index d363b93..1225540 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -12,7 +12,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System.ComponentModel;
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 DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
@@ -54,6 +58,22 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private readonly IList<IIdentifierDiscoveryService> discoveryServices = new List<IIdentifierDiscoveryService>(2);
/// <summary>
+ /// Backing field for the <see cref="NonVerifyingRelyingParty"/> property.
+ /// </summary>
+ private OpenIdRelyingParty nonVerifyingRelyingParty;
+
+ /// <summary>
+ /// The lock to obtain when initializing the <see cref="nonVerifyingRelyingParty"/> member.
+ /// </summary>
+ private object nonVerifyingRelyingPartyInitLock = new object();
+
+ /// <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>
/// Backing field for the <see cref="SecuritySettings"/> property.
/// </summary>
private RelyingPartySecuritySettings securitySettings;
@@ -270,6 +290,24 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
internal AssociationManager AssociationManager { get; private set; }
/// <summary>
+ /// Gets the <see cref="OpenIdRelyingParty"/> instance used to process authentication responses
+ /// without verifying the assertion or consuming nonces.
+ /// </summary>
+ protected OpenIdRelyingParty NonVerifyingRelyingParty {
+ get {
+ if (this.nonVerifyingRelyingParty == null) {
+ lock (this.nonVerifyingRelyingPartyInitLock) {
+ if (this.nonVerifyingRelyingParty == null) {
+ this.nonVerifyingRelyingParty = OpenIdRelyingParty.CreateNonVerifying();
+ }
+ }
+ }
+
+ return this.nonVerifyingRelyingParty;
+ }
+ }
+
+ /// <summary>
/// Creates an authentication request to verify that a user controls
/// some given Identifier.
/// </summary>
@@ -534,6 +572,32 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
}
+ /// <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 ProcessResponseFromPopup() {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
+ Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
+
+ return this.ProcessResponseFromPopup(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 ProcessResponseFromPopup(HttpRequestInfo request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
+
+ return this.ProcessResponseFromPopup(request, null);
+ }
+
#region IDisposable Members
/// <summary>
@@ -581,6 +645,86 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <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="ProcessResponseFromPopup()"/>.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "By design")]
+ internal 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>
+ /// <param name="request">The incoming HTTP request that is expected to carry an OpenID authentication response.</param>
+ /// <param name="callback">The callback fired after the response status has been determined but before the Javascript response is formulated.</param>
+ /// <returns>
+ /// The HTTP response to send to this HTTP request.
+ /// </returns>
+ internal OutgoingWebResponse ProcessResponseFromPopup(HttpRequestInfo request, Action<AuthenticationStatus> callback) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Ensures(Contract.Result<OutgoingWebResponse>() != 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.");
+
+ // Give the caller a chance to notify the hosting page and fill up the clientScriptExtensions collection.
+ if (callback != null) {
+ callback(authResponse.Status);
+ }
+
+ Logger.OpenId.DebugFormat("Popup or iframe callback from OP: {0}", request.Url);
+ Logger.Controls.DebugFormat(
+ "An authentication response was found in a popup window or iframe using a non-verifying RP with status: {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>
/// Performs discovery on the specified identifier.
/// </summary>
/// <param name="identifier">The identifier to discover services for.</param>
@@ -622,6 +766,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing) {
if (disposing) {
+ if (this.nonVerifyingRelyingParty != null) {
+ this.nonVerifyingRelyingParty.Dispose();
+ this.nonVerifyingRelyingParty = null;
+ }
+
// Tear off the instance member as a local variable for thread safety.
IDisposable disposableChannel = this.channel as IDisposable;
if (disposableChannel != null) {
@@ -631,6 +780,42 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <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>
/// Called by derived classes when behaviors are added or removed.
/// </summary>
/// <param name="sender">The collection being modified.</param>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
index c6c0a5d..7153aa2 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
@@ -86,11 +86,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const LogOnSiteNotification LogOnModeDefault = LogOnSiteNotification.None;
/// <summary>
- /// Backing field for the <see cref="RelyingPartyNonVerifying"/> property.
- /// </summary>
- private static OpenIdRelyingParty relyingPartyNonVerifying;
-
- /// <summary>
/// The authentication response that just came in.
/// </summary>
private IAuthenticationResponse authenticationResponse;
@@ -102,12 +97,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private string discoveryResult;
/// <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="OpenIdRelyingPartyAjaxControlBase"/> class.
/// </summary>
protected OpenIdRelyingPartyAjaxControlBase() {
@@ -152,6 +141,31 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets or sets the <see cref="OpenIdRelyingParty"/> instance to use.
+ /// </summary>
+ /// <value>
+ /// The default value is an <see cref="OpenIdRelyingParty"/> instance initialized according to the web.config file.
+ /// </value>
+ /// <remarks>
+ /// A performance optimization would be to store off the
+ /// instance as a static member in your web site and set it
+ /// to this property in your <see cref="Control.Load">Page.Load</see>
+ /// event since instantiating these instances can be expensive on
+ /// heavily trafficked web pages.
+ /// </remarks>
+ public override OpenIdRelyingParty RelyingParty {
+ get {
+ return base.RelyingParty;
+ }
+
+ set {
+ // Make sure we get an AJAX-ready instance.
+ ErrorUtilities.VerifyArgument(value is OpenIdAjaxRelyingParty, OpenIdStrings.TypeMustImplementX, typeof(OpenIdAjaxRelyingParty).Name);
+ base.RelyingParty = value;
+ }
+ }
+
+ /// <summary>
/// Gets the completed authentication response.
/// </summary>
public IAuthenticationResponse AuthenticationResponse {
@@ -193,22 +207,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Gets the name of the open id auth data form key (for the value as stored at the user agent as a FORM field).
+ /// Gets the relying party as its AJAX type.
/// </summary>
- /// <value>Usually a concatenation of the control's name and <c>"_openidAuthData"</c>.</value>
- protected abstract string OpenIdAuthDataFormKey { get; }
+ protected OpenIdAjaxRelyingParty AjaxRelyingParty {
+ get { return (OpenIdAjaxRelyingParty)this.RelyingParty; }
+ }
/// <summary>
- /// Gets the relying party to use when verification of incoming messages is NOT wanted.
+ /// Gets the name of the open id auth data form key (for the value as stored at the user agent as a FORM field).
/// </summary>
- private static OpenIdRelyingParty RelyingPartyNonVerifying {
- get {
- if (relyingPartyNonVerifying == null) {
- relyingPartyNonVerifying = OpenIdRelyingParty.CreateNonVerifying();
- }
- return relyingPartyNonVerifying;
- }
- }
+ /// <value>Usually a concatenation of the control's name and <c>"_openidAuthData"</c>.</value>
+ protected abstract string OpenIdAuthDataFormKey { get; }
/// <summary>
/// Gets or sets a value indicating whether an authentication in the page's view state
@@ -232,11 +241,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[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);
+ this.RelyingParty.RegisterClientScriptExtension<T>(propertyName);
}
#region ICallbackEventHandler Members
@@ -263,27 +268,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
/// <summary>
- /// Creates the authentication requests for a given user-supplied Identifier.
- /// </summary>
- /// <param name="identifier">The identifier to create a request for.</param>
- /// <returns>
- /// A sequence of authentication requests, any one of which may be
- /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.
- /// </returns>
- protected internal override IEnumerable<IAuthenticationRequest> CreateRequests(Identifier identifier) {
- // If this control is actually a member of another OpenID RP control,
- // delegate creation of requests to the parent control.
- var parentOwner = this.ParentControls.OfType<OpenIdRelyingPartyControlBase>().FirstOrDefault();
- if (parentOwner != null) {
- return parentOwner.CreateRequests(identifier);
- } else {
- // We delegate all our logic to another method, since invoking base. methods
- // within an iterator method results in unverifiable code.
- return this.CreateRequestsCore(base.CreateRequests(identifier));
- }
- }
-
- /// <summary>
/// Returns the results of a callback event that targets a control.
/// </summary>
/// <returns>The result of the callback.</returns>
@@ -309,6 +293,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Creates the relying party instance used to generate authentication requests.
+ /// </summary>
+ /// <param name="store">The store to pass to the relying party constructor.</param>
+ /// <returns>The instantiated relying party.</returns>
+ protected override OpenIdRelyingParty CreateRelyingParty(IRelyingPartyApplicationStore store) {
+ return new OpenIdAjaxRelyingParty(store);
+ }
+
+ /// <summary>
/// Pre-discovers an identifier and makes the results available to the
/// user agent for javascript as soon as the page loads.
/// </summary>
@@ -420,48 +413,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// Notifies the user agent via an AJAX response of a completed authentication attempt.
/// </summary>
protected override void ScriptClosingPopupOrIFrame() {
- Logger.OpenId.DebugFormat("AJAX (iframe) callback from OP: {0}", this.Page.Request.Url);
- string extensionsJson = null;
-
- var authResponse = RelyingPartyNonVerifying.GetResponse();
- Logger.Controls.DebugFormat(
- "The {0} control checked for an authentication response from a popup window or iframe using a non-verifying RP and found: {1}",
- this.ID,
- 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;
- }
+ Action<AuthenticationStatus> callback = status => {
+ if (status == AuthenticationStatus.Authenticated) {
+ this.OnUnconfirmedPositiveAssertion(); // event handler will fill the clientScriptExtensions collection.
}
+ };
- extensionsJson = MessagingUtilities.CreateJsonObject(extensionsDictionary, true);
- }
-
- string payload = "document.URL";
- if (Page.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(Page.Request.Url);
- payloadUri.AppendQueryArgs(Page.Request.Form.ToDictionary());
- payload = MessagingUtilities.GetSafeJavascriptValue(payloadUri.Uri.AbsoluteUri);
- }
-
- if (!string.IsNullOrEmpty(extensionsJson)) {
- payload += ", " + extensionsJson;
- }
+ OutgoingWebResponse response = this.RelyingParty.ProcessResponseFromPopup(
+ this.RelyingParty.Channel.GetRequestFromContext(),
+ callback);
- this.CallbackUserAgentMethod("dnoa_internal.processAuthorizationResult(" + payload + ")");
+ response.Send();
}
/// <summary>
@@ -548,47 +510,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Creates the authentication requests for a given user-supplied Identifier.
- /// </summary>
- /// <param name="requests">The authentication requests to prepare.</param>
- /// <returns>
- /// A sequence of authentication requests, any one of which may be
- /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.
- /// </returns>
- private IEnumerable<IAuthenticationRequest> CreateRequestsCore(IEnumerable<IAuthenticationRequest> requests) {
- ErrorUtilities.VerifyArgumentNotNull(requests, "requests"); // NO CODE CONTRACTS! (yield return used here)
-
- // 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(OPEndpointParameterName, req.Provider.Uri.AbsoluteUri);
- req.SetUntrustedCallbackArgument(ClaimedIdParameterName, (string)req.ClaimedIdentifier ?? string.Empty);
-
- // Inform ourselves in return_to that we're in a popup or iframe.
- req.SetUntrustedCallbackArgument(UIPopupCallbackKey, "1");
-
- // We append a # at the end so that if the OP happens to support it,
- // the OpenID response "query string" is appended after the hash rather than before, resulting in the
- // browser being super-speedy in closing the popup window since it doesn't try to pull a newer version
- // of the static resource down from the server merely because of a changed URL.
- // http://www.nabble.com/Re:-Defining-how-OpenID-should-behave-with-fragments-in-the-return_to-url-p22694227.html
- ////TODO:
-
- yield return req;
- }
- }
-
- /// <summary>
/// Constructs a function that will initiate an AJAX callback.
/// </summary>
/// <param name="async">if set to <c>true</c> causes the AJAX callback to be a little more asynchronous. Note that <c>false</c> does not mean the call is absolutely synchronous.</param>
@@ -615,33 +536,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <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 void CallbackUserAgentMethod(string methodCall) {
- Logger.OpenId.DebugFormat("Sending Javascript callback: {0}", methodCall);
- Page.Response.Write(@"<html><body><script language='javascript'>
- 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};
- }}
-</script></body></html>";
- Page.Response.Write(string.Format(CultureInfo.InvariantCulture, htmlFormat, methodCall));
- Page.Response.End();
- }
-
- /// <summary>
/// Sets the window.aspnetapppath variable on the user agent so that cookies can be set with the proper path.
/// </summary>
private void SetWebAppPathOnUserAgent() {
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
index 672bbfa..838b749 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
@@ -297,10 +297,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// heavily trafficked web pages.
/// </remarks>
[Browsable(false)]
- public OpenIdRelyingParty RelyingParty {
+ public virtual OpenIdRelyingParty RelyingParty {
get {
if (this.relyingParty == null) {
this.relyingParty = this.CreateRelyingParty();
+ this.ConfigureRelyingParty(this.relyingParty);
this.relyingPartyOwned = true;
}
return this.relyingParty;
@@ -560,8 +561,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
protected internal virtual IEnumerable<IAuthenticationRequest> CreateRequests(Identifier identifier) {
Contract.Requires<ArgumentNullException>(identifier != null);
- // Delegate to a private method to keep 'yield return' and Code Contract separate.
- return this.CreateRequestsCore(identifier);
+ // If this control is actually a member of another OpenID RP control,
+ // delegate creation of requests to the parent control.
+ var parentOwner = this.ParentControls.OfType<OpenIdRelyingPartyControlBase>().FirstOrDefault();
+ if (parentOwner != null) {
+ return parentOwner.CreateRequests(identifier);
+ } else {
+ // Delegate to a private method to keep 'yield return' and Code Contract separate.
+ return this.CreateRequestsCore(identifier);
+ }
}
/// <summary>
@@ -638,6 +646,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Notifies the user agent via an AJAX response of a completed authentication attempt.
+ /// </summary>
+ protected virtual void ScriptClosingPopupOrIFrame() {
+ this.RelyingParty.ProcessResponseFromPopup();
+ }
+
+ /// <summary>
/// Called when the <see cref="Identifier"/> property is changed.
/// </summary>
protected virtual void OnIdentifierChanged() {
@@ -773,29 +788,32 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// Creates the relying party instance used to generate authentication requests.
/// </summary>
/// <returns>The instantiated relying party.</returns>
- protected virtual OpenIdRelyingParty CreateRelyingParty() {
- return this.CreateRelyingParty(true);
+ protected OpenIdRelyingParty CreateRelyingParty() {
+ IRelyingPartyApplicationStore store = this.Stateless ? null : DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.ApplicationStore.CreateInstance(OpenIdRelyingParty.HttpApplicationStore);
+ return this.CreateRelyingParty(store);
}
/// <summary>
/// Creates the relying party instance used to generate authentication requests.
/// </summary>
- /// <param name="verifySignature">
- /// A value indicating whether message protections should be applied to the processed messages.
- /// Use <c>false</c> to postpone verification to a later time without invalidating nonces.
- /// </param>
+ /// <param name="store">The store to pass to the relying party constructor.</param>
/// <returns>The instantiated relying party.</returns>
- protected virtual OpenIdRelyingParty CreateRelyingParty(bool verifySignature) {
- IRelyingPartyApplicationStore store = this.Stateless ? null : DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.ApplicationStore.CreateInstance(OpenIdRelyingParty.HttpApplicationStore);
- var rp = verifySignature ? new OpenIdRelyingParty(store) : OpenIdRelyingParty.CreateNonVerifying();
+ protected virtual OpenIdRelyingParty CreateRelyingParty(IRelyingPartyApplicationStore store) {
+ return new OpenIdRelyingParty(store);
+ }
+
+ /// <summary>
+ /// Configures the relying party.
+ /// </summary>
+ /// <param name="relyingParty">The relying party.</param>
+ protected virtual void ConfigureRelyingParty(OpenIdRelyingParty relyingParty) {
+ Contract.Requires<ArgumentNullException>(relyingParty != null);
// Only set RequireSsl to true, as we don't want to override
// a .config setting of true with false.
if (this.RequireSsl) {
- rp.SecuritySettings.RequireSsl = true;
+ relyingParty.SecuritySettings.RequireSsl = true;
}
-
- return rp;
}
/// <summary>
@@ -845,21 +863,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Wires the popup window to close itself and pass the authentication result to the parent window.
- /// </summary>
- protected virtual void ScriptClosingPopupOrIFrame() {
- StringBuilder startupScript = new StringBuilder();
- startupScript.AppendLine("window.opener.dnoa_internal.processAuthorizationResult(document.URL);");
- startupScript.AppendLine("window.close();");
-
- this.Page.ClientScript.RegisterStartupScript(typeof(OpenIdRelyingPartyControlBase), "loginPopupClose", startupScript.ToString(), true);
-
- // TODO: alternately we should probably take over rendering this page here to avoid
- // a lot of unnecessary work on the server and possible momentary display of the
- // page in the popup window.
- }
-
- /// <summary>
/// Creates the identifier-persisting cookie, either for saving or deleting.
/// </summary>
/// <param name="response">The positive authentication response; or <c>null</c> to clear the cookie.</param>
@@ -943,7 +946,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
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());
+ // But beware that the extension MAY have already been added if we're using
+ // the OpenIdAjaxRelyingParty class.
+ if (!((AuthenticationRequest)req).Extensions.OfType<UIRequest>().Any()) {
+ 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.