1
//-----------------------------------------------------------------------
2 // <copyright file="OpenIdAjaxTextBox.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
5 //-----------------------------------------------------------------------
7 [assembly
: System
.Web
.UI
.WebResource(DotNetOpenAuth
.OpenId
.RelyingParty
.OpenIdAjaxTextBox
.EmbeddedScriptResourceName
, "text/javascript")]
8 [assembly
: System
.Web
.UI
.WebResource(DotNetOpenAuth
.OpenId
.RelyingParty
.OpenIdAjaxTextBox
.EmbeddedDotNetOpenIdLogoResourceName
, "image/gif")]
9 [assembly
: System
.Web
.UI
.WebResource(DotNetOpenAuth
.OpenId
.RelyingParty
.OpenIdAjaxTextBox
.EmbeddedSpinnerResourceName
, "image/gif")]
10 [assembly
: System
.Web
.UI
.WebResource(DotNetOpenAuth
.OpenId
.RelyingParty
.OpenIdAjaxTextBox
.EmbeddedLoginSuccessResourceName
, "image/png")]
11 [assembly
: System
.Web
.UI
.WebResource(DotNetOpenAuth
.OpenId
.RelyingParty
.OpenIdAjaxTextBox
.EmbeddedLoginFailureResourceName
, "image/png")]
13 #pragma warning disable 0809 // marking inherited, unsupported properties as obsolete to discourage their use
15 namespace DotNetOpenAuth
.OpenId
.RelyingParty
{
17 using System
.Collections
.Generic
;
18 using System
.Collections
.Specialized
;
19 using System
.ComponentModel
;
20 using System
.Diagnostics
;
21 using System
.Diagnostics
.CodeAnalysis
;
22 using System
.Globalization
;
25 using System
.Text
.RegularExpressions
;
28 using System
.Web
.UI
.WebControls
;
29 using DotNetOpenAuth
.Messaging
;
30 using DotNetOpenAuth
.OpenId
.ChannelElements
;
31 using DotNetOpenAuth
.OpenId
.Extensions
;
34 /// An ASP.NET control that provides a minimal text box that is OpenID-aware and uses AJAX for
35 /// a premium login experience.
37 [DefaultProperty("Text"), ValidationProperty("Text")]
38 [ToolboxData("<{0}:OpenIdAjaxTextBox runat=\"server\" />")]
39 public class OpenIdAjaxTextBox
: WebControl
, ICallbackEventHandler
{
41 /// The name of the manifest stream containing the OpenIdAjaxTextBox.js file.
43 internal const string EmbeddedScriptResourceName
= Util
.DefaultNamespace
+ ".OpenId.RelyingParty.OpenIdAjaxTextBox.js";
46 /// The name of the manifest stream containing the dotnetopenid_16x16.gif file.
48 internal const string EmbeddedDotNetOpenIdLogoResourceName
= Util
.DefaultNamespace
+ ".OpenId.RelyingParty.dotnetopenid_16x16.gif";
51 /// The name of the manifest stream containing the spinner.gif file.
53 internal const string EmbeddedSpinnerResourceName
= Util
.DefaultNamespace
+ ".OpenId.RelyingParty.spinner.gif";
56 /// The name of the manifest stream containing the login_success.png file.
58 internal const string EmbeddedLoginSuccessResourceName
= Util
.DefaultNamespace
+ ".OpenId.RelyingParty.login_success.png";
61 /// The name of the manifest stream containing the login_failure.png file.
63 internal const string EmbeddedLoginFailureResourceName
= Util
.DefaultNamespace
+ ".OpenId.RelyingParty.login_failure.png";
65 #region Property viewstate keys
68 /// The viewstate key to use for storing the value of the <see cref="Columns"/> property.
70 private const string ColumnsViewStateKey
= "Columns";
73 /// The viewstate key to use for storing the value of the <see cref="OnClientAssertionReceived"/> property.
75 private const string OnClientAssertionReceivedViewStateKey
= "OnClientAssertionReceived";
78 /// The viewstate key to use for storing the value of the <see cref="AuthenticationResponse"/> property.
80 private const string AuthenticationResponseViewStateKey
= "AuthenticationResponse";
83 /// The viewstate key to use for storing the value of the a successful authentication.
85 private const string AuthDataViewStateKey
= "AuthData";
88 /// The viewstate key to use for storing the value of the <see cref="AuthenticatedAsToolTip"/> property.
90 private const string AuthenticatedAsToolTipViewStateKey
= "AuthenticatedAsToolTip";
93 /// The viewstate key to use for storing the value of the <see cref="AuthenticationSucceededToolTip"/> property.
95 private const string AuthenticationSucceededToolTipViewStateKey
= "AuthenticationSucceededToolTip";
98 /// The viewstate key to use for storing the value of the <see cref="ReturnToUrl"/> property.
100 private const string ReturnToUrlViewStateKey
= "ReturnToUrl";
103 /// The viewstate key to use for storing the value of the <see cref="RealmUrl"/> property.
105 private const string RealmUrlViewStateKey
= "RealmUrl";
108 /// The viewstate key to use for storing the value of the <see cref="LogOnInProgressMessage"/> property.
110 private const string LogOnInProgressMessageViewStateKey
= "BusyToolTip";
113 /// The viewstate key to use for storing the value of the <see cref="AuthenticationFailedToolTip"/> property.
115 private const string AuthenticationFailedToolTipViewStateKey
= "AuthenticationFailedToolTip";
118 /// The viewstate key to use for storing the value of the <see cref="IdentifierRequiredMessage"/> property.
120 private const string IdentifierRequiredMessageViewStateKey
= "BusyToolTip";
123 /// The viewstate key to use for storing the value of the <see cref="BusyToolTip"/> property.
125 private const string BusyToolTipViewStateKey
= "BusyToolTip";
128 /// The viewstate key to use for storing the value of the <see cref="LogOnText"/> property.
130 private const string LogOnTextViewStateKey
= "LoginText";
133 /// The viewstate key to use for storing the value of the <see cref="Throttle"/> property.
135 private const string ThrottleViewStateKey
= "Throttle";
138 /// The viewstate key to use for storing the value of the <see cref="LogOnToolTip"/> property.
140 private const string LogOnToolTipViewStateKey
= "LoginToolTip";
143 /// The viewstate key to use for storing the value of the <see cref="Name"/> property.
145 private const string NameViewStateKey
= "Name";
148 /// The viewstate key to use for storing the value of the <see cref="Timeout"/> property.
150 private const string TimeoutViewStateKey
= "Timeout";
153 /// The viewstate key to use for storing the value of the <see cref="Text"/> property.
155 private const string TextViewStateKey
= "Text";
158 /// The viewstate key to use for storing the value of the <see cref="TabIndex"/> property.
160 private const string TabIndexViewStateKey
= "TabIndex";
163 /// The viewstate key to use for storing the value of the <see cref="RetryToolTip"/> property.
165 private const string RetryToolTipViewStateKey
= "RetryToolTip";
168 /// The viewstate key to use for storing the value of the <see cref="RetryText"/> property.
170 private const string RetryTextViewStateKey
= "RetryText";
174 #region Property defaults
177 /// The default value for the <see cref="Columns"/> property.
179 private const int ColumnsDefault
= 40;
182 /// The default value for the <see cref="ReturnToUrl"/> property.
184 private const string ReturnToUrlDefault
= "";
187 /// The default value for the <see cref="RealmUrl"/> property.
189 private const string RealmUrlDefault
= "~/";
192 /// The default value for the <see cref="LogOnInProgressMessage"/> property.
194 private const string LogOnInProgressMessageDefault
= "Please wait for login to complete.";
197 /// The default value for the <see cref="AuthenticationSucceededToolTip"/> property.
199 private const string AuthenticationSucceededToolTipDefault
= "Authenticated by {0}.";
202 /// The default value for the <see cref="AuthenticatedAsToolTip"/> property.
204 private const string AuthenticatedAsToolTipDefault
= "Authenticated as {0}.";
207 /// The default value for the <see cref="AuthenticationFailedToolTip"/> property.
209 private const string AuthenticationFailedToolTipDefault
= "Authentication failed.";
212 /// The default value for the <see cref="Throttle"/> property.
214 private const int ThrottleDefault
= 3;
217 /// The default value for the <see cref="LogOnText"/> property.
219 private const string LogOnTextDefault
= "LOG IN";
222 /// The default value for the <see cref="BusyToolTip"/> property.
224 private const string BusyToolTipDefault
= "Discovering/authenticating";
227 /// The default value for the <see cref="IdentifierRequiredMessage"/> property.
229 private const string IdentifierRequiredMessageDefault
= "Please correct errors in OpenID identifier and allow login to complete before submitting.";
232 /// The default value for the <see cref="Name"/> property.
234 private const string NameDefault
= "openid_identifier";
237 /// Default value for <see cref="TabIndex"/> property.
239 private const short TabIndexDefault
= 0;
242 /// The default value for the <see cref="RetryToolTip"/> property.
244 private const string RetryToolTipDefault
= "Retry a failed identifier discovery.";
247 /// The default value for the <see cref="LogOnToolTip"/> property.
249 private const string LogOnToolTipDefault
= "Click here to log in using a pop-up window.";
252 /// The default value for the <see cref="RetryText"/> property.
254 private const string RetryTextDefault
= "RETRY";
259 /// Tracks whether the text box should receive input focus when the page is rendered.
261 private bool focusCalled
;
264 /// The authentication response that just came in.
266 private IAuthenticationResponse authenticationResponse
;
269 /// A dictionary of extension response types and the javascript member
270 /// name to map them to on the user agent.
272 private Dictionary
<Type
, string> clientScriptExtensions
= new Dictionary
<Type
, string>();
275 /// Stores the result of an AJAX discovery request while it is waiting
276 /// to be picked up by ASP.NET on the way down to the user agent.
278 private string discoveryResult
;
283 /// Fired when the user has typed in their identifier, discovery was successful
284 /// and a login attempt is about to begin.
286 [Description("Fired when the user has typed in their identifier, discovery was successful and a login attempt is about to begin.")]
287 public event EventHandler
<OpenIdEventArgs
> LoggingIn
;
290 /// Fired when a Provider sends back a positive assertion to this control,
291 /// but the authentication has not yet been verified.
294 /// <b>No security critical decisions should be made within event handlers
295 /// for this event</b> as the authenticity of the assertion has not been
296 /// verified yet. All security related code should go in the event handler
297 /// for the <see cref="LoggedIn"/> event.
299 [Description("Fired when a Provider sends back a positive assertion to this control, but the authentication has not yet been verified.")]
300 public event EventHandler
<OpenIdEventArgs
> UnconfirmedPositiveAssertion
;
303 /// Fired when authentication has completed successfully.
305 [Description("Fired when authentication has completed successfully.")]
306 public event EventHandler
<OpenIdEventArgs
> LoggedIn
;
309 /// Gets or sets the client-side script that executes when an authentication
310 /// assertion is received (but before it is verified).
313 /// <para>In the context of the executing javascript set in this property, the
314 /// local variable <i>sender</i> is set to the openid_identifier input box
315 /// that is executing this code.
316 /// This variable has a getClaimedIdentifier() method that may be used to
317 /// identify the user who is being authenticated.</para>
318 /// <para>It is <b>very</b> important to note that when this code executes,
319 /// the authentication has not been verified and may have been spoofed.
320 /// No security-sensitive operations should take place in this javascript code.
321 /// The authentication is verified on the server by the time the
322 /// <see cref="LoggedIn"/> server-side event fires.</para>
324 [Description("Gets or sets the client-side script that executes when an authentication assertion is received (but before it is verified).")]
325 [Bindable(true), DefaultValue(""), Category("Behavior")]
326 public string OnClientAssertionReceived
{
327 get { return this.ViewState[OnClientAssertionReceivedViewStateKey] as string; }
328 set { this.ViewState[OnClientAssertionReceivedViewStateKey] = value; }
336 /// Gets the completed authentication response.
338 public IAuthenticationResponse AuthenticationResponse
{
340 if (this.authenticationResponse
== null) {
341 // We will either validate a new response and return a live AuthenticationResponse
342 // or we will try to deserialize a previous IAuthenticationResponse (snapshot)
343 // from viewstate and return that.
344 IAuthenticationResponse viewstateResponse
= this.ViewState
[AuthenticationResponseViewStateKey
] as IAuthenticationResponse
;
345 string viewstateAuthData
= this.ViewState
[AuthDataViewStateKey
] as string;
346 string formAuthData
= this.Page
.Request
.Form
[this.OpenIdAuthDataFormKey
];
348 // First see if there is fresh auth data to be processed into a response.
349 if (!string.IsNullOrEmpty(formAuthData
) && !string.Equals(viewstateAuthData
, formAuthData
, StringComparison
.Ordinal
)) {
350 this.ViewState
[AuthDataViewStateKey
] = formAuthData
;
352 Uri authUri
= new Uri(formAuthData
);
353 HttpRequestInfo clientResponseInfo
= new HttpRequestInfo
{
356 var rp
= CreateRelyingParty(true);
357 this.authenticationResponse
= rp
.GetResponse(clientResponseInfo
);
359 // Save out the authentication response to viewstate so we can find it on
360 // a subsequent postback.
361 this.ViewState
[AuthenticationResponseViewStateKey
] = this.authenticationResponse
;
363 this.authenticationResponse
= viewstateResponse
;
366 return this.authenticationResponse
;
371 /// Gets or sets the value in the text field, completely unprocessed or normalized.
373 [Bindable(true), DefaultValue(""), Category("Appearance")]
374 [Description("The value in the text field, completely unprocessed or normalized.")]
376 get { return (string)(this.ViewState[TextViewStateKey] ?? string.Empty); }
377 set { this.ViewState[TextViewStateKey] = value ?? string.Empty; }
381 /// Gets or sets the width of the text box in characters.
383 [Bindable(true), Category("Appearance"), DefaultValue(ColumnsDefault
)]
384 [Description("The width of the text box in characters.")]
387 return (int)(this.ViewState
[ColumnsViewStateKey
] ?? ColumnsDefault
);
391 ErrorUtilities
.VerifyArgumentInRange(value >= 0, "value");
392 this.ViewState
[ColumnsViewStateKey
] = value;
397 /// Gets or sets the tab index of the text box control. Use 0 to omit an explicit tabindex.
399 [Bindable(true), Category("Behavior"), DefaultValue(TabIndexDefault
)]
400 [Description("The tab index of the text box control. Use 0 to omit an explicit tabindex.")]
401 public override short TabIndex
{
402 get { return (short)(this.ViewState[TabIndexViewStateKey] ?? TabIndexDefault); }
403 set { this.ViewState[TabIndexViewStateKey] = value; }
407 /// Gets or sets the HTML name to assign to the text field.
409 [Bindable(true), DefaultValue(NameDefault
), Category("Misc")]
410 [Description("The HTML name to assign to the text field.")]
413 return (string)(this.ViewState
[NameViewStateKey
] ?? NameDefault
);
417 ErrorUtilities
.VerifyNonZeroLength(value, "value");
418 this.ViewState
[NameViewStateKey
] = value ?? string.Empty
;
423 /// Gets or sets the time duration for the AJAX control to wait for an OP to respond before reporting failure to the user.
425 [Browsable(true), DefaultValue(typeof(TimeSpan
), "00:00:01"), Category("Behavior")]
426 [Description("The time duration for the AJAX control to wait for an OP to respond before reporting failure to the user.")]
427 public TimeSpan Timeout
{
429 return (TimeSpan
)(this.ViewState
[TimeoutViewStateKey
] ?? TimeoutDefault
);
433 ErrorUtilities
.VerifyArgumentInRange(value.TotalMilliseconds
> 0, "value");
434 this.ViewState
[TimeoutViewStateKey
] = value;
439 /// Gets or sets the maximum number of OpenID Providers to simultaneously try to authenticate with.
441 [Browsable(true), DefaultValue(ThrottleDefault
), Category("Behavior")]
442 [Description("The maximum number of OpenID Providers to simultaneously try to authenticate with.")]
443 public int Throttle
{
445 return (int)(this.ViewState
[ThrottleViewStateKey
] ?? ThrottleDefault
);
449 ErrorUtilities
.VerifyArgumentInRange(value > 0, "value");
450 this.ViewState
[ThrottleViewStateKey
] = value;
455 /// Gets or sets the text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.
457 [Bindable(true), DefaultValue(LogOnTextDefault
), Localizable(true), Category("Appearance")]
458 [Description("The text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.")]
459 public string LogOnText
{
461 return (string)(this.ViewState
[LogOnTextViewStateKey
] ?? LogOnTextDefault
);
465 ErrorUtilities
.VerifyNonZeroLength(value, "value");
466 this.ViewState
[LogOnTextViewStateKey
] = value ?? string.Empty
;
471 /// Gets or sets the rool tip text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.
473 [Bindable(true), DefaultValue(LogOnToolTipDefault
), Localizable(true), Category("Appearance")]
474 [Description("The tool tip text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.")]
475 public string LogOnToolTip
{
476 get { return (string)(this.ViewState[LogOnToolTipViewStateKey] ?? LogOnToolTipDefault); }
477 set { this.ViewState[LogOnToolTipViewStateKey] = value ?? string.Empty; }
481 /// Gets or sets the text that appears on the RETRY button in cases where authentication times out.
483 [Bindable(true), DefaultValue(RetryTextDefault
), Localizable(true), Category("Appearance")]
484 [Description("The text that appears on the RETRY button in cases where authentication times out.")]
485 public string RetryText
{
487 return (string)(this.ViewState
[RetryTextViewStateKey
] ?? RetryTextDefault
);
491 ErrorUtilities
.VerifyNonZeroLength(value, "value");
492 this.ViewState
[RetryTextViewStateKey
] = value ?? string.Empty
;
497 /// Gets or sets the tool tip text that appears on the RETRY button in cases where authentication times out.
499 [Bindable(true), DefaultValue(RetryToolTipDefault
), Localizable(true), Category("Appearance")]
500 [Description("The tool tip text that appears on the RETRY button in cases where authentication times out.")]
501 public string RetryToolTip
{
502 get { return (string)(this.ViewState[RetryToolTipViewStateKey] ?? RetryToolTipDefault); }
503 set { this.ViewState[RetryToolTipViewStateKey] = value ?? string.Empty; }
507 /// Gets or sets the tool tip text that appears when authentication succeeds.
509 [Bindable(true), DefaultValue(AuthenticationSucceededToolTipDefault
), Localizable(true), Category("Appearance")]
510 [Description("The tool tip text that appears when authentication succeeds.")]
511 public string AuthenticationSucceededToolTip
{
512 get { return (string)(this.ViewState[AuthenticationSucceededToolTipViewStateKey] ?? AuthenticationSucceededToolTipDefault); }
513 set { this.ViewState[AuthenticationSucceededToolTipViewStateKey] = value ?? string.Empty; }
517 /// Gets or sets the tool tip text that appears on the green checkmark when authentication succeeds.
519 [Bindable(true), DefaultValue(AuthenticatedAsToolTipDefault
), Localizable(true), Category("Appearance")]
520 [Description("The tool tip text that appears on the green checkmark when authentication succeeds.")]
521 public string AuthenticatedAsToolTip
{
522 get { return (string)(this.ViewState[AuthenticatedAsToolTipViewStateKey] ?? AuthenticatedAsToolTipDefault); }
523 set { this.ViewState[AuthenticatedAsToolTipViewStateKey] = value ?? string.Empty; }
527 /// Gets or sets the tool tip text that appears when authentication fails.
529 [Bindable(true), DefaultValue(AuthenticationFailedToolTipDefault
), Localizable(true), Category("Appearance")]
530 [Description("The tool tip text that appears when authentication fails.")]
531 public string AuthenticationFailedToolTip
{
532 get { return (string)(this.ViewState[AuthenticationFailedToolTipViewStateKey] ?? AuthenticationFailedToolTipDefault); }
533 set { this.ViewState[AuthenticationFailedToolTipViewStateKey] = value ?? string.Empty; }
537 /// Gets or sets the tool tip text that appears over the text box when it is discovering and authenticating.
539 [Bindable(true), DefaultValue(BusyToolTipDefault
), Localizable(true), Category("Appearance")]
540 [Description("The tool tip text that appears over the text box when it is discovering and authenticating.")]
541 public string BusyToolTip
{
542 get { return (string)(this.ViewState[BusyToolTipViewStateKey] ?? BusyToolTipDefault); }
543 set { this.ViewState[BusyToolTipViewStateKey] = value ?? string.Empty; }
547 /// Gets or sets the message that is displayed if a postback is about to occur before the identifier has been supplied.
549 [Bindable(true), DefaultValue(IdentifierRequiredMessageDefault
), Localizable(true), Category("Appearance")]
550 [Description("The message that is displayed if a postback is about to occur before the identifier has been supplied.")]
551 public string IdentifierRequiredMessage
{
552 get { return (string)(this.ViewState[IdentifierRequiredMessageViewStateKey] ?? IdentifierRequiredMessageDefault); }
553 set { this.ViewState[IdentifierRequiredMessageViewStateKey] = value ?? string.Empty; }
557 /// Gets or sets the message that is displayed if a postback is attempted while login is in process.
559 [Bindable(true), DefaultValue(LogOnInProgressMessageDefault
), Localizable(true), Category("Appearance")]
560 [Description("The message that is displayed if a postback is attempted while login is in process.")]
561 public string LogOnInProgressMessage
{
562 get { return (string)(this.ViewState[LogOnInProgressMessageViewStateKey] ?? LogOnInProgressMessageDefault); }
563 set { this.ViewState[LogOnInProgressMessageViewStateKey] = value ?? string.Empty; }
567 /// Gets or sets the OpenID <see cref="Realm"/> of the relying party web site.
569 [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId
= "System.Uri", Justification
= "Using Uri.ctor for validation.")]
570 [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId
= "DotNetOpenId.Realm", Justification
= "Using ctor for validation.")]
571 [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification
= "Property grid on form designer only supports primitive types.")]
572 [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification
= "Property grid on form designer only supports primitive types.")]
574 [Category("Behavior")]
575 [DefaultValue(RealmUrlDefault
)]
576 [Description("The OpenID Realm of the relying party web site.")]
577 public string RealmUrl
{
579 return (string)(this.ViewState
[RealmUrlViewStateKey
] ?? RealmUrlDefault
);
583 if (Page
!= null && !DesignMode
) {
584 // Validate new value by trying to construct a Realm object based on it.
585 new Realm(OpenIdUtilities
.GetResolvedRealm(Page
, value)); // throws an exception on failure.
587 // We can't fully test it, but it should start with either ~/ or a protocol.
588 if (Regex
.IsMatch(value, @"^https?://")) {
589 new Uri(value.Replace("*.", "")); // make sure it's fully-qualified, but ignore wildcards
590 } else if (value.StartsWith("~/", StringComparison
.Ordinal
)) {
593 throw new UriFormatException();
596 this.ViewState
[RealmUrlViewStateKey
] = value;
601 /// Gets or sets the OpenID ReturnTo of the relying party web site.
603 [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId
= "System.Uri", Justification
= "Using Uri.ctor for validation.")]
604 [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification
= "Property grid on form designer only supports primitive types.")]
605 [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification
= "Property grid on form designer only supports primitive types.")]
607 [Category("Behavior")]
608 [DefaultValue(ReturnToUrlDefault
)]
609 [Description("The OpenID ReturnTo of the relying party web site.")]
610 public string ReturnToUrl
{
612 return (string)(this.ViewState
[ReturnToUrlViewStateKey
] ?? ReturnToUrlDefault
);
616 if (Page
!= null && !DesignMode
) {
617 // Validate new value by trying to construct a Uri based on it.
618 new Uri(MessagingUtilities
.GetRequestUrlFromContext(), Page
.ResolveUrl(value)); // throws an exception on failure.
620 // We can't fully test it, but it should start with either ~/ or a protocol.
621 if (Regex
.IsMatch(value, @"^https?://")) {
622 new Uri(value); // make sure it's fully-qualified, but ignore wildcards
623 } else if (value.StartsWith("~/", StringComparison
.Ordinal
)) {
626 throw new UriFormatException();
629 this.ViewState
[ReturnToUrlViewStateKey
] = value;
635 #region Properties to hide
638 /// Gets or sets the foreground color (typically the color of the text) of the Web server control.
641 /// 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"/>.
643 [Obsolete
, Browsable(false), Bindable(false)]
644 public override System
.Drawing
.Color ForeColor
{
645 get { throw new NotSupportedException(); }
646 set { throw new NotSupportedException(); }
650 /// Gets or sets the background color of the Web server control.
653 /// 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.
655 [Obsolete
, Browsable(false), Bindable(false)]
656 public override System
.Drawing
.Color BackColor
{
657 get { throw new NotSupportedException(); }
658 set { throw new NotSupportedException(); }
662 /// Gets or sets the border color of the Web control.
665 /// 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.
667 [Obsolete
, Browsable(false), Bindable(false)]
668 public override System
.Drawing
.Color BorderColor
{
669 get { throw new NotSupportedException(); }
670 set { throw new NotSupportedException(); }
674 /// Gets or sets the border width of the Web server control.
677 /// 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.
679 /// <exception cref="T:System.ArgumentException">
680 /// The specified border width is a negative value.
682 [Obsolete
, Browsable(false), Bindable(false)]
683 public override Unit BorderWidth
{
684 get { return Unit.Empty; }
685 set { throw new NotSupportedException(); }
689 /// Gets or sets the border style of the Web server control.
692 /// One of the <see cref="T:System.Web.UI.WebControls.BorderStyle"/> enumeration values. The default is NotSet.
694 [Obsolete
, Browsable(false), Bindable(false)]
695 public override BorderStyle BorderStyle
{
696 get { return BorderStyle.None; }
697 set { throw new NotSupportedException(); }
701 /// Gets the font properties associated with the Web server control.
704 /// A <see cref="T:System.Web.UI.WebControls.FontInfo"/> that represents the font properties of the Web server control.
706 [Obsolete
, Browsable(false), Bindable(false)]
707 public override FontInfo Font
{
712 /// Gets or sets the height of the Web server control.
715 /// 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"/>.
717 /// <exception cref="T:System.ArgumentException">
718 /// The height was set to a negative value.
720 [Obsolete
, Browsable(false), Bindable(false)]
721 public override Unit Height
{
722 get { return Unit.Empty; }
723 set { throw new NotSupportedException(); }
727 /// Gets or sets the width of the Web server control.
730 /// 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"/>.
732 /// <exception cref="T:System.ArgumentException">
733 /// The width of the Web server control was set to a negative value.
735 [Obsolete
, Browsable(false), Bindable(false)]
736 public override Unit Width
{
737 get { return Unit.Empty; }
738 set { throw new NotSupportedException(); }
742 /// Gets or sets the text displayed when the mouse pointer hovers over the Web server control.
745 /// The text displayed when the mouse pointer hovers over the Web server control. The default is <see cref="F:System.String.Empty"/>.
747 [Obsolete
, Browsable(false), Bindable(false)]
748 public override string ToolTip
{
749 get { return string.Empty; }
750 set { throw new NotSupportedException(); }
754 /// Gets or sets the skin to apply to the control.
757 /// The name of the skin to apply to the control. The default is <see cref="F:System.String.Empty"/>.
759 /// <exception cref="T:System.ArgumentException">
760 /// The skin specified in the <see cref="P:System.Web.UI.WebControls.WebControl.SkinID"/> property does not exist in the theme.
762 [Obsolete
, Browsable(false), Bindable(false)]
763 public override string SkinID
{
764 get { return string.Empty; }
765 set { throw new NotSupportedException(); }
769 /// Gets or sets a value indicating whether themes apply to this control.
771 /// <returns>true to use themes; otherwise, false. The default is false.
773 [Obsolete
, Browsable(false), Bindable(false)]
774 public override bool EnableTheming
{
775 get { return false; }
776 set { throw new NotSupportedException(); }
782 /// Gets the default value for the <see cref="Timeout"/> property.
784 /// <value>8 seconds; or eternity if the debugger is attached.</value>
785 private static TimeSpan TimeoutDefault
{
787 if (Debugger
.IsAttached
) {
788 Logger
.Warn("Debugger is attached. Inflating default OpenIdAjaxTextbox.Timeout value to infinity.");
789 return TimeSpan
.MaxValue
;
791 return TimeSpan
.FromSeconds(8);
797 /// Gets the name of the open id auth data form key.
799 /// <value>A concatenation of <see cref="Name"/> and <c>"_openidAuthData"</c>.</value>
800 private string OpenIdAuthDataFormKey
{
801 get { return this.Name + "_openidAuthData"; }
805 /// Places focus on the text box when the page is rendered on the browser.
807 public override void Focus() {
808 // we don't emit the code to focus the control immediately, in case the control
809 // is never rendered to the page because its Visible property is false or that
810 // of any of its parent containers.
811 this.focusCalled
= true;
815 /// Allows an OpenID extension to read data out of an unverified positive authentication assertion
816 /// and send it down to the client browser so that Javascript running on the page can perform
817 /// some preprocessing on the extension data.
819 /// <typeparam name="T">The extension <i>response</i> type that will read data from the assertion.</typeparam>
820 /// <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>
822 /// This method should be called from the <see cref="UnconfirmedPositiveAssertion"/> event handler.
824 [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification
= "By design")]
825 public void RegisterClientScriptExtension
<T
>(string propertyName
) where T
: IClientScriptExtensionResponse
{
826 ErrorUtilities
.VerifyNonZeroLength(propertyName
, "propertyName");
827 ErrorUtilities
.VerifyArgumentNamed(!this.clientScriptExtensions
.ContainsValue(propertyName
), "propertyName", OpenIdStrings
.ClientScriptExtensionPropertyNameCollision
, propertyName
);
828 foreach (var ext
in this.clientScriptExtensions
.Keys
) {
829 ErrorUtilities
.VerifyArgument(ext
!= typeof(T
), OpenIdStrings
.ClientScriptExtensionTypeCollision
, typeof(T
).FullName
);
831 this.clientScriptExtensions
.Add(typeof(T
), propertyName
);
834 #region ICallbackEventHandler Members
837 /// Returns the result of discovery on some Identifier passed to <see cref="ICallbackEventHandler.RaiseCallbackEvent"/>.
839 /// <returns>The result of the callback.</returns>
840 /// <value>A whitespace delimited list of URLs that can be used to initiate authentication.</value>
841 string ICallbackEventHandler
.GetCallbackResult() {
842 this.Page
.Response
.ContentType
= "text/javascript";
843 return this.discoveryResult
;
847 /// Performs discovery on some OpenID Identifier. Called directly from the user agent via
848 /// AJAX callback mechanisms.
850 /// <param name="eventArgument">The identifier to perform discovery on.</param>
851 void ICallbackEventHandler
.RaiseCallbackEvent(string eventArgument
) {
852 string userSuppliedIdentifier
= eventArgument
;
854 ErrorUtilities
.VerifyNonZeroLength(userSuppliedIdentifier
, "userSuppliedIdentifier");
855 Logger
.InfoFormat("AJAX discovery on {0} requested.", userSuppliedIdentifier
);
857 // We prepare a JSON object with this interface:
858 // class jsonResponse {
859 // string claimedIdentifier;
860 // Array requests; // never null
861 // string error; // null if no error
863 // Each element in the requests array looks like this:
864 // class jsonAuthRequest {
865 // string endpoint; // URL to the OP endpoint
866 // string immediate; // URL to initiate an immediate request
867 // string setup; // URL to initiate a setup request.
869 StringBuilder discoveryResultBuilder
= new StringBuilder();
870 discoveryResultBuilder
.Append("{");
872 List
<IAuthenticationRequest
> requests
= this.CreateRequests(userSuppliedIdentifier
, true);
873 if (requests
.Count
> 0) {
874 discoveryResultBuilder
.AppendFormat("claimedIdentifier: {0},", Util
.GetSafeJavascriptValue(requests
[0].ClaimedIdentifier
));
875 discoveryResultBuilder
.Append("requests: [");
876 foreach (IAuthenticationRequest request
in requests
) {
877 this.OnLoggingIn(request
);
878 discoveryResultBuilder
.Append("{");
879 discoveryResultBuilder
.AppendFormat("endpoint: {0},", Util
.GetSafeJavascriptValue(request
.Provider
.Uri
.AbsoluteUri
));
880 request
.Mode
= AuthenticationRequestMode
.Immediate
;
881 UserAgentResponse response
= request
.RedirectingResponse
;
882 discoveryResultBuilder
.AppendFormat("immediate: {0},", Util
.GetSafeJavascriptValue(response
.DirectUriRequest
.AbsoluteUri
));
883 request
.Mode
= AuthenticationRequestMode
.Setup
;
884 response
= request
.RedirectingResponse
;
885 discoveryResultBuilder
.AppendFormat("setup: {0}", Util
.GetSafeJavascriptValue(response
.DirectUriRequest
.AbsoluteUri
));
886 discoveryResultBuilder
.Append("},");
888 discoveryResultBuilder
.Length
-= 1; // trim off last comma
889 discoveryResultBuilder
.Append("]");
891 discoveryResultBuilder
.Append("requests: new Array(),");
892 discoveryResultBuilder
.AppendFormat("error: {0}", Util
.GetSafeJavascriptValue(OpenIdStrings
.OpenIdEndpointNotFound
));
894 } catch (ProtocolException ex
) {
895 discoveryResultBuilder
.Append("requests: new Array(),");
896 discoveryResultBuilder
.AppendFormat("error: {0}", Util
.GetSafeJavascriptValue(ex
.Message
));
898 discoveryResultBuilder
.Append("}");
899 this.discoveryResult
= discoveryResultBuilder
.ToString();
905 /// Fires the <see cref="LoggingIn"/> event.
907 /// <param name="request">The request.</param>
908 protected virtual void OnLoggingIn(IAuthenticationRequest request
) {
909 var loggingIn
= this.LoggingIn
;
910 if (loggingIn
!= null) {
911 loggingIn(this, new OpenIdEventArgs(request
));
916 /// Fires the <see cref="UnconfirmedPositiveAssertion"/> event.
918 protected virtual void OnUnconfirmedPositiveAssertion() {
919 var unconfirmedPositiveAssertion
= this.UnconfirmedPositiveAssertion
;
920 if (unconfirmedPositiveAssertion
!= null) {
921 unconfirmedPositiveAssertion(this, null);
926 /// Fires the <see cref="LoggedIn"/> event.
928 /// <param name="response">The response.</param>
929 protected virtual void OnLoggedIn(IAuthenticationResponse response
) {
930 var loggedIn
= this.LoggedIn
;
931 if (loggedIn
!= null) {
932 loggedIn(this, new OpenIdEventArgs(response
));
937 /// Prepares the control for loading.
939 /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
940 protected override void OnLoad(EventArgs e
) {
943 if (this.Page
.IsPostBack
) {
944 // If the control was temporarily hidden, it won't be in the Form data,
945 // and we'll just implicitly keep the last Text setting.
946 if (this.Page
.Request
.Form
[this.Name
] != null) {
947 this.Text
= this.Page
.Request
.Form
[this.Name
];
950 // If there is a response, and it is fresh (live object, not a snapshot object)...
951 if (this.AuthenticationResponse
!= null && this.AuthenticationResponse
.Status
== AuthenticationStatus
.Authenticated
) {
952 this.OnLoggedIn(this.AuthenticationResponse
);
955 NameValueCollection query
= MessagingUtilities
.GetQueryOrFormFromContext();
956 string userSuppliedIdentifier
= query
["dotnetopenid.userSuppliedIdentifier"];
957 if (!string.IsNullOrEmpty(userSuppliedIdentifier
) && query
["dotnetopenid.phase"] == "2") {
958 this.ReportAuthenticationResult();
964 /// Prepares to render the control.
966 /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
967 protected override void OnPreRender(EventArgs e
) {
970 this.PrepareClientJavascript();
974 /// Renders the control.
976 /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the control content.</param>
977 protected override void Render(System
.Web
.UI
.HtmlTextWriter writer
) {
978 // We surround the textbox with a span so that the .js file can inject a
979 // login button within the text box with easy placement.
980 writer
.WriteBeginTag("span");
981 writer
.WriteAttribute("class", this.CssClass
);
982 writer
.Write(" style='");
983 writer
.WriteStyleAttribute("position", "relative");
984 writer
.WriteStyleAttribute("font-size", "16px");
987 writer
.WriteBeginTag("input");
988 writer
.WriteAttribute("name", this.Name
);
989 writer
.WriteAttribute("id", this.ClientID
);
990 writer
.WriteAttribute("value", this.Text
, true);
991 writer
.WriteAttribute("size", this.Columns
.ToString(CultureInfo
.InvariantCulture
));
992 if (this.TabIndex
> 0) {
993 writer
.WriteAttribute("tabindex", this.TabIndex
.ToString(CultureInfo
.InvariantCulture
));
996 writer
.WriteAttribute("disabled", "true");
998 if (!string.IsNullOrEmpty(this.CssClass
)) {
999 writer
.WriteAttribute("class", this.CssClass
);
1001 writer
.Write(" style='");
1002 writer
.WriteStyleAttribute("padding-left", "18px");
1003 writer
.WriteStyleAttribute("border-style", "solid");
1004 writer
.WriteStyleAttribute("border-width", "1px");
1005 writer
.WriteStyleAttribute("border-color", "lightgray");
1007 writer
.Write(" />");
1009 writer
.WriteEndTag("span");
1011 // Emit a hidden field to let the javascript on the user agent know if an
1012 // authentication has already successfully taken place.
1013 string viewstateAuthData
= this.ViewState
[AuthDataViewStateKey
] as string;
1014 if (!string.IsNullOrEmpty(viewstateAuthData
)) {
1015 writer
.WriteBeginTag("input");
1016 writer
.WriteAttribute("type", "hidden");
1017 writer
.WriteAttribute("name", this.OpenIdAuthDataFormKey
);
1018 writer
.WriteAttribute("value", viewstateAuthData
, true);
1019 writer
.Write(" />");
1024 /// Filters a sequence of OP endpoints so that an OP hostname only appears once in the list.
1026 /// <param name="requests">The authentication requests against those OP endpoints.</param>
1027 /// <returns>The filtered list.</returns>
1028 private static List
<IAuthenticationRequest
> RemoveDuplicateEndpoints(List
<IAuthenticationRequest
> requests
) {
1029 var filteredRequests
= new List
<IAuthenticationRequest
>(requests
.Count
);
1030 foreach (IAuthenticationRequest request
in requests
) {
1031 // We'll distinguish based on the host name only, which
1032 // admittedly is only a heuristic, but if we remove one that really wasn't a duplicate, well,
1033 // this multiple OP attempt thing was just a convenience feature anyway.
1034 if (!filteredRequests
.Any(req
=> string.Equals(req
.Provider
.Uri
.Host
, request
.Provider
.Uri
.Host
, StringComparison
.OrdinalIgnoreCase
))) {
1035 filteredRequests
.Add(request
);
1039 return filteredRequests
;
1043 /// Creates the relying party.
1045 /// <param name="verifySignature">
1046 /// A value indicating whether message protections should be applied to the processed messages.
1047 /// Use <c>false</c> to postpone verification to a later time without invalidating nonces.
1049 /// <returns>The newly instantiated relying party.</returns>
1050 private static OpenIdRelyingParty
CreateRelyingParty(bool verifySignature
) {
1051 return verifySignature
? new OpenIdRelyingParty() : OpenIdRelyingParty
.CreateNonVerifying();
1055 /// Invokes a method on a parent frame/window's OpenIdAjaxTextBox,
1056 /// and closes the calling popup window if applicable.
1058 /// <param name="methodCall">The method to call on the OpenIdAjaxTextBox, including
1059 /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param>
1060 private void CallbackUserAgentMethod(string methodCall
) {
1061 this.CallbackUserAgentMethod(methodCall
, null);
1065 /// Invokes a method on a parent frame/window's OpenIdAjaxTextBox,
1066 /// and closes the calling popup window if applicable.
1068 /// <param name="methodCall">The method to call on the OpenIdAjaxTextBox, including
1069 /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param>
1070 /// <param name="preAssignments">An optional list of assignments to make to the input box object before placing the method call.</param>
1071 private void CallbackUserAgentMethod(string methodCall
, string[] preAssignments
) {
1072 Logger
.InfoFormat("Sending Javascript callback: {0}", methodCall
);
1073 Page
.Response
.Write(@"<html><body><script language='javascript'>
1074 var inPopup = !window.frameElement;
1075 var objSrc = inPopup ? window.opener.waiting_openidBox : window.frameElement.openidBox;
1077 if (preAssignments
!= null) {
1078 foreach (string assignment
in preAssignments
) {
1079 Page
.Response
.Write(string.Format(CultureInfo
.InvariantCulture
, " objSrc.{0};\n", assignment
));
1083 // Something about calling objSrc.{0} can somehow cause FireFox to forget about the inPopup variable,
1084 // so we have to actually put the test for it ABOVE the call to objSrc.{0} so that it already
1085 // whether to call window.self.close() after the call.
1086 string htmlFormat
= @" if (inPopup) {{
1088 window.self.close();
1092 </script></body></html>";
1093 Page
.Response
.Write(string.Format(CultureInfo
.InvariantCulture
, htmlFormat
, methodCall
));
1094 Page
.Response
.End();
1098 /// Assembles the javascript to send to the client and registers it with ASP.NET for transmission.
1100 private void PrepareClientJavascript() {
1101 string identifierParameterName
= "identifier";
1102 string discoveryCallbackResultParameterName
= "resultFunction";
1103 string discoveryErrorCallbackParameterName
= "errorCallback";
1104 string discoveryCallback
= Page
.ClientScript
.GetCallbackEventReference(
1106 identifierParameterName
,
1107 discoveryCallbackResultParameterName
,
1108 identifierParameterName
,
1109 discoveryErrorCallbackParameterName
,
1112 // Import the .js file where most of the code is.
1113 this.Page
.ClientScript
.RegisterClientScriptResource(typeof(OpenIdAjaxTextBox
), EmbeddedScriptResourceName
);
1115 // Call into the .js file with initialization information.
1116 StringBuilder startupScript
= new StringBuilder();
1117 startupScript
.AppendLine("<script language='javascript'>");
1118 startupScript
.AppendFormat("var box = document.getElementsByName('{0}')[0];{1}", this.Name
, Environment
.NewLine
);
1119 if (this.focusCalled
) {
1120 startupScript
.AppendLine("box.focus();");
1122 startupScript
.AppendFormat(
1123 CultureInfo
.InvariantCulture
,
1124 "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}",
1125 Util
.GetSafeJavascriptValue(this.Page
.ClientScript
.GetWebResourceUrl(this.GetType(), OpenIdTextBox
.EmbeddedLogoResourceName
)),
1126 Util
.GetSafeJavascriptValue(this.Page
.ClientScript
.GetWebResourceUrl(this.GetType(), EmbeddedDotNetOpenIdLogoResourceName
)),
1127 Util
.GetSafeJavascriptValue(this.Page
.ClientScript
.GetWebResourceUrl(this.GetType(), EmbeddedSpinnerResourceName
)),
1128 Util
.GetSafeJavascriptValue(this.Page
.ClientScript
.GetWebResourceUrl(this.GetType(), EmbeddedLoginSuccessResourceName
)),
1129 Util
.GetSafeJavascriptValue(this.Page
.ClientScript
.GetWebResourceUrl(this.GetType(), EmbeddedLoginFailureResourceName
)),
1131 this.Timeout
.TotalMilliseconds
,
1132 string.IsNullOrEmpty(this.OnClientAssertionReceived
) ? "null" : "'" + this.OnClientAssertionReceived
.Replace(@"\", @"\\").Replace("'", @"\'") + "'",
1133 Util
.GetSafeJavascriptValue(this.LogOnText
),
1134 Util
.GetSafeJavascriptValue(this.LogOnToolTip
),
1135 Util
.GetSafeJavascriptValue(this.RetryText
),
1136 Util
.GetSafeJavascriptValue(this.RetryToolTip
),
1137 Util
.GetSafeJavascriptValue(this.BusyToolTip
),
1138 Util
.GetSafeJavascriptValue(this.IdentifierRequiredMessage
),
1139 Util
.GetSafeJavascriptValue(this.LogOnInProgressMessage
),
1140 Util
.GetSafeJavascriptValue(this.AuthenticationSucceededToolTip
),
1141 Util
.GetSafeJavascriptValue(this.AuthenticatedAsToolTip
),
1142 Util
.GetSafeJavascriptValue(this.AuthenticationFailedToolTip
),
1143 identifierParameterName
,
1144 discoveryCallbackResultParameterName
,
1145 discoveryErrorCallbackParameterName
,
1147 Environment
.NewLine
);
1149 startupScript
.AppendLine("</script>");
1151 Page
.ClientScript
.RegisterStartupScript(this.GetType(), "ajaxstartup", startupScript
.ToString());
1152 string htmlFormat
= @"
1153 var openidbox = document.getElementsByName('{0}')[0];
1154 if (!openidbox.dnoi_internal.onSubmit()) {{ return false; }}
1156 Page
.ClientScript
.RegisterOnSubmitStatement(
1159 string.Format(CultureInfo
.InvariantCulture
, htmlFormat
, this.Name
));
1163 /// Creates the authentication requests for a given user-supplied Identifier.
1165 /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
1166 /// <param name="immediate">A value indicating whether the authentication
1167 /// requests should be initialized for use in invisible iframes for background authentication.</param>
1168 /// <returns>The list of authentication requests, any one of which may be
1169 /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.</returns>
1170 private List
<IAuthenticationRequest
> CreateRequests(string userSuppliedIdentifier
, bool immediate
) {
1171 var requests
= new List
<IAuthenticationRequest
>();
1173 OpenIdRelyingParty rp
= CreateRelyingParty(true);
1175 // Resolve the trust root, and swap out the scheme and port if necessary to match the
1176 // return_to URL, since this match is required by OpenId, and the consumer app
1177 // may be using HTTP at some times and HTTPS at others.
1178 UriBuilder realm
= OpenIdUtilities
.GetResolvedRealm(this.Page
, this.RealmUrl
);
1179 realm
.Scheme
= Page
.Request
.Url
.Scheme
;
1180 realm
.Port
= Page
.Request
.Url
.Port
;
1182 // Initiate openid request
1183 // We use TryParse here to avoid throwing an exception which
1184 // might slip through our validator control if it is disabled.
1185 Realm typedRealm
= new Realm(realm
);
1186 if (string.IsNullOrEmpty(this.ReturnToUrl
)) {
1187 requests
.AddRange(rp
.CreateRequests(userSuppliedIdentifier
, typedRealm
));
1189 Uri returnTo
= new Uri(MessagingUtilities
.GetRequestUrlFromContext(), this.ReturnToUrl
);
1190 requests
.AddRange(rp
.CreateRequests(userSuppliedIdentifier
, typedRealm
, returnTo
));
1193 // Some OPs may be listed multiple times (one with HTTPS and the other with HTTP, for example).
1194 // Since we're gathering OPs to try one after the other, just take the first choice of each OP
1195 // and don't try it multiple times.
1196 requests
= RemoveDuplicateEndpoints(requests
);
1198 // Configure each generated request.
1200 foreach (var req
in requests
) {
1201 req
.AddCallbackArguments("index", (reqIndex
++).ToString(CultureInfo
.InvariantCulture
));
1203 // If the ReturnToUrl was explicitly set, we'll need to reset our first parameter
1204 if (string.IsNullOrEmpty(HttpUtility
.ParseQueryString(req
.ReturnToUrl
.Query
)["dotnetopenid.userSuppliedIdentifier"])) {
1205 req
.AddCallbackArguments("dotnetopenid.userSuppliedIdentifier", userSuppliedIdentifier
);
1208 // Our javascript needs to let the user know which endpoint responded. So we force it here.
1209 // This gives us the info even for 1.0 OPs and 2.0 setup_required responses.
1210 req
.AddCallbackArguments("dotnetopenid.op_endpoint", req
.Provider
.Uri
.AbsoluteUri
);
1211 req
.AddCallbackArguments("dotnetopenid.claimed_id", req
.ClaimedIdentifier
);
1212 req
.AddCallbackArguments("dotnetopenid.phase", "2");
1214 req
.Mode
= AuthenticationRequestMode
.Immediate
;
1215 ((AuthenticationRequest
)req
).AssociationPreference
= AssociationPreference
.IfAlreadyEstablished
;
1223 /// Notifies the user agent via an AJAX response of a completed authentication attempt.
1225 private void ReportAuthenticationResult() {
1226 Logger
.InfoFormat("AJAX (iframe) callback from OP: {0}", this.Page
.Request
.Url
);
1227 List
<string> assignments
= new List
<string>();
1229 OpenIdRelyingParty rp
= CreateRelyingParty(false);
1230 var f
= HttpUtility
.ParseQueryString(this.Page
.Request
.Url
.Query
).ToDictionary();
1231 var authResponse
= rp
.GetResponse();
1232 if (authResponse
.Status
== AuthenticationStatus
.Authenticated
) {
1233 this.OnUnconfirmedPositiveAssertion();
1234 foreach (var pair
in this.clientScriptExtensions
) {
1235 IClientScriptExtensionResponse extension
= (IClientScriptExtensionResponse
)authResponse
.GetExtension(pair
.Key
);
1236 var positiveResponse
= (PositiveAuthenticationResponse
)authResponse
;
1237 string js
= extension
.InitializeJavaScriptData(positiveResponse
.Response
);
1238 if (string.IsNullOrEmpty(js
)) {
1241 assignments
.Add(pair
.Value
+ " = " + js
);
1245 this.CallbackUserAgentMethod("dnoi_internal.processAuthorizationResult(document.URL)", assignments
.ToArray());