diff options
Diffstat (limited to 'src')
4 files changed, 162 insertions, 1 deletions
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 9197cee..02e093a 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -353,6 +353,7 @@ <Compile Include="OpenId\Extensions\SimpleRegistration\Constants.cs" /> <Compile Include="OpenId\Extensions\SimpleRegistration\DemandLevel.cs" /> <Compile Include="OpenId\Extensions\SimpleRegistration\Gender.cs" /> + <Compile Include="OpenId\Extensions\UI\UIUtilities.cs" /> <Compile Include="OpenId\Extensions\UI\UIModes.cs" /> <Compile Include="OpenId\Extensions\UI\UIRequest.cs" /> <Compile Include="OpenId\Identifier.cs" /> @@ -428,6 +429,7 @@ <Compile Include="OpenId\RelyingParty\OpenIdLogin.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdMobileTextBox.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdTextBox.cs" /> + <Compile Include="OpenId\RelyingParty\PopupBehavior.cs" /> <Compile Include="OpenId\RelyingParty\PositiveAnonymousResponse.cs" /> <Compile Include="OpenId\RelyingParty\PositiveAuthenticationResponse.cs" /> <Compile Include="OpenId\RelyingParty\AuthenticationStatus.cs" /> diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIUtilities.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIUtilities.cs new file mode 100644 index 0000000..ff6ec7f --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIUtilities.cs @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------- +// <copyright file="UIUtilities.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.Extensions.UI { + using System; + using System.Diagnostics.Contracts; + using System.Globalization; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.RelyingParty; + + /// <summary> + /// Constants used in implementing support for the UI extension. + /// </summary> + public static class UIUtilities { + /// <summary> + /// The required width of the popup window the relying party creates for the provider. + /// </summary> + public const int PopupWidth = 450; + + /// <summary> + /// The required height of the popup window the relying party creates for the provider. + /// </summary> + public const int PopupHeight = 500; + + /// <summary> + /// Gets the <c>window.open</c> javascript snippet to use to open a popup window + /// compliant with the UI extension. + /// </summary> + /// <param name="relyingParty">The relying party.</param> + /// <param name="request">The authentication request to place in the window.</param> + /// <param name="windowName">The name to assign to the popup window.</param> + /// <returns>A string starting with 'window.open' and forming just that one method call.</returns> + internal static string GetWindowPopupScript(OpenIdRelyingParty relyingParty, IAuthenticationRequest request, string windowName) { + Contract.Requires(relyingParty != null); + Contract.Requires(request != null); + Contract.Requires(!string.IsNullOrEmpty(windowName)); + + Uri popupUrl = request.RedirectingResponse.GetDirectUriRequest(relyingParty.Channel); + + return string.Format( + CultureInfo.InvariantCulture, + "window.open({0}, {1}, 'status=0,toolbar=0,location=1,resizable=1,scrollbars=1,width={2},height={3}');", + MessagingUtilities.GetSafeJavascriptValue(popupUrl.AbsoluteUri), + MessagingUtilities.GetSafeJavascriptValue(windowName), + PopupWidth, + PopupHeight); + } + } +} diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs index 831c53c..adaecc6 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs @@ -15,9 +15,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; using System.Drawing.Design; using System.Globalization; using System.Net; + using System.Text; using System.Text.RegularExpressions; using System.Web; using System.Web.Security; @@ -26,6 +28,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; + using DotNetOpenAuth.OpenId.Extensions.UI; /// <summary> /// An ASP.NET control that provides a minimal text box that is OpenID-aware. @@ -76,6 +79,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { #region Property viewstate keys /// <summary> + /// The viewstate key to use for the <see cref="Popup"/> property. + /// </summary> + private const string PopupViewStateKey = "Popup"; + + /// <summary> /// The viewstate key to use for the <see cref="RequestEmail"/> property. /// </summary> private const string RequestEmailViewStateKey = "RequestEmail"; @@ -170,6 +178,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { #region Property defaults /// <summary> + /// The default value for the <see cref="Popup"/> property. + /// </summary> + private const PopupBehavior PopupDefault = PopupBehavior.Never; + + /// <summary> /// The default value for the <see cref="Columns"/> property. /// </summary> private const int ColumnsDefault = 40; @@ -277,6 +290,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { #endregion /// <summary> + /// The callback parameter to use for recognizing when the callback is in a popup window. + /// </summary> + private const string UIPopupCallbackKey = "dnoa.uipopup"; + + /// <summary> /// The callback parameter for use with persisting the <see cref="UsePersistentCookie"/> property. /// </summary> private const string UsePersistentCookieCallbackKey = "OpenIdTextBox_UsePersistentCookie"; @@ -330,6 +348,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { public event EventHandler<OpenIdEventArgs> SetupRequired; #endregion + #region IEditableTextControl Members /// <summary> @@ -428,6 +447,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <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> + [Bindable(true), DefaultValue(PopupDefault), Category(BehaviorCategory)] + [Description("When to use a popup window to complete the login experience.")] + public PopupBehavior Popup { + get { return (PopupBehavior)(ViewState[PopupViewStateKey] ?? PopupDefault); } + set { ViewState[PopupViewStateKey] = value; } + } + + /// <summary> /// Gets or sets a value indicating whether stateless mode is used. /// </summary> [Bindable(true), DefaultValue(StatelessDefault), Category(BehaviorCategory)] @@ -906,6 +936,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { this.AddProfileArgs(this.Request); } + if (this.IsPopupAppropriate()) { + // Inform the OP that it will appear in a popup window. + this.Request.AddExtension(new UIRequest()); + } + // Add state that needs to survive across the redirect. if (!this.Stateless) { this.Request.AddCallbackArguments(UsePersistentCookieCallbackKey, this.UsePersistentCookie.ToString(CultureInfo.InvariantCulture)); @@ -931,7 +966,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } if (this.Request != null) { - this.Request.RedirectToProvider(); + if (this.IsPopupAppropriate()) { + this.ScriptPopupWindow(); + } else { + this.Request.RedirectToProvider(); + } } } @@ -982,6 +1021,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { var response = this.RelyingParty.GetResponse(); if (response != null) { + if (response.GetCallbackArgument(UIPopupCallbackKey) == "1") { + // We're in a popup window. We need to close it and pass the + // message back to the parent window for processing. + this.Page.ClientScript.RegisterStartupScript(this.GetType(), "loginPopupClose", "window.close()", true); + + // TODO: we still need to script sending the message back to the parent window! + return; + } + string persistentString = response.GetCallbackArgument(UsePersistentCookieCallbackKey); bool persistentBool; if (persistentString != null && bool.TryParse(persistentString, out persistentBool)) { @@ -1157,5 +1205,33 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } return rp; } + + /// <summary> + /// Detects whether a popup window should be used to show the Provider's UI + /// and applies the UI extension to the request when appropriate. + /// </summary> + /// <returns><c>true</c> if a popup should be used; <c>false</c> otherwise.</returns> + private bool IsPopupAppropriate() { + Contract.Requires(this.Request != null); + + return this.Popup == PopupBehavior.Always || this.Request.Provider.IsExtensionSupported<UIRequest>(); + } + + /// <summary> + /// Wires the return page to immediately display a popup window with the Provider in it. + /// </summary> + private void ScriptPopupWindow() { + Contract.Requires(this.Request != null); + Contract.Requires(this.RelyingParty != null); + + this.Request.AddCallbackArguments(UIPopupCallbackKey, "1"); + + StringBuilder startupScript = new StringBuilder(); + startupScript.AppendFormat( + @"var openidPopup = {0}", + UIUtilities.GetWindowPopupScript(this.RelyingParty, this.Request, "openidPopup")); + + this.Page.ClientScript.RegisterStartupScript(this.GetType(), "loginPopup", startupScript.ToString(), true); + } } } diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PopupBehavior.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PopupBehavior.cs new file mode 100644 index 0000000..e84f4f5 --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PopupBehavior.cs @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------- +// <copyright file="PopupBehavior.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.RelyingParty { + /// <summary> + /// Several ways that the relying party can direct the user to the Provider + /// to complete authentication. + /// </summary> + public enum PopupBehavior { + /// <summary> + /// A full browser window redirect will be used to send the + /// user to the Provider. + /// </summary> + Never, + + /// <summary> + /// A popup window will be used to send the user to the Provider. + /// </summary> + Always, + + /// <summary> + /// A popup window will be used to send the user to the Provider + /// if the Provider advertises support for the popup UI extension; + /// otherwise a standard redirect is used. + /// </summary> + IfProviderSupported, + } +} |