summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2009-07-23 20:40:21 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2009-07-23 20:40:21 -0700
commitb3086860a2f285cf4b40526b9d49c85cf1451268 (patch)
treed59701d99451339e0d6ba35b76cce27b71acee0b
parent75289bf74be74a2fefeceaa8868ef65a008a5692 (diff)
downloadDotNetOpenAuth-b3086860a2f285cf4b40526b9d49c85cf1451268.zip
DotNetOpenAuth-b3086860a2f285cf4b40526b9d49c85cf1451268.tar.gz
DotNetOpenAuth-b3086860a2f285cf4b40526b9d49c85cf1451268.tar.bz2
OpenIdAjaxTextBox rewritten to derive from OpenIdRelyingPartyAjaxControlBase.
A great deal of refactoring went into this, but of C# and Javascript code. The upside to this is now we can write many more AJAX controls (auto-login, for example) with minimal code. Note that the OpenIdAjaxTextBox control is not yet back up to par with the prior one. Postbacks don't yet properly deserialize the 'authenticated' state of the page.
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs2
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj8
-rw-r--r--src/DotNetOpenAuth/OpenId/Identifier.cs9
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs885
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js542
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs300
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js385
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs178
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js5
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshot.cs (renamed from src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs)8
12 files changed, 924 insertions, 1402 deletions
diff --git a/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs b/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs
index 669e4d3..bb52f65 100644
--- a/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs
@@ -24,7 +24,7 @@ namespace DotNetOpenAuth.Test.Mocks {
private Identifier wrappedIdentifier;
public MockIdentifier(Identifier wrappedIdentifier, MockHttpRequest mockHttpRequest, IEnumerable<ServiceEndpoint> endpoints)
- : base(false) {
+ : base(wrappedIdentifier.OriginalString, false) {
ErrorUtilities.VerifyArgumentNotNull(wrappedIdentifier, "wrappedIdentifier");
ErrorUtilities.VerifyArgumentNotNull(mockHttpRequest, "mockHttpRequest");
ErrorUtilities.VerifyArgumentNotNull(endpoints, "endpoints");
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index 8f7715e..6f161be 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -147,9 +147,15 @@
<Reference Include="System.IdentityModel.Selectors">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
+ <Reference Include="System.Runtime.Serialization">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
<Reference Include="System.ServiceModel">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
+ <Reference Include="System.ServiceModel.Web">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
<Reference Include="System.Web" />
<Reference Include="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -492,7 +498,7 @@
<Compile Include="OpenId\ProviderEndpointDescription.cs" />
<Compile Include="OpenId\Provider\ProviderSecuritySettings.cs" />
<Compile Include="OpenId\RelyingParty\IRelyingPartyApplicationStore.cs" />
- <Compile Include="OpenId\RelyingParty\AuthenticationResponseSnapshot.cs" />
+ <Compile Include="OpenId\RelyingParty\PositiveAuthenticationResponseSnapshot.cs" />
<Compile Include="OpenId\RelyingParty\PrivateSecretManager.cs" />
<Compile Include="OpenId\RelyingParty\RelyingPartySecuritySettings.cs" />
<Compile Include="OpenId\RelyingParty\ServiceEndpoint.cs" />
diff --git a/src/DotNetOpenAuth/OpenId/Identifier.cs b/src/DotNetOpenAuth/OpenId/Identifier.cs
index 6a03660..435dc00 100644
--- a/src/DotNetOpenAuth/OpenId/Identifier.cs
+++ b/src/DotNetOpenAuth/OpenId/Identifier.cs
@@ -24,17 +24,16 @@ namespace DotNetOpenAuth.OpenId {
/// <summary>
/// Initializes a new instance of the <see cref="Identifier"/> class.
/// </summary>
- /// <param name="isDiscoverySecureEndToEnd">
- /// Whether the derived class is prepared to guarantee end-to-end discovery
- /// and initial redirect for authentication is performed using SSL.
- /// </param>
+ /// <param name="originalString">The original string before any normalization.</param>
+ /// <param name="isDiscoverySecureEndToEnd">Whether the derived class is prepared to guarantee end-to-end discovery
+ /// and initial redirect for authentication is performed using SSL.</param>
protected Identifier(string originalString, bool isDiscoverySecureEndToEnd) {
this.OriginalString = originalString;
this.IsDiscoverySecureEndToEnd = isDiscoverySecureEndToEnd;
}
/// <summary>
- /// Gets or sets the original string that was normalized to create this Identifier.
+ /// Gets the original string that was normalized to create this Identifier.
/// </summary>
public string OriginalString { get; private set; }
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
index 2da537c..b3fe500 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
@@ -18,19 +18,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using System.Drawing.Design;
+ using System.Diagnostics.Contracts;
using System.Globalization;
- using System.Linq;
using System.Text;
- using System.Text.RegularExpressions;
- using System.Web;
using System.Web.UI;
- using System.Web.UI.WebControls;
using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.ChannelElements;
- using DotNetOpenAuth.OpenId.Extensions;
- using DotNetOpenAuth.OpenId.Extensions.UI;
/// <summary>
/// An ASP.NET control that provides a minimal text box that is OpenID-aware and uses AJAX for
@@ -38,7 +30,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
[DefaultProperty("Text"), ValidationProperty("Text")]
[ToolboxData("<{0}:OpenIdAjaxTextBox runat=\"server\" />")]
- public sealed class OpenIdAjaxTextBox : WebControl, ICallbackEventHandler, ITextControl {
+ public class OpenIdAjaxTextBox : OpenIdRelyingPartyAjaxControlBase, ICallbackEventHandler, IEditableTextControl, ITextControl, IPostBackDataHandler {
/// <summary>
/// The name of the manifest stream containing the OpenIdAjaxTextBox.js file.
/// </summary>
@@ -72,19 +64,14 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string ColumnsViewStateKey = "Columns";
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="OnClientAssertionReceived"/> property.
- /// </summary>
- private const string OnClientAssertionReceivedViewStateKey = "OnClientAssertionReceived";
-
- /// <summary>
- /// The viewstate key to use for storing the value of the <see cref="AuthenticationResponse"/> property.
+ /// The viewstate key to use for the <see cref="CssClass"/> property.
/// </summary>
- private const string AuthenticationResponseViewStateKey = "AuthenticationResponse";
+ private const string CssClassViewStateKey = "CssClass";
/// <summary>
- /// The viewstate key to use for storing the value of the a successful authentication.
+ /// The viewstate key to use for storing the value of the <see cref="OnClientAssertionReceived"/> property.
/// </summary>
- private const string AuthDataViewStateKey = "AuthData";
+ private const string OnClientAssertionReceivedViewStateKey = "OnClientAssertionReceived";
/// <summary>
/// The viewstate key to use for storing the value of the <see cref="AuthenticatedAsToolTip"/> property.
@@ -97,16 +84,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string AuthenticationSucceededToolTipViewStateKey = "AuthenticationSucceededToolTip";
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="ReturnToUrl"/> property.
- /// </summary>
- private const string ReturnToUrlViewStateKey = "ReturnToUrl";
-
- /// <summary>
- /// The viewstate key to use for storing the value of the <see cref="RealmUrl"/> property.
- /// </summary>
- private const string RealmUrlViewStateKey = "RealmUrl";
-
- /// <summary>
/// The viewstate key to use for storing the value of the <see cref="LogOnInProgressMessage"/> property.
/// </summary>
private const string LogOnInProgressMessageViewStateKey = "BusyToolTip";
@@ -152,14 +129,14 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string TimeoutViewStateKey = "Timeout";
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="Text"/> property.
+ /// The viewstate key to use for storing the value of the <see cref="TabIndex"/> property.
/// </summary>
- private const string TextViewStateKey = "Text";
+ private const string TabIndexViewStateKey = "TabIndex";
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="TabIndex"/> property.
+ /// The viewstate key to use for the <see cref="Enabled"/> property.
/// </summary>
- private const string TabIndexViewStateKey = "TabIndex";
+ private const string EnabledViewStateKey = "Enabled";
/// <summary>
/// The viewstate key to use for storing the value of the <see cref="RetryToolTip"/> property.
@@ -181,14 +158,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const int ColumnsDefault = 40;
/// <summary>
- /// The default value for the <see cref="ReturnToUrl"/> property.
- /// </summary>
- private const string ReturnToUrlDefault = "";
-
- /// <summary>
- /// The default value for the <see cref="RealmUrl"/> property.
+ /// The default value for the <see cref="CssClass"/> property.
/// </summary>
- private const string RealmUrlDefault = "~/";
+ private const string CssClassDefault = "openid";
/// <summary>
/// The default value for the <see cref="LogOnInProgressMessage"/> property.
@@ -258,26 +230,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
/// <summary>
- /// Backing field for the <see cref="RelyingParty"/> property.
- /// </summary>
- private OpenIdRelyingParty relyingParty;
-
- /// <summary>
- /// Backing field for the <see cref="RelyingPartyNonVerifying"/> property.
- /// </summary>
- private OpenIdRelyingParty relyingPartyNonVerifying;
-
- /// <summary>
- /// Tracks whether the text box should receive input focus when the page is rendered.
- /// </summary>
- private bool focusCalled;
-
- /// <summary>
- /// The authentication response that just came in.
- /// </summary>
- private IAuthenticationResponse authenticationResponse;
-
- /// <summary>
/// A dictionary of extension response types and the javascript member
/// name to map them to on the user agent.
/// </summary>
@@ -292,30 +244,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#region Events
/// <summary>
- /// Fired when the user has typed in their identifier, discovery was successful
- /// and a login attempt is about to begin.
- /// </summary>
- [Description("Fired when the user has typed in their identifier, discovery was successful and a login attempt is about to begin.")]
- public event EventHandler<OpenIdEventArgs> LoggingIn;
-
- /// <summary>
- /// Fired when a Provider sends back a positive assertion to this control,
- /// but the authentication has not yet been verified.
- /// </summary>
- /// <remarks>
- /// <b>No security critical decisions should be made within event handlers
- /// for this event</b> as the authenticity of the assertion has not been
- /// verified yet. All security related code should go in the event handler
- /// for the <see cref="LoggedIn"/> event.
- /// </remarks>
- [Description("Fired when a Provider sends back a positive assertion to this control, but the authentication has not yet been verified.")]
- public event EventHandler<OpenIdEventArgs> UnconfirmedPositiveAssertion;
-
- /// <summary>
- /// Fired when authentication has completed successfully.
+ /// Fired when the content of the text changes between posts to the server.
/// </summary>
- [Description("Fired when authentication has completed successfully.")]
- public event EventHandler<OpenIdEventArgs> LoggedIn;
+ [Description("Occurs when the content of the text changes between posts to the server."), Category(BehaviorCategory)]
+ public event EventHandler TextChanged;
/// <summary>
/// Gets or sets the client-side script that executes when an authentication
@@ -331,10 +263,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// the authentication has not been verified and may have been spoofed.
/// No security-sensitive operations should take place in this javascript code.
/// The authentication is verified on the server by the time the
- /// <see cref="LoggedIn"/> server-side event fires.</para>
+ /// <see cref="OpenIdRelyingPartyControlBase.LoggedIn"/> server-side event fires.</para>
/// </remarks>
[Description("Gets or sets the client-side script that executes when an authentication assertion is received (but before it is verified).")]
- [Bindable(true), DefaultValue(""), Category("Behavior")]
+ [Bindable(true), DefaultValue(""), Category(BehaviorCategory)]
public string OnClientAssertionReceived {
get { return this.ViewState[OnClientAssertionReceivedViewStateKey] as string; }
set { this.ViewState[OnClientAssertionReceivedViewStateKey] = value; }
@@ -345,54 +277,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#region Properties
/// <summary>
- /// Gets the completed authentication response.
- /// </summary>
- public IAuthenticationResponse AuthenticationResponse {
- get {
- if (this.authenticationResponse == null) {
- // We will either validate a new response and return a live AuthenticationResponse
- // or we will try to deserialize a previous IAuthenticationResponse (snapshot)
- // from viewstate and return that.
- IAuthenticationResponse viewstateResponse = this.ViewState[AuthenticationResponseViewStateKey] as IAuthenticationResponse;
- string viewstateAuthData = this.ViewState[AuthDataViewStateKey] as string;
- string formAuthData = this.Page.Request.Form[this.OpenIdAuthDataFormKey];
-
- // First see if there is fresh auth data to be processed into a response.
- if (!string.IsNullOrEmpty(formAuthData) && !string.Equals(viewstateAuthData, formAuthData, StringComparison.Ordinal)) {
- this.ViewState[AuthDataViewStateKey] = formAuthData;
-
- Uri authUri = new Uri(formAuthData);
- HttpRequestInfo clientResponseInfo = new HttpRequestInfo {
- UrlBeforeRewriting = authUri,
- };
-
- this.authenticationResponse = this.RelyingParty.GetResponse(clientResponseInfo);
-
- // Save out the authentication response to viewstate so we can find it on
- // a subsequent postback.
- this.ViewState[AuthenticationResponseViewStateKey] = new AuthenticationResponseSnapshot(this.authenticationResponse);
- } else {
- this.authenticationResponse = viewstateResponse;
- }
- }
- return this.authenticationResponse;
- }
- }
-
- /// <summary>
/// Gets or sets the value in the text field, completely unprocessed or normalized.
/// </summary>
- [Bindable(true), DefaultValue(""), Category("Appearance")]
- [Description("The value in the text field, completely unprocessed or normalized.")]
+ [Bindable(true), DefaultValue(""), Category(AppearanceCategory)]
+ [Description("The content of the text box.")]
public string Text {
- get { return (string)(this.ViewState[TextViewStateKey] ?? string.Empty); }
- set { this.ViewState[TextViewStateKey] = value ?? string.Empty; }
+ get { return this.Identifier != null ? this.Identifier.OriginalString : string.Empty; }
+ set { this.Identifier = value; }
}
/// <summary>
/// Gets or sets the width of the text box in characters.
/// </summary>
- [Bindable(true), Category("Appearance"), DefaultValue(ColumnsDefault)]
+ [Bindable(true), Category(AppearanceCategory), DefaultValue(ColumnsDefault)]
[Description("The width of the text box in characters.")]
public int Columns {
get {
@@ -400,22 +297,45 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
set {
+ Contract.Requires<ArgumentOutOfRangeException>(value >= 0);
ErrorUtilities.VerifyArgumentInRange(value >= 0, "value");
this.ViewState[ColumnsViewStateKey] = value;
}
}
/// <summary>
+ /// Gets or sets the CSS class assigned to the text box.
+ /// </summary>
+ [Bindable(true), DefaultValue(CssClassDefault), Category(AppearanceCategory)]
+ [Description("The CSS class assigned to the text box.")]
+ public string CssClass {
+ get { return (string)this.ViewState[CssClassViewStateKey]; }
+ set { this.ViewState[CssClassViewStateKey] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the tab index of the text box control. Use 0 to omit an explicit tabindex.
/// </summary>
- [Bindable(true), Category("Behavior"), DefaultValue(TabIndexDefault)]
+ [Bindable(true), Category(BehaviorCategory), DefaultValue(TabIndexDefault)]
[Description("The tab index of the text box control. Use 0 to omit an explicit tabindex.")]
- public override short TabIndex {
+ public virtual short TabIndex {
get { return (short)(this.ViewState[TabIndexViewStateKey] ?? TabIndexDefault); }
set { this.ViewState[TabIndexViewStateKey] = value; }
}
/// <summary>
+ /// Gets or sets a value indicating whether this <see cref="OpenIdTextBox"/> is enabled
+ /// in the browser for editing and will respond to incoming OpenID messages.
+ /// </summary>
+ /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
+ [Bindable(true), DefaultValue(true), Category(BehaviorCategory)]
+ [Description("Whether the control is editable in the browser and will respond to OpenID messages.")]
+ public bool Enabled {
+ get { return (bool)(this.ViewState[EnabledViewStateKey] ?? true); }
+ set { this.ViewState[EnabledViewStateKey] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the HTML name to assign to the text field.
/// </summary>
[Bindable(true), DefaultValue(NameDefault), Category("Misc")]
@@ -434,7 +354,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the time duration for the AJAX control to wait for an OP to respond before reporting failure to the user.
/// </summary>
- [Browsable(true), DefaultValue(typeof(TimeSpan), "00:00:01"), Category("Behavior")]
+ [Browsable(true), DefaultValue(typeof(TimeSpan), "00:00:08"), Category(BehaviorCategory)]
[Description("The time duration for the AJAX control to wait for an OP to respond before reporting failure to the user.")]
public TimeSpan Timeout {
get {
@@ -450,7 +370,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the maximum number of OpenID Providers to simultaneously try to authenticate with.
/// </summary>
- [Browsable(true), DefaultValue(ThrottleDefault), Category("Behavior")]
+ [Browsable(true), DefaultValue(ThrottleDefault), Category(BehaviorCategory)]
[Description("The maximum number of OpenID Providers to simultaneously try to authenticate with.")]
public int Throttle {
get {
@@ -466,7 +386,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.
/// </summary>
- [Bindable(true), DefaultValue(LogOnTextDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(LogOnTextDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.")]
public string LogOnText {
get {
@@ -482,7 +402,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the rool tip text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.
/// </summary>
- [Bindable(true), DefaultValue(LogOnToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(LogOnToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.")]
public string LogOnToolTip {
get { return (string)(this.ViewState[LogOnToolTipViewStateKey] ?? LogOnToolTipDefault); }
@@ -492,7 +412,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the text that appears on the RETRY button in cases where authentication times out.
/// </summary>
- [Bindable(true), DefaultValue(RetryTextDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(RetryTextDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The text that appears on the RETRY button in cases where authentication times out.")]
public string RetryText {
get {
@@ -508,7 +428,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the tool tip text that appears on the RETRY button in cases where authentication times out.
/// </summary>
- [Bindable(true), DefaultValue(RetryToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(RetryToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears on the RETRY button in cases where authentication times out.")]
public string RetryToolTip {
get { return (string)(this.ViewState[RetryToolTipViewStateKey] ?? RetryToolTipDefault); }
@@ -518,7 +438,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the tool tip text that appears when authentication succeeds.
/// </summary>
- [Bindable(true), DefaultValue(AuthenticationSucceededToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(AuthenticationSucceededToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears when authentication succeeds.")]
public string AuthenticationSucceededToolTip {
get { return (string)(this.ViewState[AuthenticationSucceededToolTipViewStateKey] ?? AuthenticationSucceededToolTipDefault); }
@@ -528,7 +448,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the tool tip text that appears on the green checkmark when authentication succeeds.
/// </summary>
- [Bindable(true), DefaultValue(AuthenticatedAsToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(AuthenticatedAsToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears on the green checkmark when authentication succeeds.")]
public string AuthenticatedAsToolTip {
get { return (string)(this.ViewState[AuthenticatedAsToolTipViewStateKey] ?? AuthenticatedAsToolTipDefault); }
@@ -538,7 +458,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the tool tip text that appears when authentication fails.
/// </summary>
- [Bindable(true), DefaultValue(AuthenticationFailedToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(AuthenticationFailedToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears when authentication fails.")]
public string AuthenticationFailedToolTip {
get { return (string)(this.ViewState[AuthenticationFailedToolTipViewStateKey] ?? AuthenticationFailedToolTipDefault); }
@@ -548,7 +468,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the tool tip text that appears over the text box when it is discovering and authenticating.
/// </summary>
- [Bindable(true), DefaultValue(BusyToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(BusyToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears over the text box when it is discovering and authenticating.")]
public string BusyToolTip {
get { return (string)(this.ViewState[BusyToolTipViewStateKey] ?? BusyToolTipDefault); }
@@ -558,7 +478,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the message that is displayed if a postback is about to occur before the identifier has been supplied.
/// </summary>
- [Bindable(true), DefaultValue(IdentifierRequiredMessageDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(IdentifierRequiredMessageDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The message that is displayed if a postback is about to occur before the identifier has been supplied.")]
public string IdentifierRequiredMessage {
get { return (string)(this.ViewState[IdentifierRequiredMessageViewStateKey] ?? IdentifierRequiredMessageDefault); }
@@ -568,230 +488,25 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the message that is displayed if a postback is attempted while login is in process.
/// </summary>
- [Bindable(true), DefaultValue(LogOnInProgressMessageDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(LogOnInProgressMessageDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The message that is displayed if a postback is attempted while login is in process.")]
public string LogOnInProgressMessage {
get { return (string)(this.ViewState[LogOnInProgressMessageViewStateKey] ?? LogOnInProgressMessageDefault); }
set { this.ViewState[LogOnInProgressMessageViewStateKey] = value ?? string.Empty; }
}
- /// <summary>
- /// Gets or sets the OpenID <see cref="Realm"/> of the relying party web site.
- /// </summary>
- [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "Using Uri.ctor for validation.")]
- [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "DotNetOpenAuth.OpenId.Realm", Justification = "Using ctor for validation.")]
- [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Property grid on form designer only supports primitive types.")]
- [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Property grid on form designer only supports primitive types.")]
- [Bindable(true)]
- [Category("Behavior")]
- [DefaultValue(RealmUrlDefault)]
- [Description("The OpenID Realm of the relying party web site.")]
- [UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
- public string RealmUrl {
- get {
- return (string)(this.ViewState[RealmUrlViewStateKey] ?? RealmUrlDefault);
- }
-
- set {
- if (Page != null && !DesignMode) {
- // Validate new value by trying to construct a Realm object based on it.
- new Realm(OpenIdUtilities.GetResolvedRealm(this.Page, value, this.RelyingParty.Channel.GetRequestFromContext())); // 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.Replace("*.", "")); // 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[RealmUrlViewStateKey] = value;
- }
- }
-
- /// <summary>
- /// Gets or sets the OpenID ReturnTo of the relying party web site.
- /// </summary>
- [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "Using Uri.ctor for validation.")]
- [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Property grid on form designer only supports primitive types.")]
- [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Property grid on form designer only supports primitive types.")]
- [Bindable(true)]
- [Category("Behavior")]
- [DefaultValue(ReturnToUrlDefault)]
- [Description("The OpenID ReturnTo of the relying party web site.")]
- [UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
- public string ReturnToUrl {
- get {
- return (string)(this.ViewState[ReturnToUrlViewStateKey] ?? ReturnToUrlDefault);
- }
-
- set {
- if (Page != null && !DesignMode) {
- // Validate new value by trying to construct a Uri based on it.
- new Uri(this.RelyingParty.Channel.GetRequestFromContext().UrlBeforeRewriting, 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[ReturnToUrlViewStateKey] = value;
- }
- }
-
#endregion
- #region Properties to hide
-
/// <summary>
- /// Gets or sets the foreground color (typically the color of the text) of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Drawing.Color"/> that represents the foreground color of the control. The default is <see cref="F:System.Drawing.Color.Empty"/>.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override System.Drawing.Color ForeColor {
- get { throw new NotSupportedException(); }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the background color of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Drawing.Color"/> that represents the background color of the control. The default is <see cref="F:System.Drawing.Color.Empty"/>, which indicates that this property is not set.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override System.Drawing.Color BackColor {
- get { throw new NotSupportedException(); }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the border color of the Web control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Drawing.Color"/> that represents the border color of the control. The default is <see cref="F:System.Drawing.Color.Empty"/>, which indicates that this property is not set.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override System.Drawing.Color BorderColor {
- get { throw new NotSupportedException(); }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the border width of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.Unit"/> that represents the border width of a Web server control. The default value is <see cref="F:System.Web.UI.WebControls.Unit.Empty"/>, which indicates that this property is not set.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The specified border width is a negative value.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override Unit BorderWidth {
- get { return Unit.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the border style of the Web server control.
- /// </summary>
- /// <returns>
- /// One of the <see cref="T:System.Web.UI.WebControls.BorderStyle"/> enumeration values. The default is NotSet.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override BorderStyle BorderStyle {
- get { return BorderStyle.None; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets the font properties associated with the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.FontInfo"/> that represents the font properties of the Web server control.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override FontInfo Font {
- get { return null; }
- }
-
- /// <summary>
- /// Gets or sets the height of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.Unit"/> that represents the height of the control. The default is <see cref="F:System.Web.UI.WebControls.Unit.Empty"/>.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The height was set to a negative value.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override Unit Height {
- get { return Unit.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the width of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.Unit"/> that represents the width of the control. The default is <see cref="F:System.Web.UI.WebControls.Unit.Empty"/>.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The width of the Web server control was set to a negative value.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override Unit Width {
- get { return Unit.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the text displayed when the mouse pointer hovers over the Web server control.
- /// </summary>
- /// <returns>
- /// The text displayed when the mouse pointer hovers over the Web server control. The default is <see cref="F:System.String.Empty"/>.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override string ToolTip {
- get { return string.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the skin to apply to the control.
- /// </summary>
- /// <returns>
- /// The name of the skin to apply to the control. The default is <see cref="F:System.String.Empty"/>.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The skin specified in the <see cref="P:System.Web.UI.WebControls.WebControl.SkinID"/> property does not exist in the theme.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override string SkinID {
- get { return string.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether themes apply to this control.
+ /// Gets the name of the open id auth data form key.
/// </summary>
- /// <returns>true to use themes; otherwise, false. The default is false.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override bool EnableTheming {
- get { return false; }
- set { throw new NotSupportedException(); }
+ /// <value>
+ /// A concatenation of <see cref="Name"/> and <c>"_openidAuthData"</c>.
+ /// </value>
+ protected override string OpenIdAuthDataFormKey {
+ get { return this.Name + "_openidAuthData"; }
}
- #endregion
-
/// <summary>
/// Gets the default value for the <see cref="Timeout"/> property.
/// </summary>
@@ -807,171 +522,37 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
}
- /// <summary>
- /// Gets the relying party to use when verification of incoming messages is needed.
- /// </summary>
- private OpenIdRelyingParty RelyingParty {
- get {
- if (this.relyingParty == null) {
- this.relyingParty = CreateRelyingParty(true);
- }
- return this.relyingParty;
- }
- }
+ #region IPostBackDataHandler Members
/// <summary>
- /// Gets the relying party to use when verification of incoming messages is NOT wanted.
+ /// When implemented by a class, processes postback data for an ASP.NET server control.
/// </summary>
- private OpenIdRelyingParty RelyingPartyNonVerifying {
- get {
- if (this.relyingPartyNonVerifying == null) {
- this.relyingPartyNonVerifying = CreateRelyingParty(false);
- }
- return this.relyingPartyNonVerifying;
- }
- }
-
- /// <summary>
- /// Gets the name of the open id auth data form key.
- /// </summary>
- /// <value>A concatenation of <see cref="Name"/> and <c>"_openidAuthData"</c>.</value>
- private string OpenIdAuthDataFormKey {
- get { return this.Name + "_openidAuthData"; }
- }
-
- /// <summary>
- /// Places focus on the text box when the page is rendered on the browser.
- /// </summary>
- public override void Focus() {
- // we don't emit the code to focus the control immediately, in case the control
- // is never rendered to the page because its Visible property is false or that
- // of any of its parent containers.
- this.focusCalled = true;
- }
-
- /// <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 from the <see cref="UnconfirmedPositiveAssertion"/> event handler.
- /// </remarks>
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "By design")]
- public void RegisterClientScriptExtension<T>(string propertyName) where T : IClientScriptExtensionResponse {
- ErrorUtilities.VerifyNonZeroLength(propertyName, "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);
+ /// <param name="postDataKey">The key identifier for the control.</param>
+ /// <param name="postCollection">The collection of all incoming name values.</param>
+ /// <returns>
+ /// true if the server control's state changes as a result of the postback; otherwise, false.
+ /// </returns>
+ bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {
+ return this.LoadPostData(postDataKey, postCollection);
}
- #region ICallbackEventHandler Members
-
/// <summary>
- /// Returns the result of discovery on some Identifier passed to <see cref="ICallbackEventHandler.RaiseCallbackEvent"/>.
+ /// When implemented by a class, signals the server control to notify the ASP.NET application that the state of the control has changed.
/// </summary>
- /// <returns>The result of the callback.</returns>
- /// <value>A whitespace delimited list of URLs that can be used to initiate authentication.</value>
- string ICallbackEventHandler.GetCallbackResult() {
- this.Page.Response.ContentType = "text/javascript";
- return this.discoveryResult;
- }
-
- /// <summary>
- /// Performs discovery on some OpenID Identifier. Called directly from the user agent via
- /// AJAX callback mechanisms.
- /// </summary>
- /// <param name="eventArgument">The identifier to perform discovery on.</param>
- void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) {
- string userSuppliedIdentifier = eventArgument;
-
- ErrorUtilities.VerifyNonZeroLength(userSuppliedIdentifier, "userSuppliedIdentifier");
- Logger.OpenId.InfoFormat("AJAX discovery on {0} requested.", userSuppliedIdentifier);
-
- // 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.
- // }
- StringBuilder discoveryResultBuilder = new StringBuilder();
- discoveryResultBuilder.Append("{");
- try {
- List<IAuthenticationRequest> requests = this.CreateRequests(userSuppliedIdentifier, true).Where(req => this.OnLoggingIn(req)).ToList();
- if (requests.Count > 0) {
- discoveryResultBuilder.AppendFormat("claimedIdentifier: {0},", MessagingUtilities.GetSafeJavascriptValue(requests[0].ClaimedIdentifier));
- discoveryResultBuilder.Append("requests: [");
- foreach (IAuthenticationRequest request in requests) {
- discoveryResultBuilder.Append("{");
- discoveryResultBuilder.AppendFormat("endpoint: {0},", MessagingUtilities.GetSafeJavascriptValue(request.Provider.Uri.AbsoluteUri));
- request.Mode = AuthenticationRequestMode.Immediate;
- OutgoingWebResponse response = request.RedirectingResponse;
- discoveryResultBuilder.AppendFormat("immediate: {0},", MessagingUtilities.GetSafeJavascriptValue(response.GetDirectUriRequest(this.RelyingParty.Channel).AbsoluteUri));
- request.Mode = AuthenticationRequestMode.Setup;
- response = request.RedirectingResponse;
- discoveryResultBuilder.AppendFormat("setup: {0}", MessagingUtilities.GetSafeJavascriptValue(response.GetDirectUriRequest(this.RelyingParty.Channel).AbsoluteUri));
- discoveryResultBuilder.Append("},");
- }
- discoveryResultBuilder.Length -= 1; // trim off last comma
- discoveryResultBuilder.Append("]");
- } else {
- discoveryResultBuilder.Append("requests: new Array(),");
- discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(OpenIdStrings.OpenIdEndpointNotFound));
- }
- } catch (ProtocolException ex) {
- discoveryResultBuilder.Append("requests: new Array(),");
- discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(ex.Message));
- }
- discoveryResultBuilder.Append("}");
- this.discoveryResult = discoveryResultBuilder.ToString();
+ void IPostBackDataHandler.RaisePostDataChangedEvent() {
+ this.RaisePostDataChangedEvent();
}
#endregion
/// <summary>
- /// Enables a server control to perform final clean up before it is released from memory.
+ /// Raises the <see cref="E:Load"/> event.
/// </summary>
- public sealed override void Dispose() {
- this.Dispose(true);
- base.Dispose();
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Prepares the control for loading.
- /// </summary>
- /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
- if (this.Page.IsPostBack) {
- // If the control was temporarily hidden, it won't be in the Form data,
- // and we'll just implicitly keep the last Text setting.
- if (this.Page.Request.Form[this.Name] != null) {
- this.Text = this.Page.Request.Form[this.Name];
- }
-
- // If there is a response, and it is fresh (live object, not a snapshot object)...
- if (this.AuthenticationResponse != null && this.AuthenticationResponse.Status == AuthenticationStatus.Authenticated) {
- this.OnLoggedIn(this.AuthenticationResponse);
- }
- } else {
- NameValueCollection query = this.RelyingParty.Channel.GetRequestFromContext().GetQueryOrFormFromContext();
- string userSuppliedIdentifier = query["dotnetopenid.userSuppliedIdentifier"];
- if (!string.IsNullOrEmpty(userSuppliedIdentifier) && query["dotnetopenid.phase"] == "2") {
- this.ReportAuthenticationResult();
- }
- }
+ this.Page.RegisterRequiresPostBack(this);
}
/// <summary>
@@ -988,183 +569,85 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// Renders the control.
/// </summary>
/// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the control content.</param>
- protected override void Render(System.Web.UI.HtmlTextWriter writer) {
+ protected override void Render(HtmlTextWriter writer) {
+ base.Render(writer);
+
// We surround the textbox with a span so that the .js file can inject a
// login button within the text box with easy placement.
- writer.WriteBeginTag("span");
- writer.WriteAttribute("class", this.CssClass);
- writer.Write(" style='");
- writer.WriteStyleAttribute("display", "inline-block");
- writer.WriteStyleAttribute("position", "relative");
- writer.WriteStyleAttribute("font-size", "16px");
- writer.Write("'>");
-
- writer.WriteBeginTag("input");
- writer.WriteAttribute("name", this.Name);
- writer.WriteAttribute("id", this.ClientID);
- writer.WriteAttribute("value", this.Text, true);
- writer.WriteAttribute("size", this.Columns.ToString(CultureInfo.InvariantCulture));
+ if (!string.IsNullOrEmpty(this.CssClass)) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass);
+ }
+
+ writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "inline-block");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "relative");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize, "16px");
+ writer.RenderBeginTag(HtmlTextWriterTag.Span);
+
+ writer.AddAttribute(HtmlTextWriterAttribute.Name, this.Name);
+ writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
+ writer.AddAttribute(HtmlTextWriterAttribute.Size, this.Columns.ToString(CultureInfo.InvariantCulture));
+ if (!string.IsNullOrEmpty(this.Text)) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Value, this.Text, true);
+ }
+
if (this.TabIndex > 0) {
- writer.WriteAttribute("tabindex", this.TabIndex.ToString(CultureInfo.InvariantCulture));
+ writer.AddAttribute(HtmlTextWriterAttribute.Tabindex, this.TabIndex.ToString(CultureInfo.InvariantCulture));
}
if (!this.Enabled) {
- writer.WriteAttribute("disabled", "true");
+ writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "true");
}
if (!string.IsNullOrEmpty(this.CssClass)) {
- writer.WriteAttribute("class", this.CssClass);
- }
- writer.Write(" style='");
- writer.WriteStyleAttribute("padding-left", "18px");
- writer.WriteStyleAttribute("border-style", "solid");
- writer.WriteStyleAttribute("border-width", "1px");
- writer.WriteStyleAttribute("border-color", "lightgray");
- writer.Write("'");
- writer.Write(" />");
-
- writer.WriteEndTag("span");
-
- // Emit a hidden field to let the javascript on the user agent know if an
- // authentication has already successfully taken place.
- string viewstateAuthData = this.ViewState[AuthDataViewStateKey] as string;
- if (!string.IsNullOrEmpty(viewstateAuthData)) {
- writer.WriteBeginTag("input");
- writer.WriteAttribute("type", "hidden");
- writer.WriteAttribute("name", this.OpenIdAuthDataFormKey);
- writer.WriteAttribute("value", viewstateAuthData, true);
- writer.Write(" />");
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass);
}
+ writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingLeft, "18px");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "solid");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "1px");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.BorderColor, "lightgray");
+ writer.RenderBeginTag(HtmlTextWriterTag.Input);
+ writer.RenderEndTag(); // </input>
+ writer.RenderEndTag(); // </span>
}
/// <summary>
- /// Filters a sequence of OP endpoints so that an OP hostname only appears once in the list.
+ /// When implemented by a class, processes postback data for an ASP.NET server control.
/// </summary>
- /// <param name="requests">The authentication requests against those OP endpoints.</param>
- /// <returns>The filtered list.</returns>
- private static List<IAuthenticationRequest> RemoveDuplicateEndpoints(List<IAuthenticationRequest> requests) {
- var filteredRequests = new List<IAuthenticationRequest>(requests.Count);
- foreach (IAuthenticationRequest request in requests) {
- // 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.
- if (!filteredRequests.Any(req => string.Equals(req.Provider.Uri.Host, request.Provider.Uri.Host, StringComparison.OrdinalIgnoreCase))) {
- filteredRequests.Add(request);
+ /// <param name="postDataKey">The key identifier for the control.</param>
+ /// <param name="postCollection">The collection of all incoming name values.</param>
+ /// <returns>
+ /// true if the server control's state changes as a result of the postback; otherwise, false.
+ /// </returns>
+ protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) {
+ // If the control was temporarily hidden, it won't be in the Form data,
+ // and we'll just implicitly keep the last Text setting.
+ if (postCollection[this.Name] != null) {
+ Identifier identifier = postCollection[this.Name].Length == 0 ? null : postCollection[this.Name];
+ if (identifier != this.Identifier) {
+ this.Identifier = identifier;
+ return true;
}
}
- return filteredRequests;
+ return false;
}
/// <summary>
- /// Creates the relying party.
+ /// When implemented by a class, signals the server control to notify the ASP.NET application that the state of the control has changed.
/// </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>
- /// <returns>The newly instantiated relying party.</returns>
- private static OpenIdRelyingParty CreateRelyingParty(bool verifySignature) {
- return verifySignature ? new OpenIdRelyingParty() : OpenIdRelyingParty.CreateNonVerifying();
+ protected virtual void RaisePostDataChangedEvent() {
+ this.OnTextChanged();
}
/// <summary>
- /// Releases unmanaged and - optionally - managed resources
+ /// Called on a postback when the Text property has changed.
/// </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) {
- if (disposing) {
- if (this.relyingParty != null) {
- this.relyingParty.Dispose();
- this.relyingParty = null;
- }
-
- if (this.relyingPartyNonVerifying != null) {
- this.relyingPartyNonVerifying.Dispose();
- this.relyingPartyNonVerifying = null;
- }
+ protected virtual void OnTextChanged() {
+ EventHandler textChanged = this.TextChanged;
+ if (textChanged != null) {
+ textChanged(this, EventArgs.Empty);
}
}
/// <summary>
- /// Fires the <see cref="LoggingIn"/> event.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns><c>true</c> if the login should proceed; <c>false</c> otherwise.</returns>
- private bool OnLoggingIn(IAuthenticationRequest request) {
- var loggingIn = this.LoggingIn;
- if (loggingIn != null) {
- var args = new OpenIdEventArgs(request);
- loggingIn(this, args);
- return !args.Cancel;
- }
-
- return true;
- }
-
- /// <summary>
- /// Fires the <see cref="UnconfirmedPositiveAssertion"/> event.
- /// </summary>
- private void OnUnconfirmedPositiveAssertion() {
- var unconfirmedPositiveAssertion = this.UnconfirmedPositiveAssertion;
- if (unconfirmedPositiveAssertion != null) {
- unconfirmedPositiveAssertion(this, null);
- }
- }
-
- /// <summary>
- /// Fires the <see cref="LoggedIn"/> event.
- /// </summary>
- /// <param name="response">The response.</param>
- private void OnLoggedIn(IAuthenticationResponse response) {
- var loggedIn = this.LoggedIn;
- if (loggedIn != null) {
- loggedIn(this, new OpenIdEventArgs(response));
- }
- }
-
- /// <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) {
- this.CallbackUserAgentMethod(methodCall, null);
- }
-
- /// <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>
- /// <param name="preAssignments">An optional list of assignments to make to the input box object before placing the method call.</param>
- private void CallbackUserAgentMethod(string methodCall, string[] preAssignments) {
- Logger.OpenId.InfoFormat("Sending Javascript callback: {0}", methodCall);
- Page.Response.Write(@"<html><body><script language='javascript'>
- var inPopup = !window.frameElement;
- var objSrc = inPopup ? window.opener.waiting_openidBox : window.frameElement.openidBox;
-");
- if (preAssignments != null) {
- foreach (string assignment in preAssignments) {
- Page.Response.Write(string.Format(CultureInfo.InvariantCulture, " objSrc.{0};\n", assignment));
- }
- }
-
- // 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>
/// Assembles the javascript to send to the client and registers it with ASP.NET for transmission.
/// </summary>
private void PrepareClientJavascript() {
@@ -1186,9 +669,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
StringBuilder startupScript = new StringBuilder();
startupScript.AppendLine("<script language='javascript'>");
startupScript.AppendFormat("var box = document.getElementsByName('{0}')[0];{1}", this.Name, Environment.NewLine);
- if (this.focusCalled) {
- startupScript.AppendLine("box.focus();");
- }
startupScript.AppendFormat(
CultureInfo.InvariantCulture,
"initAjaxOpenId(box, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, function({18}, {19}, {20}) {{{21}}});{22}",
@@ -1228,106 +708,5 @@ if (!openidbox.dnoi_internal.onSubmit()) {{ return false; }}
"loginvalidation",
string.Format(CultureInfo.InvariantCulture, htmlFormat, this.Name));
}
-
- /// <summary>
- /// Creates the authentication requests for a given user-supplied Identifier.
- /// </summary>
- /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
- /// <param name="immediate">A value indicating whether the authentication
- /// requests should be initialized for use in invisible iframes for background authentication.</param>
- /// <returns>The list 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> CreateRequests(string userSuppliedIdentifier, bool immediate) {
- var requests = new List<IAuthenticationRequest>();
-
- // Approximate the returnTo (either based on the customize property or the page URL)
- // so we can use it to help with Realm resolution.
- Uri returnToApproximation = this.ReturnToUrl != null ? new Uri(this.RelyingParty.Channel.GetRequestFromContext().UrlBeforeRewriting, this.ReturnToUrl) : this.Page.Request.Url;
-
- // Resolve the trust root, and swap out the scheme and port if necessary to match the
- // return_to URL, since this match is required by OpenId, and the consumer app
- // may be using HTTP at some times and HTTPS at others.
- UriBuilder realm = OpenIdUtilities.GetResolvedRealm(this.Page, this.RealmUrl, this.RelyingParty.Channel.GetRequestFromContext());
- realm.Scheme = returnToApproximation.Scheme;
- realm.Port = returnToApproximation.Port;
-
- // Initiate openid request
- // We use TryParse here to avoid throwing an exception which
- // might slip through our validator control if it is disabled.
- Realm typedRealm = new Realm(realm);
- if (string.IsNullOrEmpty(this.ReturnToUrl)) {
- requests.AddRange(this.RelyingParty.CreateRequests(userSuppliedIdentifier, typedRealm));
- } else {
- // Since the user actually gave us a return_to value,
- // the "approximation" is exactly what we want.
- requests.AddRange(this.RelyingParty.CreateRequests(userSuppliedIdentifier, typedRealm, returnToApproximation));
- }
-
- // 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 = RemoveDuplicateEndpoints(requests);
-
- // Configure each generated request.
- int reqIndex = 0;
- foreach (var req in requests) {
- req.AddCallbackArguments("index", (reqIndex++).ToString(CultureInfo.InvariantCulture));
-
- if (req.Provider.IsExtensionSupported<UIRequest>()) {
- // Inform the OP that we'll be using a popup window.
- 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.AddCallbackArguments("dotnetopenid.popupUISupported", "1");
- }
-
- // If the ReturnToUrl was explicitly set, we'll need to reset our first parameter
- if (string.IsNullOrEmpty(HttpUtility.ParseQueryString(req.ReturnToUrl.Query)["dotnetopenid.userSuppliedIdentifier"])) {
- req.AddCallbackArguments("dotnetopenid.userSuppliedIdentifier", userSuppliedIdentifier);
- }
-
- // 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.AddCallbackArguments("dotnetopenid.op_endpoint", req.Provider.Uri.AbsoluteUri);
- req.AddCallbackArguments("dotnetopenid.claimed_id", (string)req.ClaimedIdentifier ?? string.Empty);
- req.AddCallbackArguments("dotnetopenid.phase", "2");
- if (immediate) {
- req.Mode = AuthenticationRequestMode.Immediate;
- ((AuthenticationRequest)req).AssociationPreference = AssociationPreference.IfAlreadyEstablished;
- }
- }
-
- return requests;
- }
-
- /// <summary>
- /// Notifies the user agent via an AJAX response of a completed authentication attempt.
- /// </summary>
- private void ReportAuthenticationResult() {
- Logger.OpenId.InfoFormat("AJAX (iframe) callback from OP: {0}", this.Page.Request.Url);
- List<string> assignments = new List<string>();
-
- var authResponse = this.RelyingPartyNonVerifying.GetResponse();
- if (authResponse.Status == AuthenticationStatus.Authenticated) {
- this.OnUnconfirmedPositiveAssertion();
- 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)) {
- js = "null";
- }
- assignments.Add(pair.Value + " = " + js);
- }
- }
-
- this.CallbackUserAgentMethod("dnoi_internal.processAuthorizationResult(document.URL)", assignments.ToArray());
- }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js
index 1078003..c549b17 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js
@@ -4,46 +4,12 @@
// </copyright>
//-----------------------------------------------------------------------
-// Options that can be set on the host page:
-//window.openid_visible_iframe = true; // causes the hidden iframe to show up
-//window.openid_trace = true; // causes lots of messages
-
-function trace(msg) {
- if (window.openid_trace) {
- if (!window.tracediv) {
- window.tracediv = document.createElement("ol");
- document.body.appendChild(window.tracediv);
- }
- var el = document.createElement("li");
- el.appendChild(document.createTextNode(msg));
- window.tracediv.appendChild(el);
- //alert(msg);
- }
-}
-
-/// <summary>Removes a given element from the array.</summary>
-/// <returns>True if the element was in the array, or false if it was not found.</returns>
-Array.prototype.remove = function(element) {
- function elementToRemoveLast(a, b) {
- if (a == element) { return 1; }
- if (b == element) { return -1; }
- return 0;
- }
- this.sort(elementToRemoveLast);
- if (this[this.length - 1] == element) {
- this.pop();
- return true;
- } else {
- return false;
- }
-};
-
function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url, success_icon_url, failure_icon_url,
throttle, timeout, assertionReceivedCode,
loginButtonText, loginButtonToolTip, retryButtonText, retryButtonToolTip, busyToolTip,
identifierRequiredMessage, loginInProgressMessage,
authenticatedByToolTip, authenticatedAsToolTip, authenticationFailedToolTip,
- discoverCallback, discoveryFailedCallback) {
+ discoverCallback/*removeme*/, discoveryFailedCallback) {
box.dnoi_internal = new Object();
if (assertionReceivedCode) {
box.dnoi_internal.onauthenticated = function(sender, e) { eval(assertionReceivedCode); }
@@ -51,79 +17,8 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
box.dnoi_internal.originalBackground = box.style.background;
box.timeout = timeout;
- box.dnoi_internal.discoverIdentifier = discoverCallback;
- box.dnoi_internal.authenticationRequests = new Array();
-
- // The possible authentication results
- var authSuccess = new Object();
- var authRefused = new Object();
- var timedOut = new Object();
-
- function FrameManager(maxFrames) {
- this.queuedWork = new Array();
- this.frames = new Array();
- this.maxFrames = maxFrames;
-
- /// <summary>Called to queue up some work that will use an iframe as soon as it is available.</summary>
- /// <param name="job">
- /// A delegate that must return the url to point to iframe to.
- /// Its first parameter is the iframe created to service the request.
- /// It will only be called when the work actually begins.
- /// </param>
- this.enqueueWork = function(job) {
- // Assign an iframe to this task immediately if there is one available.
- if (this.frames.length < this.maxFrames) {
- this.createIFrame(job);
- } else {
- this.queuedWork.unshift(job);
- }
- };
- /// <summary>Clears the job queue and immediately closes all iframes.</summary>
- this.cancelAllWork = function() {
- trace('Canceling all open and pending iframes.');
- while (this.queuedWork.pop());
- this.closeFrames();
- };
-
- /// <summary>An event fired when a frame is closing.</summary>
- this.onJobCompleted = function() {
- // If there is a job in the queue, go ahead and start it up.
- if (job = this.queuedWork.pop()) {
- this.createIFrame(job);
- }
- }
-
- this.createIFrame = function(job) {
- var iframe = document.createElement("iframe");
- if (!window.openid_visible_iframe) {
- iframe.setAttribute("width", 0);
- iframe.setAttribute("height", 0);
- iframe.setAttribute("style", "display: none");
- }
- iframe.setAttribute("src", job(iframe));
- iframe.openidBox = box;
- box.parentNode.insertBefore(iframe, box);
- this.frames.push(iframe);
- return iframe;
- };
- this.closeFrames = function() {
- if (this.frames.length == 0) { return false; }
- for (var i = 0; i < this.frames.length; i++) {
- if (this.frames[i].parentNode) { this.frames[i].parentNode.removeChild(this.frames[i]); }
- }
- while (this.frames.length > 0) { this.frames.pop(); }
- return true;
- };
- this.closeFrame = function(frame) {
- if (frame.parentNode) { frame.parentNode.removeChild(frame); }
- var removed = this.frames.remove(frame);
- this.onJobCompleted();
- return removed;
- };
- }
-
- box.dnoi_internal.authenticationIFrames = new FrameManager(throttle);
+ box.dnoi_internal.authenticationIFrames = new window.dnoa_internal.FrameManager(throttle);
box.dnoi_internal.constructButton = function(text, tooltip, onclick) {
var button = document.createElement('input');
@@ -199,14 +94,14 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
};
box.dnoi_internal.loginButton = box.dnoi_internal.constructButton(loginButtonText, loginButtonToolTip, function() {
- var discoveryInfo = box.dnoi_internal.authenticationRequests[box.lastDiscoveredIdentifier];
+ var discoveryInfo = window.dnoa_internal.discoveryResults[box.lastDiscoveredIdentifier];
if (discoveryInfo == null) {
trace('Ooops! Somehow the login button click event was invoked, but no openid discovery information for ' + box.lastDiscoveredIdentifier + ' is available.');
return;
}
// The login button always sends a setup message to the first OP.
var selectedProvider = discoveryInfo[0];
- selectedProvider.trySetup();
+ selectedProvider.loginPopup(box.dnoi_internal.onAuthSuccess, box.dnoi_internal.onAuthFailed);
return false;
});
box.dnoi_internal.retryButton = box.dnoi_internal.constructButton(retryButtonText, retryButtonToolTip, function() {
@@ -250,13 +145,14 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
box.dnoi_internal.op_logo.style.visibility = 'visible';
box.dnoi_internal.op_logo.title = box.dnoi_internal.op_logo.originalTitle.replace('{0}', authenticatedBy.getHost());
}
- trace("OP icon size: " + box.dnoi_internal.op_logo.fileSize);
- if (opLogo == null || box.dnoi_internal.op_logo.fileSize == -1 /*IE*/ || box.dnoi_internal.op_logo.fileSize === undefined /* FF */) {
- trace('recovering from missing OP icon');
- box.dnoi_internal.op_logo.style.visibility = 'hidden';
- box.dnoi_internal.openid_logo.style.visibility = 'visible';
- box.dnoi_internal.openid_logo.title = box.dnoi_internal.op_logo.originalTitle.replace('{0}', authenticatedBy.getHost());
- }
+ //trace("OP icon size: " + box.dnoi_internal.op_logo.fileSize);
+ // This just doesn't seem to work any more.
+// if (opLogo == null || box.dnoi_internal.op_logo.fileSize == -1 /*IE*/ || box.dnoi_internal.op_logo.fileSize === undefined /* FF */) {
+// trace('recovering from missing OP icon');
+// box.dnoi_internal.op_logo.style.visibility = 'hidden';
+// box.dnoi_internal.openid_logo.style.visibility = 'visible';
+// box.dnoi_internal.openid_logo.title = box.dnoi_internal.op_logo.originalTitle.replace('{0}', authenticatedBy.getHost());
+// }
box.dnoi_internal.success_icon.style.visibility = 'visible';
box.dnoi_internal.success_icon.title = box.dnoi_internal.success_icon.originalTitle.replace('{0}', authenticatedAs);
box.title = box.dnoi_internal.claimedIdentifier;
@@ -291,20 +187,20 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
}
box.dnoi_internal.isBusy = function() {
- var lastDiscovery = box.dnoi_internal.authenticationRequests[box.lastDiscoveredIdentifier];
+ var lastDiscovery = window.dnoa_internal.discoveryResults[box.lastDiscoveredIdentifier];
return box.dnoi_internal.state == 'discovering' ||
(lastDiscovery && lastDiscovery.busy());
};
box.dnoi_internal.canAttemptLogin = function() {
if (box.value.length == 0) return false;
- if (box.dnoi_internal.authenticationRequests[box.value] == null) return false;
+ if (window.dnoa_internal.discoveryResults[box.value] == null) return false;
if (box.dnoi_internal.state == 'failed') return false;
return true;
};
box.dnoi_internal.getUserSuppliedIdentifierResults = function() {
- return box.dnoi_internal.authenticationRequests[box.value];
+ return window.dnoa_internal.discoveryResults[box.value];
}
box.dnoi_internal.isAuthenticated = function() {
@@ -316,7 +212,7 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
var hiddenField = findOrCreateHiddenField();
if (box.dnoi_internal.isAuthenticated()) {
// stick the result in a hidden field so the RP can verify it
- hiddenField.setAttribute("value", box.dnoi_internal.authenticationRequests[box.value].successAuthData);
+ hiddenField.setAttribute("value", window.dnoa_internal.discoveryResults[box.value].successAuthData);
} else {
hiddenField.setAttribute("value", '');
if (box.dnoi_internal.isBusy()) {
@@ -380,180 +276,26 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
/// <summary>
/// Returns the URL of the authenticating OP's logo so it can be displayed to the user.
/// </summary>
- box.dnoi_internal.deriveOPFavIcon = function() {
- var response = box.dnoi_internal.getUserSuppliedIdentifierResults().successAuthData;
- if (!response || response.length == 0) return;
- var authResult = new Uri(response);
- var opUri;
- if (authResult.getQueryArgValue("openid.op_endpoint")) {
- opUri = new Uri(authResult.getQueryArgValue("openid.op_endpoint"));
- } if (authResult.getQueryArgValue("dotnetopenid.op_endpoint")) {
- opUri = new Uri(authResult.getQueryArgValue("dotnetopenid.op_endpoint"));
- } else if (authResult.getQueryArgValue("openid.user_setup_url")) {
- opUri = new Uri(authResult.getQueryArgValue("openid.user_setup_url"));
- } else return null;
- var favicon = opUri.getAuthority() + "/favicon.ico";
- return favicon;
- };
-
- box.dnoi_internal.createDiscoveryInfo = function(discoveryInfo, identifier) {
- this.identifier = identifier;
- // The claimed identifier may be null if the user provided an OP Identifier.
- this.claimedIdentifier = discoveryInfo.claimedIdentifier;
- trace('Discovered claimed identifier: ' + this.claimedIdentifier);
-
- // Add extra tracking bits and behaviors.
- this.findByEndpoint = function(opEndpoint) {
- for (var i = 0; i < this.length; i++) {
- if (this[i].endpoint == opEndpoint) {
- return this[i];
- }
+ /// <param name="opUri">The OP Endpoint, if known.</param>
+ box.dnoi_internal.deriveOPFavIcon = function(opUri) {
+ if (!opUri) {
+ var response = box.dnoi_internal.getUserSuppliedIdentifierResults().successAuthData;
+ if (!response || response.length == 0) {
+ trace('No favicon because no successAuthData.');
+ return;
}
- };
- this.findSuccessfulRequest = function() {
- for (var i = 0; i < this.length; i++) {
- if (this[i].result == authSuccess) {
- return this[i];
- }
- }
- };
- this.busy = function() {
- for (var i = 0; i < this.length; i++) {
- if (this[i].busy()) {
- return true;
- }
- }
- };
- this.abortAll = function() {
- // Abort all other asynchronous authentication attempts that may be in progress.
- box.dnoi_internal.authenticationIFrames.cancelAllWork();
- for (var i = 0; i < this.length; i++) {
- this[i].abort();
- }
- };
- this.tryImmediate = function() {
- if (this.length > 0) {
- for (var i = 0; i < this.length; i++) {
- box.dnoi_internal.authenticationIFrames.enqueueWork(this[i].tryImmediate);
- }
- } else {
- box.dnoi_internal.discoveryFailed(null, this.identifier);
- }
- };
-
- this.length = discoveryInfo.requests.length;
- for (var i = 0; i < discoveryInfo.requests.length; i++) {
- this[i] = new box.dnoi_internal.createTrackingRequest(discoveryInfo.requests[i], identifier);
- }
- };
-
- box.dnoi_internal.createTrackingRequest = function(requestInfo, identifier) {
- // It's possible during a postback that discovered request URLs are not available.
- this.immediate = requestInfo.immediate ? new Uri(requestInfo.immediate) : null;
- this.setup = requestInfo.setup ? new Uri(requestInfo.setup) : null;
- this.endpoint = new Uri(requestInfo.endpoint);
- this.identifier = identifier;
- var self = this; // closure so that delegates have the right instance
-
- this.host = self.endpoint.getHost();
-
- this.getDiscoveryInfo = function() {
- return box.dnoi_internal.authenticationRequests[self.identifier];
+ var authResult = new window.dnoa_internal.Uri(response);
+ if (authResult.getQueryArgValue("openid.op_endpoint")) {
+ opUri = new window.dnoa_internal.Uri(authResult.getQueryArgValue("openid.op_endpoint"));
+ } else if (authResult.getQueryArgValue("dnoa.op_endpoint")) {
+ opUri = new window.dnoa_internal.Uri(authResult.getQueryArgValue("dnoa.op_endpoint"));
+ } else if (authResult.getQueryArgValue("openid.user_setup_url")) {
+ opUri = new window.dnoa_internal.Uri(authResult.getQueryArgValue("openid.user_setup_url"));
+ } else return null;
}
-
- this.busy = function() {
- return self.iframe != null || self.popup != null;
- };
-
- this.completeAttempt = function() {
- if (!self.busy()) return false;
- if (self.iframe) {
- trace('iframe hosting ' + self.endpoint + ' now CLOSING.');
- box.dnoi_internal.authenticationIFrames.closeFrame(self.iframe);
- self.iframe = null;
- }
- if (self.popup) {
- self.popup.close();
- self.popup = null;
- }
- if (self.timeout) {
- window.clearTimeout(self.timeout);
- self.timeout = null;
- }
-
- if (!self.getDiscoveryInfo().busy() && self.getDiscoveryInfo().findSuccessfulRequest() == null) {
- trace('No asynchronous authentication attempt is in progress. Display setup view.');
- // visual cue that auth failed
- box.dnoi_internal.setVisualCue('setup');
- }
-
- return true;
- };
-
- this.authenticationTimedOut = function() {
- if (self.completeAttempt()) {
- trace(self.host + " timed out");
- self.result = timedOut;
- }
- };
- this.authSuccess = function(authUri) {
- if (self.completeAttempt()) {
- trace(self.host + " authenticated!");
- self.result = authSuccess;
- self.response = authUri;
- box.dnoi_internal.authenticationRequests[self.identifier].abortAll();
- }
- };
- this.authFailed = function() {
- if (self.completeAttempt()) {
- //trace(self.host + " failed authentication");
- self.result = authRefused;
- }
- };
- this.abort = function() {
- if (self.completeAttempt()) {
- trace(self.host + " aborted");
- // leave the result as whatever it was before.
- }
- };
-
- this.tryImmediate = function(iframe) {
- self.abort(); // ensure no concurrent attempts
- self.timeout = setTimeout(function() { self.authenticationTimedOut(); }, box.timeout);
- trace('iframe hosting ' + self.endpoint + ' now OPENING.');
- self.iframe = iframe;
- //trace('initiating auth attempt with: ' + self.immediate);
- return self.immediate.toString();
- };
- this.trySetup = function() {
- self.abort(); // ensure no concurrent attempts
- window.waiting_openidBox = box;
- var width = 800;
- var height = 600;
- if (self.setup.getQueryArgValue("openid.return_to").indexOf("dotnetopenid.popupUISupported") >= 0) {
- width = 450;
- height = 500;
- }
-
- var left = (screen.width - width) / 2;
- var top = (screen.height - height) / 2;
- self.popup = window.open(self.setup, 'opLogin', 'status=0,toolbar=0,location=1,resizable=1,scrollbars=1,left=' + left + ',top=' + top + ',width=' + width + ',height=' + height);
-
- // If the OP supports the UI extension it MAY close its own window
- // for a negative assertion. We must be able to recover from that scenario.
- var localSelf = self;
- self.popupCloseChecker = window.setInterval(function() {
- if (localSelf.popup && localSelf.popup.closed) {
- // So the user canceled and the window closed.
- // It turns out we hae nothing special to do.
- // If we were graying out the entire page while the child window was up,
- // we would probably revert that here.
- trace('User or OP canceled by closing the window.');
- window.clearInterval(localSelf.popupCloseChecker);
- localSelf.popup = null;
- }
- }, 250);
- };
+ var favicon = opUri.getAuthority() + "/favicon.ico";
+ trace('Guessing favicon location of: ' + favicon);
+ return favicon;
};
/*****************************************
@@ -565,7 +307,8 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
box.dnoi_internal.authenticationIFrames.closeFrames();
box.dnoi_internal.setVisualCue('discovering');
box.lastDiscoveredIdentifier = identifier;
- box.dnoi_internal.discoverIdentifier(identifier, box.dnoi_internal.discoveryResult, box.dnoi_internal.discoveryFailed);
+ var openid = new window.OpenIdIdentifier(identifier);
+ openid.discover(box.dnoi_internal.discoverySuccess, box.dnoi_internal.discoveryFailed);
};
/// <summary>Callback that is invoked when discovery fails.</summary>
@@ -577,95 +320,64 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
/// <summary>Callback that is invoked when discovery results are available.</summary>
/// <param name="discoveryResult">The JSON object containing the OpenID auth requests.</param>
/// <param name="identifier">The identifier that discovery was performed on.</param>
- box.dnoi_internal.discoveryResult = function(discoveryResult, identifier) {
- // Deserialize the JSON object and store the result if it was a successful discovery.
- discoveryResult = eval('(' + discoveryResult + ')');
- // Store the discovery results and added behavior for later use.
- box.dnoi_internal.authenticationRequests[identifier] = discoveryBehavior = new box.dnoi_internal.createDiscoveryInfo(discoveryResult, identifier);
-
+ box.dnoi_internal.discoverySuccess = function(discoveryResult) {
// Only act on the discovery event if we're still interested in the result.
// If the user already changed the identifier since discovery was initiated,
// we aren't interested in it any more.
- if (identifier == box.lastDiscoveredIdentifier) {
- discoveryBehavior.tryImmediate();
+ if (discoveryResult.userSuppliedIdentifier === box.lastDiscoveredIdentifier) {
+ // Start pre-fetching the OP favicons
+ for (var i = 0; i < discoveryResult.length; i++) {
+ var favicon = box.dnoi_internal.deriveOPFavIcon(discoveryResult[i].endpoint);
+ if (favicon) {
+ trace('Prefetching ' + favicon);
+ box.dnoi_internal.prefetchImage(favicon);
+ }
+ }
+ discoveryResult.loginBackground(
+ box.dnoi_internal.authenticationIFrames,
+ box.dnoi_internal.onAuthSuccess,
+ box.dnoi_internal.onAuthFailed,
+ box.dnoi_internal.lastAuthenticationFailed,
+ box.timeout);
}
}
- /// <summary>Invoked by RP web server when an authentication has completed.</summary>
- /// <remarks>The duty of this method is to distribute the notification to the appropriate tracking object.</remarks>
- box.dnoi_internal.processAuthorizationResult = function(resultUrl) {
- self.waiting_openidBox = null;
- //trace('processAuthorizationResult ' + resultUrl);
- var resultUri = new Uri(resultUrl);
+ box.dnoi_internal.lastAuthenticationFailed = function() {
+ trace('No asynchronous authentication attempt is in progress. Display setup view.');
+ // visual cue that auth failed
+ box.dnoi_internal.setVisualCue('setup');
+ };
- // Find the tracking object responsible for this request.
- var discoveryInfo = box.dnoi_internal.authenticationRequests[resultUri.getQueryArgValue('dotnetopenid.userSuppliedIdentifier')];
- if (discoveryInfo == null) {
- trace('processAuthorizationResult called but no userSuppliedIdentifier parameter was found. Exiting function.');
- return;
+ box.dnoi_internal.onAuthSuccess = function(discoveryResult, respondingEndpoint) {
+ // visual cue that auth was successful
+ box.dnoi_internal.claimedIdentifier = discoveryResult.claimedIdentifier;
+ box.dnoi_internal.setVisualCue('authenticated', respondingEndpoint.endpoint, discoveryResult.claimedIdentifier);
+ if (box.dnoi_internal.onauthenticated) {
+ box.dnoi_internal.onauthenticated(box);
}
- var opEndpoint = resultUri.getQueryArgValue("openid.op_endpoint") ? resultUri.getQueryArgValue("openid.op_endpoint") : resultUri.getQueryArgValue("dotnetopenid.op_endpoint");
- var tracker = discoveryInfo.findByEndpoint(opEndpoint);
- //trace('Auth result for ' + tracker.host + ' received:\n' + resultUrl);
-
- if (isAuthSuccessful(resultUri)) {
- tracker.authSuccess(resultUri);
-
- discoveryInfo.successAuthData = resultUrl;
- var claimed_id = resultUri.getQueryArgValue("openid.claimed_id");
- if (claimed_id && claimed_id != discoveryInfo.claimedIdentifier) {
- discoveryInfo.claimedIdentifier = resultUri.getQueryArgValue("openid.claimed_id");
- trace('Authenticated as ' + claimed_id);
- }
- // visual cue that auth was successful
- box.dnoi_internal.claimedIdentifier = discoveryInfo.claimedIdentifier;
- box.dnoi_internal.setVisualCue('authenticated', tracker.endpoint, discoveryInfo.claimedIdentifier);
- if (box.dnoi_internal.onauthenticated) {
- box.dnoi_internal.onauthenticated(box);
- }
- if (box.dnoi_internal.submitPending) {
- // We submit the form BEFORE resetting the submitPending so
- // the submit handler knows we've already tried this route.
- if (box.dnoi_internal.submitPending == true) {
- box.parentForm.submit();
- } else {
- box.dnoi_internal.submitPending.click();
- }
+ if (box.dnoi_internal.submitPending) {
+ // We submit the form BEFORE resetting the submitPending so
+ // the submit handler knows we've already tried this route.
+ if (box.dnoi_internal.submitPending == true) {
+ box.parentForm.submit();
+ } else {
+ box.dnoi_internal.submitPending.click();
}
- } else {
- tracker.authFailed();
- }
- box.dnoi_internal.submitPending = null;
- };
-
- function isAuthSuccessful(resultUri) {
- if (isOpenID2Response(resultUri)) {
- return resultUri.getQueryArgValue("openid.mode") == "id_res";
- } else {
- return resultUri.getQueryArgValue("openid.mode") == "id_res" && !resultUri.containsQueryArg("openid.user_setup_url");
+ box.dnoi_internal.submitPending = null;
}
};
- function isOpenID2Response(resultUri) {
- return resultUri.containsQueryArg("openid.ns");
+ box.dnoi_internal.onAuthFailed = function() {
+ box.dnoi_internal.submitPending = null;
};
-
+
box.onblur = function(event) {
- var discoveryInfo = box.dnoi_internal.authenticationRequests[box.value];
- if (discoveryInfo == null) {
- if (box.value.length > 0) {
- box.dnoi_internal.performDiscovery(box.value);
- } else {
- box.dnoi_internal.setVisualCue();
- }
+ if (box.value.length > 0) {
+ box.dnoi_internal.performDiscovery(box.value);
} else {
- if ((priorSuccess = discoveryInfo.findSuccessfulRequest())) {
- box.dnoi_internal.setVisualCue('authenticated', priorSuccess.endpoint, discoveryInfo.claimedIdentifier);
- } else {
- discoveryInfo.tryImmediate();
- }
+ box.dnoi_internal.setVisualCue();
}
return true;
};
@@ -679,99 +391,25 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
// Restore a previously achieved state (from pre-postback) if it is given.
var oldAuth = findOrCreateHiddenField().value;
if (oldAuth.length > 0) {
- var oldAuthResult = new Uri(oldAuth);
+ var oldAuthResult = new window.dnoa_internal.Uri(oldAuth);
// The control ensures that we ALWAYS have an OpenID 2.0-style claimed_id attribute, even against
// 1.0 Providers via the return_to URL mechanism.
- var claimedId = oldAuthResult.getQueryArgValue("dotnetopenid.claimed_id");
- var endpoint = oldAuthResult.getQueryArgValue("dotnetopenid.op_endpoint");
+ var claimedId = oldAuthResult.getQueryArgValue("dnoa.claimed_id");
+ var endpoint = oldAuthResult.getQueryArgValue("dnoa.op_endpoint");
+ var userSuppliedIdentifier = oldAuthResult.getQueryArgValue('dnoa.userSuppliedIdentifier');
+
+// TODO:
// We weren't given a full discovery history, but we can spoof this much from the
// authentication assertion.
- box.dnoi_internal.authenticationRequests[box.value] = new box.dnoi_internal.createDiscoveryInfo({
+ //new window.OpenIdIdentifier(userSuppliedIdentifier).discover
+ var discoveryResult = {
claimedIdentifier: claimedId,
requests: [{ endpoint: endpoint }]
- }, box.value);
-
- box.dnoi_internal.processAuthorizationResult(oldAuthResult.toString());
- }
-}
-
-function Uri(url) {
- this.originalUri = url;
-
- this.toString = function() {
- return this.originalUri;
- };
+ };
- this.getAuthority = function() {
- var authority = this.getScheme() + "://" + this.getHost();
- return authority;
- }
+ // window.dnoa_internal.discoveryResults[box.value] = discoveryResult;
- this.getHost = function() {
- var hostStartIdx = this.originalUri.indexOf("://") + 3;
- var hostEndIndex = this.originalUri.indexOf("/", hostStartIdx);
- if (hostEndIndex < 0) hostEndIndex = this.originalUri.length;
- var host = this.originalUri.substr(hostStartIdx, hostEndIndex - hostStartIdx);
- return host;
+ // restore old state from before postback
+ //window.dnoa_internal.processAuthorizationResult(oldAuthResult.toString());
}
-
- this.getScheme = function() {
- var schemeStartIdx = this.indexOf("://");
- return this.originalUri.substr(this.originalUri, schemeStartIdx);
- }
-
- this.trimFragment = function() {
- var hashmark = this.originalUri.indexOf('#');
- if (hashmark >= 0) {
- return new Uri(this.originalUri.substr(0, hashmark));
- }
- return this;
- };
-
- this.appendQueryVariable = function(name, value) {
- var pair = encodeURI(name) + "=" + encodeURI(value);
- if (this.originalUri.indexOf('?') >= 0) {
- this.originalUri = this.originalUri + "&" + pair;
- } else {
- this.originalUri = this.originalUri + "?" + pair;
- }
- };
-
- function KeyValuePair(key, value) {
- this.key = key;
- this.value = value;
- };
-
- this.Pairs = new Array();
-
- var queryBeginsAt = this.originalUri.indexOf('?');
- if (queryBeginsAt >= 0) {
- this.queryString = url.substr(queryBeginsAt + 1);
- var queryStringPairs = this.queryString.split('&');
-
- for (var i = 0; i < queryStringPairs.length; i++) {
- var equalsAt = queryStringPairs[i].indexOf('=');
- left = (equalsAt >= 0) ? queryStringPairs[i].substring(0, equalsAt) : null;
- right = (equalsAt >= 0) ? queryStringPairs[i].substring(equalsAt + 1) : queryStringPairs[i];
- this.Pairs.push(new KeyValuePair(unescape(left), unescape(right)));
- }
- };
-
- this.getQueryArgValue = function(key) {
- for (var i = 0; i < this.Pairs.length; i++) {
- if (this.Pairs[i].key == key) {
- return this.Pairs[i].value;
- }
- }
- };
-
- this.containsQueryArg = function(key) {
- return this.getQueryArgValue(key);
- };
-
- this.indexOf = function(args) {
- return this.originalUri.indexOf(args);
- };
-
- return this;
-};
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs
index a917e24..a56f257 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs
@@ -610,7 +610,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
// Add state that needs to survive across the redirect.
- this.Request.AddCallbackArguments(UsePersistentCookieCallbackKey, this.UsePersistentCookie.ToString(CultureInfo.InvariantCulture));
+ this.Request.SetCallbackArgument(UsePersistentCookieCallbackKey, this.UsePersistentCookie.ToString(CultureInfo.InvariantCulture));
} else {
Logger.OpenId.WarnFormat("An invalid identifier was entered ({0}), but not caught by any validation routine.", this.Text);
this.Request = null;
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
index 5ab8053..8c0a7a7 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
@@ -12,24 +12,18 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
- using System.Drawing.Design;
using System.Globalization;
using System.Linq;
using System.Text;
- using System.Text.RegularExpressions;
using System.Web;
- using System.Web.Security;
using System.Web.UI;
- using DotNetOpenAuth.ComponentModel;
- using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Extensions;
- using DotNetOpenAuth.OpenId.Extensions.UI;
/// <summary>
/// A common base class for OpenID Relying Party controls.
/// </summary>
- internal abstract class OpenIdRelyingPartyAjaxControlBase : OpenIdRelyingPartyControlBase, ICallbackEventHandler {
+ public abstract class OpenIdRelyingPartyAjaxControlBase : OpenIdRelyingPartyControlBase, ICallbackEventHandler {
/// <summary>
/// The manifest resource name of the javascript file to include on the hosting page.
/// </summary>
@@ -46,6 +40,45 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
protected const string CallbackJsFunctionAsync = "window.dnoa_internal.callbackAsync";
/// <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>
+ /// The viewstate key to use for storing the value of the a successful authentication.
+ /// </summary>
+ private const string AuthDataViewStateKey = "AuthData";
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="AuthenticationResponse"/> property.
+ /// </summary>
+ private const string AuthenticationResponseViewStateKey = "AuthenticationResponse";
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="AuthenticationProcessedAlready"/> property.
+ /// </summary>
+ private const string AuthenticationProcessedAlreadyViewStateKey = "AuthenticationProcessedAlready";
+
+ #endregion
+
+ /// <summary>
+ /// The authentication response that just came in.
+ /// </summary>
+ private IAuthenticationResponse authenticationResponse;
+
+ /// <summary>
+ /// Backing field for the <see cref="RelyingPartyNonVerifying"/> property.
+ /// </summary>
+ private static OpenIdRelyingParty relyingPartyNonVerifying;
+
+ /// <summary>
/// Stores the result of a AJAX callback discovery.
/// </summary>
private string discoveryResult;
@@ -61,10 +94,26 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
protected OpenIdRelyingPartyAjaxControlBase() {
// The AJAX login style always uses popups (or invisible iframes).
- this.Popup = PopupBehavior.Always;
+ base.Popup = PopupBehavior.Always;
+
+ // The expected use case for the AJAX login box is for comments... not logging in.
+ this.LoginMode = LoginSiteNotification.None;
}
/// <summary>
+ /// Fired when a Provider sends back a positive assertion to this control,
+ /// but the authentication has not yet been verified.
+ /// </summary>
+ /// <remarks>
+ /// <b>No security critical decisions should be made within event handlers
+ /// for this event</b> as the authenticity of the assertion has not been
+ /// verified yet. All security related code should go in the event handler
+ /// for the <see cref="LoggedIn"/> event.
+ /// </remarks>
+ [Description("Fired when a Provider sends back a positive assertion to this control, but the authentication has not yet been verified.")]
+ public event EventHandler<OpenIdEventArgs> UnconfirmedPositiveAssertion;
+
+ /// <summary>
/// Gets or sets a value indicating when to use a popup window to complete the login experience.
/// </summary>
/// <value>The default value is <see cref="PopupBehavior.Never"/>.</value>
@@ -74,6 +123,90 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
set { ErrorUtilities.VerifySupported(value == base.Popup, OpenIdStrings.PropertyValueNotSupported); }
}
+ /// <summary>
+ /// Gets the completed authentication response.
+ /// </summary>
+ public IAuthenticationResponse AuthenticationResponse {
+ get {
+ if (this.authenticationResponse == null) {
+ // We will either validate a new response and return a live AuthenticationResponse
+ // or we will try to deserialize a previous IAuthenticationResponse (snapshot)
+ // from viewstate and return that.
+ IAuthenticationResponse viewstateResponse = this.ViewState[AuthenticationResponseViewStateKey] as IAuthenticationResponse;
+ string viewstateAuthData = this.ViewState[AuthDataViewStateKey] as string;
+ string formAuthData = this.Page.Request.Form[this.OpenIdAuthDataFormKey];
+
+ // First see if there is fresh auth data to be processed into a response.
+ if (!string.IsNullOrEmpty(formAuthData) && !string.Equals(viewstateAuthData, formAuthData, StringComparison.Ordinal)) {
+ this.ViewState[AuthDataViewStateKey] = formAuthData;
+
+ Uri authUri = new Uri(formAuthData);
+ HttpRequestInfo clientResponseInfo = new HttpRequestInfo {
+ UrlBeforeRewriting = authUri,
+ };
+
+ this.authenticationResponse = this.RelyingParty.GetResponse(clientResponseInfo);
+ this.AuthenticationProcessedAlready = false;
+
+ // Save out the authentication response to viewstate so we can find it on
+ // a subsequent postback.
+ this.ViewState[AuthenticationResponseViewStateKey] = new PositiveAuthenticationResponseSnapshot(this.authenticationResponse);
+ } else {
+ this.authenticationResponse = viewstateResponse;
+ }
+ }
+
+ return this.authenticationResponse;
+ }
+ }
+
+ /// <summary>
+ /// Gets the name of the open id auth data form key.
+ /// </summary>
+ /// <value>Usually a concatenation of the control's name and <c>"_openidAuthData"</c>.</value>
+ protected abstract string OpenIdAuthDataFormKey { get; }
+
+ /// <summary>
+ /// Gets the relying party to use when verification of incoming messages is NOT wanted.
+ /// </summary>
+ private static OpenIdRelyingParty RelyingPartyNonVerifying {
+ get {
+ if (relyingPartyNonVerifying == null) {
+ relyingPartyNonVerifying = OpenIdRelyingParty.CreateNonVerifying();
+ }
+ return relyingPartyNonVerifying;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether an authentication in the page's view state
+ /// has already been processed and appropriate events fired.
+ /// </summary>
+ private bool AuthenticationProcessedAlready {
+ get { return (bool)(ViewState[AuthenticationProcessedAlreadyViewStateKey] ?? false); }
+ set { ViewState[AuthenticationProcessedAlreadyViewStateKey] = value; }
+ }
+
+ /// <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 from the <see cref="UnconfirmedPositiveAssertion"/> event handler.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "By design")]
+ public void RegisterClientScriptExtension<T>(string propertyName) where T : IClientScriptExtensionResponse {
+ ErrorUtilities.VerifyNonZeroLength(propertyName, "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);
+ }
+
#region ICallbackEventHandler Members
/// <summary>
@@ -118,7 +251,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
discoveryResultBuilder.AppendFormat("claimedIdentifier: {0},", MessagingUtilities.GetSafeJavascriptValue(requests.First().ClaimedIdentifier));
discoveryResultBuilder.Append("requests: [");
foreach (IAuthenticationRequest request in requests) {
- this.OnLoggingIn(request);
discoveryResultBuilder.Append("{");
discoveryResultBuilder.AppendFormat("endpoint: {0},", MessagingUtilities.GetSafeJavascriptValue(request.Provider.Uri.AbsoluteUri));
request.Mode = AuthenticationRequestMode.Immediate;
@@ -139,6 +271,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
discoveryResultBuilder.Append("requests: new Array(),");
discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(ex.Message));
}
+
discoveryResultBuilder.Append("}");
this.discoveryResult = discoveryResultBuilder.ToString();
}
@@ -146,6 +279,39 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
/// <summary>
+ /// Fires the <see cref="UnconfirmedPositiveAssertion"/> event.
+ /// </summary>
+ protected virtual void OnUnconfirmedPositiveAssertion() {
+ var unconfirmedPositiveAssertion = this.UnconfirmedPositiveAssertion;
+ if (unconfirmedPositiveAssertion != null) {
+ unconfirmedPositiveAssertion(this, null);
+ }
+ }
+
+ /// <summary>
+ /// Raises the <see cref="E:Load"/> event.
+ /// </summary>
+ /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
+ protected override void OnLoad(EventArgs e) {
+ base.OnLoad(e);
+
+ // Our parent control ignores all OpenID messages included in a postback,
+ // but our AJAX controls hide an old OpenID message in a postback payload,
+ // so we deserialize it and process it when appropriate.
+ if (this.Page.IsPostBack) {
+ if (!this.AuthenticationProcessedAlready && this.AuthenticationResponse != null) {
+ // Only process messages targeted at this control.
+ // Note that Stateless mode causes no receiver to be indicated.
+ string receiver = this.AuthenticationResponse.GetCallbackArgument(ReturnToReceivingControlId);
+ if (receiver == null || receiver == this.ClientID) {
+ this.ProcessResponse(this.AuthenticationResponse);
+ this.AuthenticationProcessedAlready = true;
+ }
+ }
+ }
+ }
+
+ /// <summary>
/// Creates the authentication requests for a given user-supplied Identifier.
/// </summary>
/// <returns>A sequence of authentication requests, any one of which may be
@@ -177,6 +343,62 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Sends server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object, which writes the content to be rendered on the client.
+ /// </summary>
+ /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param>
+ protected override void Render(HtmlTextWriter writer) {
+ base.Render(writer);
+
+ // Emit a hidden field to let the javascript on the user agent know if an
+ // authentication has already successfully taken place.
+ string viewstateAuthData = this.ViewState[AuthDataViewStateKey] as string;
+ if (!string.IsNullOrEmpty(viewstateAuthData)) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Name, this.OpenIdAuthDataFormKey);
+ writer.AddAttribute(HtmlTextWriterAttribute.Value, viewstateAuthData, true);
+ writer.AddAttribute(HtmlTextWriterAttribute.Type, "hidden");
+ writer.RenderBeginTag(HtmlTextWriterTag.Input);
+ writer.RenderEndTag();
+ }
+ }
+
+ /// <summary>
+ /// Notifies the user agent via an AJAX response of a completed authentication attempt.
+ /// </summary>
+ protected override void ScriptClosingPopupOrIFrame() {
+ Logger.OpenId.InfoFormat("AJAX (iframe) callback from OP: {0}", this.Page.Request.Url);
+ List<string> assignments = new List<string>();
+
+ var authResponse = RelyingPartyNonVerifying.GetResponse();
+ if (authResponse.Status == AuthenticationStatus.Authenticated) {
+ this.OnUnconfirmedPositiveAssertion(); // event handler will fill the clientScriptExtensions collection.
+ 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)) {
+ js = "null";
+ }
+ assignments.Add(pair.Value + " = " + js);
+ }
+ }
+
+ 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);
+ }
+ this.CallbackUserAgentMethod("dnoa_internal.processAuthorizationResult(" + payload + ")", assignments.ToArray());
+ }
+
+ /// <summary>
/// Creates the authentication requests for a given user-supplied Identifier.
/// </summary>
/// <param name="requests">The authentication requests to prepare.</param>
@@ -190,25 +412,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// Configure each generated request.
int reqIndex = 0;
foreach (var req in requests) {
- req.AddCallbackArguments("index", (reqIndex++).ToString(CultureInfo.InvariantCulture));
-
- if (req.Provider.IsExtensionSupported<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.AddCallbackArguments("dotnetopenid.popupUISupported", "1");
- }
+ req.SetCallbackArgument("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)["dotnetopenid.userSuppliedIdentifier"])) {
- req.AddCallbackArguments("dotnetopenid.userSuppliedIdentifier", this.Identifier);
+ if (string.IsNullOrEmpty(HttpUtility.ParseQueryString(req.ReturnToUrl.Query)[AuthenticationRequest.UserSuppliedIdentifierParameterName])) {
+ req.SetCallbackArgument(AuthenticationRequest.UserSuppliedIdentifierParameterName, this.Identifier.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.AddCallbackArguments("dotnetopenid.op_endpoint", req.Provider.Uri.AbsoluteUri);
- req.AddCallbackArguments("dotnetopenid.claimed_id", (string)req.ClaimedIdentifier ?? string.Empty);
+ req.SetCallbackArgument(OPEndpointParameterName, req.Provider.Uri.AbsoluteUri);
+ req.SetCallbackArgument(ClaimedIdParameterName, (string)req.ClaimedIdentifier ?? string.Empty);
+
+ // Inform ourselves in return_to that we're in a popup or iframe.
+ req.SetCallbackArgument(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
@@ -248,33 +465,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Notifies the user agent via an AJAX response of a completed authentication attempt.
- /// </summary>
- private void ReportAuthenticationResult() {
- Logger.OpenId.InfoFormat("AJAX (iframe) callback from OP: {0}", this.Page.Request.Url);
- List<string> assignments = new List<string>();
-
- var authResponse = this.RelyingParty.GetResponse();
- if (authResponse.Status == AuthenticationStatus.Authenticated) {
- this.OnLoggedIn(authResponse);
- 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)) {
- js = "null";
- }
- assignments.Add(pair.Value + " = " + js);
- }
- }
-
- this.CallbackUserAgentMethod("dnoi_internal.processAuthorizationResult(document.URL)", assignments.ToArray());
- }
-
- /// <summary>
/// Invokes a method on a parent frame/window's OpenIdAjaxTextBox,
/// and closes the calling popup window if applicable.
/// </summary>
@@ -295,7 +485,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
Logger.OpenId.InfoFormat("Sending Javascript callback: {0}", methodCall);
Page.Response.Write(@"<html><body><script language='javascript'>
var inPopup = !window.frameElement;
- var objSrc = inPopup ? window.opener.waiting_openidBox : window.frameElement.openidBox;
+ var objSrc = inPopup ? window.opener : window.frameElement;
");
if (preAssignments != null) {
foreach (string assignment in preAssignments) {
@@ -307,11 +497,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// 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};
-}}
+ objSrc.{0};
+ window.self.close();
+ }} else {{
+ objSrc.{0};
+ }}
</script></body></html>";
Page.Response.Write(string.Format(CultureInfo.InvariantCulture, htmlFormat, methodCall));
Page.Response.End();
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js
index 65b1b99..e0fcd98 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js
@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------
-// <copyright file="OpenIdRelyingPartyControlBase.js" company="Andrew Arnott">
+// <copyright file="OpenIdRelyingPartyAjaxControlBase.js" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
@@ -8,15 +8,109 @@ if (window.dnoa_internal === undefined) {
window.dnoa_internal = new Object();
};
+/// <summary>Removes a given element from the array.</summary>
+/// <returns>True if the element was in the array, or false if it was not found.</returns>
+Array.prototype.remove = function(element) {
+ function elementToRemoveLast(a, b) {
+ if (a == element) { return 1; }
+ if (b == element) { return -1; }
+ return 0;
+ }
+ this.sort(elementToRemoveLast);
+ if (this[this.length - 1] == element) {
+ this.pop();
+ return true;
+ } else {
+ return false;
+ }
+};
+
window.dnoa_internal.discoveryResults = new Array(); // user supplied identifiers and discovery results
+// The possible authentication results
+window.dnoa_internal.authSuccess = new Object();
+window.dnoa_internal.authRefused = new Object();
+window.dnoa_internal.timedOut = new Object();
+
+/// <summary>Instantiates a new FrameManager.</summary>
+/// <param name="maxFrames">The maximum number of concurrent 'jobs' (authentication attempts).</param>
+window.dnoa_internal.FrameManager = function(maxFrames) {
+ this.queuedWork = new Array();
+ this.frames = new Array();
+ this.maxFrames = maxFrames;
+
+ /// <summary>Called to queue up some work that will use an iframe as soon as it is available.</summary>
+ /// <param name="job">
+ /// A delegate that must return the url to point the iframe to.
+ /// Its first parameter is the iframe created to service the request.
+ /// It will only be called when the work actually begins.
+ /// </param>
+ /// <param name="p1">Arbitrary additional parameter to pass to the job.</param>
+ this.enqueueWork = function(job, p1) {
+ // Assign an iframe to this task immediately if there is one available.
+ if (this.frames.length < this.maxFrames) {
+ this.createIFrame(job, p1);
+ } else {
+ this.queuedWork.unshift({ job: job, p1: p1 });
+ }
+ };
+
+ /// <summary>Clears the job queue and immediately closes all iframes.</summary>
+ this.cancelAllWork = function() {
+ trace('Canceling all open and pending iframes.');
+ while (this.queuedWork.pop());
+ this.closeFrames();
+ };
+
+ /// <summary>An event fired when a frame is closing.</summary>
+ this.onJobCompleted = function() {
+ // If there is a job in the queue, go ahead and start it up.
+ if (jobDesc = this.queuedWork.pop()) {
+ this.createIFrame(jobDesc.job, jobDesc.p1);
+ }
+ }
+
+ this.createIFrame = function(job, p1) {
+ var iframe = document.createElement("iframe");
+ if (!window.openid_visible_iframe) {
+ iframe.setAttribute("width", 0);
+ iframe.setAttribute("height", 0);
+ iframe.setAttribute("style", "display: none");
+ }
+ iframe.setAttribute("src", job(iframe, p1));
+ iframe.dnoa_internal = window.dnoa_internal;
+ box.parentNode.insertBefore(iframe, box);
+ this.frames.push(iframe);
+ return iframe;
+ };
+
+ this.closeFrames = function() {
+ if (this.frames.length == 0) { return false; }
+ for (var i = 0; i < this.frames.length; i++) {
+ if (this.frames[i].parentNode) { this.frames[i].parentNode.removeChild(this.frames[i]); }
+ }
+ while (this.frames.length > 0) { this.frames.pop(); }
+ return true;
+ };
+
+ this.closeFrame = function(frame) {
+ if (frame.parentNode) { frame.parentNode.removeChild(frame); }
+ var removed = this.frames.remove(frame);
+ this.onJobCompleted();
+ return removed;
+ };
+};
+
/// <summary>Instantiates an object that represents an OpenID Identifier.</summary>
-window.OpenId = function(identifier) {
+window.OpenIdIdentifier = function(identifier) {
/// <summary>Performs discovery on the identifier.</summary>
- /// <param name="onCompleted">A function(DiscoveryResult) callback to be called when discovery has completed.</param>
- this.discover = function(onCompleted) {
+ /// <param name="onDiscoverSuccess">A function(DiscoveryResult) callback to be called when discovery has completed successfully.</param>
+ /// <param name="onDiscoverFailure">A function callback to be called when discovery has completed in failure.</param>
+ this.discover = function(onDiscoverSuccess, onDiscoverFailure) {
/// <summary>Instantiates an object that stores discovery results of some identifier.</summary>
function DiscoveryResult(identifier, discoveryInfo) {
+ var thisDiscoveryResult = this;
+
/// <summary>
/// Instantiates an object that describes an OpenID service endpoint and facilitates
/// initiating and tracking an authentication request.
@@ -25,68 +119,136 @@ window.OpenId = function(identifier) {
this.immediate = requestInfo.immediate ? new window.dnoa_internal.Uri(requestInfo.immediate) : null;
this.setup = requestInfo.setup ? new window.dnoa_internal.Uri(requestInfo.setup) : null;
this.endpoint = new window.dnoa_internal.Uri(requestInfo.endpoint);
+ this.host = this.endpoint.getHost();
this.userSuppliedIdentifier = userSuppliedIdentifier;
- var self = this; // closure so that delegates have the right instance
- this.loginPopup = function(onSuccess, onFailure) {
- //self.abort(); // ensure no concurrent attempts
- window.dnoa_internal.processAuthorizationResult = function(childLocation) {
- window.dnoa_internal.processAuthorizationResult = null;
- trace('Received event from child window: ' + childLocation);
- var success = true; // TODO: discern between success and failure, and fire the correct event.
-
- if (success) {
- if (onSuccess) {
- onSuccess();
- }
- } else {
- if (onFailure) {
- onFailure();
- }
- }
- };
+ var thisServiceEndpoint = this; // closure so that delegates have the right instance
+ this.loginPopup = function(onAuthSuccess, onAuthFailed) {
+ thisServiceEndpoint.abort(); // ensure no concurrent attempts
+ thisDiscoveryResult.onAuthSuccess = onAuthSuccess;
+ thisDiscoveryResult.onAuthFailed = onAuthFailed;
var width = 800;
var height = 600;
- if (self.setup.getQueryArgValue("openid.return_to").indexOf("dotnetopenid.popupUISupported") >= 0) {
+ if (thisServiceEndpoint.setup.getQueryArgValue("openid.return_to").indexOf("dnoa.popupUISupported") >= 0) {
+ trace('This OP supports the UI extension. Using smaller window size.');
width = 450;
height = 500;
+ } else {
+ trace("This OP doesn't appear to support the UI extension. Using larger window size.");
}
var left = (screen.width - width) / 2;
var top = (screen.height - height) / 2;
- self.popup = window.open(self.setup, 'opLogin', 'status=0,toolbar=0,location=1,resizable=1,scrollbars=1,left=' + left + ',top=' + top + ',width=' + width + ',height=' + height);
+ thisServiceEndpoint.popup = window.open(thisServiceEndpoint.setup, 'opLogin', 'status=0,toolbar=0,location=1,resizable=1,scrollbars=1,left=' + left + ',top=' + top + ',width=' + width + ',height=' + height);
// If the OP supports the UI extension it MAY close its own window
// for a negative assertion. We must be able to recover from that scenario.
- var localSelf = self;
- self.popupCloseChecker = window.setInterval(function() {
- if (localSelf.popup && localSelf.popup.closed) {
+ var thisServiceEndpointLocal = thisServiceEndpoint;
+ thisServiceEndpoint.popupCloseChecker = window.setInterval(function() {
+ if (thisServiceEndpointLocal.popup && thisServiceEndpointLocal.popup.closed) {
// The window closed, either because the user closed it, canceled at the OP,
// or approved at the OP and the popup window closed itself due to our script.
// If we were graying out the entire page while the child window was up,
// we would probably revert that here.
- window.clearInterval(localSelf.popupCloseChecker);
- localSelf.popup = null;
+ window.clearInterval(thisServiceEndpointLocal.popupCloseChecker);
+ thisServiceEndpointLocal.popup = null;
// The popup may have managed to inform us of the result already,
// so check whether the callback method was cleared already, which
// would indicate we've already processed this.
if (window.dnoa_internal.processAuthorizationResult) {
trace('User or OP canceled by closing the window.');
- if (onFailure) {
- onFailure();
+ if (thisDiscoveryResult.onAuthFailed) {
+ thisDiscoveryResult.onAuthFailed(thisDiscoveryResult, thisServiceEndpoint);
}
window.dnoa_internal.processAuthorizationResult = null;
}
}
}, 250);
};
+
+ this.loginBackgroundJob = function(iframe, timeout) {
+ thisServiceEndpoint.abort(); // ensure no concurrent attempts
+ if (timeout) {
+ thisServiceEndpoint.timeout = setTimeout(function() { thisServiceEndpoint.onAuthenticationTimedOut(); }, timeout);
+ }
+ trace('iframe hosting ' + thisServiceEndpoint.endpoint + ' now OPENING (timeout ' + timeout + ').');
+ //trace('initiating auth attempt with: ' + thisServiceEndpoint.immediate);
+ thisServiceEndpoint.iframe = iframe;
+ return thisServiceEndpoint.immediate.toString();
+ };
+
+ this.busy = function() {
+ return thisServiceEndpoint.iframe != null || thisServiceEndpoint.popup != null;
+ };
+
+ this.completeAttempt = function(successful) {
+ if (!thisServiceEndpoint.busy()) return false;
+ window.clearInterval(thisServiceEndpoint.timeout);
+ if (thisServiceEndpoint.iframe) {
+ trace('iframe hosting ' + thisServiceEndpoint.endpoint + ' now CLOSING.');
+ thisDiscoveryResult.frameManager.closeFrame(thisServiceEndpoint.iframe);
+ thisServiceEndpoint.iframe = null;
+ }
+ if (thisServiceEndpoint.popup) {
+ thisServiceEndpoint.popup.close();
+ thisServiceEndpoint.popup = null;
+ }
+ if (thisServiceEndpoint.timeout) {
+ window.clearTimeout(thisServiceEndpoint.timeout);
+ thisServiceEndpoint.timeout = null;
+ }
+
+ if (!successful && !thisDiscoveryResult.busy() && thisDiscoveryResult.findSuccessfulRequest() == null) {
+ if (thisDiscoveryResult.onLastAttemptFailed) {
+ thisDiscoveryResult.onLastAttemptFailed();
+ }
+ }
+
+ return true;
+ };
+
+ this.onAuthenticationTimedOut = function() {
+ if (thisServiceEndpoint.completeAttempt()) {
+ trace(thisServiceEndpoint.host + " timed out");
+ thisServiceEndpoint.result = window.dnoa_internal.timedOut;
+ }
+ };
+
+ this.onAuthSuccess = function(authUri) {
+ if (thisServiceEndpoint.completeAttempt(true)) {
+ trace(thisServiceEndpoint.host + " authenticated!");
+ thisServiceEndpoint.result = window.dnoa_internal.authSuccess;
+ thisServiceEndpoint.response = authUri;
+ thisDiscoveryResult.abortAll();
+ if (thisDiscoveryResult.onAuthSuccess) {
+ thisDiscoveryResult.onAuthSuccess(thisDiscoveryResult, thisServiceEndpoint);
+ }
+ }
+ };
+
+ this.onAuthFailed = function() {
+ if (thisServiceEndpoint.completeAttempt()) {
+ trace(thisServiceEndpoint.host + " failed authentication");
+ thisServiceEndpoint.result = window.dnoa_internal.authRefused;
+ if (thisDiscoveryResult.onAuthFailed) {
+ thisDiscoveryResult.onAuthFailed(thisDiscoveryResult, thisServiceEndpoint);
+ }
+ }
+ };
+
+ this.abort = function() {
+ if (thisServiceEndpoint.completeAttempt()) {
+ trace(thisServiceEndpoint.host + " aborted");
+ // leave the result as whatever it was before.
+ }
+ };
+
};
this.userSuppliedIdentifier = identifier;
- this.claimedIdentifier = discoveryInfo.claimedIdentifier; // The claimed identifier may be null if the user provided an OP Identifier.
- trace('Discovered claimed identifier: ' + (this.claimedIdentifier ? this.claimedIdentifier : "(directed identity)"));
if (discoveryInfo) {
+ this.claimedIdentifier = discoveryInfo.claimedIdentifier; // The claimed identifier may be null if the user provided an OP Identifier.
this.length = discoveryInfo.requests.length;
for (var i = 0; i < discoveryInfo.requests.length; i++) {
this[i] = new ServiceEndpoint(discoveryInfo.requests[i], identifier);
@@ -94,10 +256,75 @@ window.OpenId = function(identifier) {
} else {
this.length = 0;
}
+
+ trace('Discovered claimed identifier: ' + (this.claimedIdentifier ? this.claimedIdentifier : "(directed identity)"));
+
+ // Add extra tracking bits and behaviors.
+ this.findByEndpoint = function(opEndpoint) {
+ for (var i = 0; i < thisDiscoveryResult.length; i++) {
+ if (thisDiscoveryResult[i].endpoint == opEndpoint) {
+ return thisDiscoveryResult[i];
+ }
+ }
+ };
+
+ this.busy = function() {
+ for (var i = 0; i < thisDiscoveryResult.length; i++) {
+ if (thisDiscoveryResult[i].busy()) {
+ return true;
+ }
+ }
+ };
+
+ // Add extra tracking bits and behaviors.
+ this.findSuccessfulRequest = function() {
+ for (var i = 0; i < thisDiscoveryResult.length; i++) {
+ if (thisDiscoveryResult[i].result === window.dnoa_internal.authSuccess) {
+ return thisDiscoveryResult[i];
+ }
+ }
+ };
+
+ this.abortAll = function() {
+ if (thisDiscoveryResult.frameManager) {
+ // Abort all other asynchronous authentication attempts that may be in progress.
+ thisDiscoveryResult.frameManager.cancelAllWork();
+ for (var i = 0; i < thisDiscoveryResult.length; i++) {
+ thisDiscoveryResult[i].abort();
+ }
+ } else {
+ trace('abortAll called without a frameManager being previously set.');
+ }
+ };
+
+ /// <summary>Initiates an asynchronous checkid_immediate login attempt against all possible service endpoints for an Identifier.</summary>
+ /// <param name="frameManager">The work queue for authentication iframes.</param>
+ /// <param name="onAuthSuccess">Fired when an endpoint responds affirmatively.</param>
+ /// <param name="onAuthFailed">Fired when an endpoint responds negatively.</param>
+ /// <param name="onLastAuthFailed">Fired when all authentication attempts have responded negatively or timed out.</param>
+ /// <param name="timeout">Timeout for an individual service endpoint to respond before the iframe closes.</param>
+ this.loginBackground = function(frameManager, onAuthSuccess, onAuthFailed, onLastAuthFailed, timeout) {
+ if (!frameManager) {
+ throw "No frameManager specified.";
+ }
+ if (thisDiscoveryResult.findSuccessfulRequest() != null) {
+ onAuthSuccess(thisDiscoveryResult, thisDiscoveryResult.findSuccessfulRequest());
+ } else {
+ thisDiscoveryResult.frameManager = frameManager;
+ thisDiscoveryResult.onAuthSuccess = onAuthSuccess;
+ thisDiscoveryResult.onAuthFailed = onAuthFailed;
+ thisDiscoveryResult.onLastAttemptFailed = onLastAuthFailed;
+ if (thisDiscoveryResult.length > 0) {
+ for (var i = 0; i < thisDiscoveryResult.length; i++) {
+ thisDiscoveryResult.frameManager.enqueueWork(thisDiscoveryResult[i].loginBackgroundJob, timeout);
+ }
+ }
+ }
+ };
};
/// <summary>Receives the results of a successful discovery (even if it yielded 0 results).</summary>
- function successCallback(discoveryResult, identifier) {
+ function discoverSuccessCallback(discoveryResult, identifier) {
trace('Discovery completed for: ' + identifier);
// Deserialize the JSON object and store the result if it was a successful discovery.
@@ -107,40 +334,60 @@ window.OpenId = function(identifier) {
discoveryResult = new DiscoveryResult(identifier, discoveryResult);
window.dnoa_internal.discoveryResults[identifier] = discoveryResult;
- if (onCompleted) {
- onCompleted(discoveryResult);
+ if (onDiscoverSuccess) {
+ onDiscoverSuccess(discoveryResult);
}
};
/// <summary>Receives the discovery failure notification.</summary>
- failureCallback = function(message, userSuppliedIdentifier) {
+ function discoverFailureCallback(message, userSuppliedIdentifier) {
trace('Discovery failed for: ' + identifier);
- if (onCompleted) {
- onCompleted();
+ if (onDiscoverFailure) {
+ onDiscoverFailure();
}
};
if (window.dnoa_internal.discoveryResults[identifier]) {
trace("We've already discovered " + identifier + " so we're skipping it this time.");
- onCompleted(window.dnoa_internal.discoveryResults[identifier]);
+ if (onDiscoverSuccess) {
+ onDiscoverSuccess(window.dnoa_internal.discoveryResults[identifier]);
+ }
+ return;
}
trace('starting discovery on ' + identifier);
- window.dnoa_internal.callbackAsync(identifier, successCallback, failureCallback);
+ window.dnoa_internal.callbackAsync(identifier, discoverSuccessCallback, discoverFailureCallback);
};
/// <summary>Performs discovery and immediately begins checkid_setup to authenticate the user using a given identifier.</summary>
- this.login = function(onSuccess, onFailure) {
+ this.login = function(onSuccess, onLoginFailure) {
+ this.discover(function(discoveryResult) {
+ if (discoveryResult) {
+ trace('Discovery succeeded and found ' + discoveryResult.length + ' OpenID service endpoints.');
+ if (discoveryResult.length > 0) {
+ discoveryResult[0].loginPopup(onSuccess, onLoginFailure);
+ } else {
+ trace("This doesn't look like an OpenID Identifier. Aborting login.");
+ if (onLoginFailure) {
+ onLoginFailure();
+ }
+ }
+ }
+ });
+ };
+
+ /// <summary>Performs discovery and immediately begins checkid_immediate on all discovered endpoints.</summary>
+ this.loginBackground = function(frameManager, onLoginSuccess, onLoginFailure, timeout) {
this.discover(function(discoveryResult) {
if (discoveryResult) {
trace('Discovery succeeded and found ' + discoveryResult.length + ' OpenID service endpoints.');
if (discoveryResult.length > 0) {
- discoveryResult[0].loginPopup(onSuccess, onFailure);
+ discoveryResult.loginBackground(frameManager, onLoginSuccess, onLoginFailure, onLoginFailure, timeout);
} else {
trace("This doesn't look like an OpenID Identifier. Aborting login.");
- if (onFailure) {
- onFailure();
+ if (onLoginFailure) {
+ onLoginFailure();
}
}
}
@@ -148,3 +395,51 @@ window.OpenId = function(identifier) {
};
};
+/// <summary>Invoked by RP web server when an authentication has completed.</summary>
+/// <remarks>The duty of this method is to distribute the notification to the appropriate tracking object.</remarks>
+window.dnoa_internal.processAuthorizationResult = function(resultUrl) {
+ //trace('processAuthorizationResult ' + resultUrl);
+ var resultUri = new window.dnoa_internal.Uri(resultUrl);
+
+ // Find the tracking object responsible for this request.
+ var userSuppliedIdentifier = resultUri.getQueryArgValue('dnoa.userSuppliedIdentifier');
+ if (!userSuppliedIdentifier) {
+ trace('processAuthorizationResult called but no userSuppliedIdentifier parameter was found. Exiting function.');
+ return;
+ }
+ var discoveryResult = window.dnoa_internal.discoveryResults[userSuppliedIdentifier];
+ if (discoveryResult == null) {
+ trace('processAuthorizationResult called but no discovery result matching user supplied identifier ' + userSuppliedIdentifier + ' was found. Exiting function.');
+ return;
+ }
+
+ var opEndpoint = resultUri.getQueryArgValue("openid.op_endpoint") ? resultUri.getQueryArgValue("openid.op_endpoint") : resultUri.getQueryArgValue("dnoa.op_endpoint");
+ var respondingEndpoint = discoveryResult.findByEndpoint(opEndpoint);
+ trace('Auth result for ' + respondingEndpoint.host + ' received.');//: ' + resultUrl);
+
+ if (window.dnoa_internal.isAuthSuccessful(resultUri)) {
+ discoveryResult.successAuthData = resultUrl;
+ respondingEndpoint.onAuthSuccess(resultUri);
+
+ var claimed_id = resultUri.getQueryArgValue("openid.claimed_id");
+ if (claimed_id && claimed_id != discoveryResult.claimedIdentifier) {
+ discoveryResult.claimedIdentifier = resultUri.getQueryArgValue("openid.claimed_id");
+ trace('Authenticated as ' + claimed_id);
+ }
+ } else {
+ respondingEndpoint.onAuthFailed();
+ }
+};
+
+window.dnoa_internal.isAuthSuccessful = function(resultUri) {
+ if (window.dnoa_internal.isOpenID2Response(resultUri)) {
+ return resultUri.getQueryArgValue("openid.mode") == "id_res";
+ } else {
+ return resultUri.getQueryArgValue("openid.mode") == "id_res" && !resultUri.containsQueryArg("openid.user_setup_url");
+ }
+};
+
+window.dnoa_internal.isOpenID2Response = function(resultUri) {
+ return resultUri.containsQueryArg("openid.ns");
+};
+
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
index 30e7f8b..45390dd 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
@@ -65,6 +65,31 @@ 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";
+
+ /// <summary>
+ /// The callback parameter for use with persisting the <see cref="UsePersistentCookie"/> property.
+ /// </summary>
+ private const string UsePersistentCookieCallbackKey = OpenIdUtilities.CustomParameterPrefix + "UsePersistentCookie";
+
+ /// <summary>
+ /// The callback parameter to use for recognizing when the callback is in the parent window.
+ /// </summary>
+ private const string UIPopupCallbackParentKey = OpenIdUtilities.CustomParameterPrefix + "uipopupParent";
+
+ #endregion
+
#region Property default values
/// <summary>
@@ -148,31 +173,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
- #region Callback parameter names
-
- /// <summary>
- /// The callback parameter for use with persisting the <see cref="UsePersistentCookie"/> property.
- /// </summary>
- private const string UsePersistentCookieCallbackKey = OpenIdUtilities.CustomParameterPrefix + "UsePersistentCookie";
-
- /// <summary>
- /// The callback parameter to use for recognizing when the callback is in a popup window.
- /// </summary>
- private const string UIPopupCallbackKey = OpenIdUtilities.CustomParameterPrefix + "uipopup";
-
- /// <summary>
- /// The callback parameter to use for recognizing when the callback is in the parent window.
- /// </summary>
- private const string UIPopupCallbackParentKey = OpenIdUtilities.CustomParameterPrefix + "uipopupParent";
-
- /// <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>
- private const string PopupUISupportedJsHint = "dotnetopenid.popupUISupported";
-
- #endregion
-
/// <summary>
/// The lifetime of the cookie used to persist the Identifier the user logged in with.
/// </summary>
@@ -198,11 +198,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#region Events
/// <summary>
- /// Fired after the user clicks the log in button, but before the authentication
- /// process begins. Offers a chance for the web application to disallow based on
- /// OpenID URL before redirecting the user to the OpenID Provider.
+ /// Fired when the user has typed in their identifier, discovery was successful
+ /// and a login attempt is about to begin.
/// </summary>
- [Description("Fired after the user clicks the log in button, but before the authentication process begins. Offers a chance for the web application to disallow based on OpenID URL before redirecting the user to the OpenID Provider."), Category(OpenIdCategory)]
+ [Description("Fired when the user has typed in their identifier, discovery was successful and a login attempt is about to begin."), Category(OpenIdCategory)]
public event EventHandler<OpenIdEventArgs> LoggingIn;
/// <summary>
@@ -391,7 +390,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
[Bindable(true), DefaultValue(LoginModeDefault), Category(BehaviorCategory)]
[Description("The way a completed login is communicated to the rest of the web site.")]
- public LoginSiteNotification LoginMode {
+ public virtual LoginSiteNotification LoginMode {
get { return (LoginSiteNotification)(this.ViewState[LoginModeViewStateKey] ?? LoginModeDefault); }
set { this.ViewState[LoginModeViewStateKey] = value; }
}
@@ -532,25 +531,25 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// Configure each generated request.
foreach (var req in requests) {
if (this.IsPopupAppropriate(req)) {
- // Inform the OP that we'll be using a popup window.
- req.AddExtension(new UIRequest());
-
// Inform ourselves in return_to that we're in a popup.
- req.AddCallbackArguments(UIPopupCallbackKey, "1");
+ req.SetCallbackArgument(UIPopupCallbackKey, "1");
if (req.Provider.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.AddCallbackArguments(PopupUISupportedJsHint, "1");
+ req.SetCallbackArgument(PopupUISupportedJsHint, "1");
}
}
// Add state that needs to survive across the redirect.
if (!this.Stateless) {
- req.AddCallbackArguments(UsePersistentCookieCallbackKey, this.UsePersistentCookie.ToString());
- req.AddCallbackArguments(ReturnToReceivingControlId, this.ClientID);
+ req.SetCallbackArgument(UsePersistentCookieCallbackKey, this.UsePersistentCookie.ToString());
+ req.SetCallbackArgument(ReturnToReceivingControlId, this.ClientID);
}
((AuthenticationRequest)req).AssociationPreference = this.AssociationPreference;
@@ -581,7 +580,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
if (this.Page.Request.QueryString[UIPopupCallbackKey] == "1" && this.Page.Request.QueryString[UIPopupCallbackParentKey] == null) {
// We're in a popup window. We need to close it and pass the
// message back to the parent window for processing.
- this.ScriptClosingPopup();
+ this.ScriptClosingPopupOrIFrame();
return; // don't do any more processing on it now
}
@@ -590,32 +589,41 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
string receiver = this.Page.Request.QueryString[ReturnToReceivingControlId] ?? this.Page.Request.Form[ReturnToReceivingControlId];
if (receiver == null || receiver == this.ClientID) {
var response = this.RelyingParty.GetResponse();
- if (response != null) {
- string persistentString = response.GetCallbackArgument(UsePersistentCookieCallbackKey);
- if (persistentString != null) {
- this.UsePersistentCookie = (LoginPersistence)Enum.Parse(typeof(LoginPersistence), persistentString);
- }
+ this.ProcessResponse(response);
+ }
+ }
- switch (response.Status) {
- case AuthenticationStatus.Authenticated:
- this.OnLoggedIn(response);
- break;
- case AuthenticationStatus.Canceled:
- this.OnCanceled(response);
- break;
- case AuthenticationStatus.Failed:
- this.OnFailed(response);
- break;
- case AuthenticationStatus.SetupRequired:
- case AuthenticationStatus.ExtensionsOnly:
- default:
- // The NotApplicable (extension-only assertion) is NOT one that we support
- // in this control because that scenario is primarily interesting to RPs
- // that are asking a specific OP, and it is not user-initiated as this textbox
- // is designed for.
- throw new InvalidOperationException(MessagingStrings.UnexpectedMessageReceivedOfMany);
- }
- }
+ /// <summary>
+ /// Processes the response.
+ /// </summary>
+ /// <param name="response">The response.</param>
+ protected virtual void ProcessResponse(IAuthenticationResponse response) {
+ if (response == null) {
+ return;
+ }
+ string persistentString = response.GetCallbackArgument(UsePersistentCookieCallbackKey);
+ if (persistentString != null) {
+ this.UsePersistentCookie = (LoginPersistence)Enum.Parse(typeof(LoginPersistence), persistentString);
+ }
+
+ switch (response.Status) {
+ case AuthenticationStatus.Authenticated:
+ this.OnLoggedIn(response);
+ break;
+ case AuthenticationStatus.Canceled:
+ this.OnCanceled(response);
+ break;
+ case AuthenticationStatus.Failed:
+ this.OnFailed(response);
+ break;
+ case AuthenticationStatus.SetupRequired:
+ case AuthenticationStatus.ExtensionsOnly:
+ default:
+ // The NotApplicable (extension-only assertion) is NOT one that we support
+ // in this control because that scenario is primarily interesting to RPs
+ // that are asking a specific OP, and it is not user-initiated as this textbox
+ // is designed for.
+ throw new InvalidOperationException(MessagingStrings.UnexpectedMessageReceivedOfMany);
}
}
@@ -719,8 +727,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <returns>The instantiated relying party.</returns>
protected virtual OpenIdRelyingParty CreateRelyingParty() {
+ return this.CreateRelyingParty(true);
+ }
+
+ /// <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>
+ /// <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 = new OpenIdRelyingParty(store);
+ var rp = verifySignature ? new OpenIdRelyingParty(store) : OpenIdRelyingParty.CreateNonVerifying();
// Only set RequireSsl to true, as we don't want to override
// a .config setting of true with false.
@@ -781,6 +801,21 @@ 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>
@@ -853,21 +888,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Wires the popup window to close itself and pass the authentication result to the parent window.
- /// </summary>
- private void ScriptClosingPopup() {
- StringBuilder startupScript = new StringBuilder();
- startupScript.AppendLine("window.opener.dnoa_internal.processAuthorizationResult(document.URL + '&" + UIPopupCallbackParentKey + "=1');");
- 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>
/// Tries to preset the <see cref="Identifier"/> property based on a persistent
/// cookie on the browser.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js
index 3a17b7b..4496445 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js
@@ -25,11 +25,6 @@ if (window.dnoa_internal === undefined) {
window.dnoa_internal = new Object();
};
-// The possible authentication results
-window.dnoa_internal.authSuccess = new Object();
-window.dnoa_internal.authRefused = new Object();
-window.dnoa_internal.timedOut = new Object();
-
/// <summary>Instantiates an object that provides string manipulation services for URIs.</summary>
window.dnoa_internal.Uri = function(url) {
this.originalUri = url.toString();
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
index 48e51dd..2dca459 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
@@ -277,7 +277,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Bindable(true), DefaultValue(""), Category(AppearanceCategory)]
[Description("The content of the text box.")]
public string Text {
- get { return this.Identifier; }
+ get { return this.Identifier.OriginalString; }
set { this.Identifier = value; }
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshot.cs
index 3fd7d20..04a403c 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshot.cs
@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------
-// <copyright file="AuthenticationResponseSnapshot.cs" company="Andrew Arnott">
+// <copyright file="PositiveAuthenticationResponseSnapshot.cs" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
@@ -16,17 +16,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// A serializable snapshot of a verified authentication message.
/// </summary>
[Serializable]
- internal class AuthenticationResponseSnapshot : IAuthenticationResponse {
+ internal class PositiveAuthenticationResponseSnapshot : IAuthenticationResponse {
/// <summary>
/// The callback arguments that came with the authentication response.
/// </summary>
private IDictionary<string, string> callbackArguments;
/// <summary>
- /// Initializes a new instance of the <see cref="AuthenticationResponseSnapshot"/> class.
+ /// Initializes a new instance of the <see cref="PositiveAuthenticationResponseSnapshot"/> class.
/// </summary>
/// <param name="copyFrom">The authentication response to copy from.</param>
- internal AuthenticationResponseSnapshot(IAuthenticationResponse copyFrom) {
+ internal PositiveAuthenticationResponseSnapshot(IAuthenticationResponse copyFrom) {
ErrorUtilities.VerifyArgumentNotNull(copyFrom, "copyFrom");
this.ClaimedIdentifier = copyFrom.ClaimedIdentifier;