summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.OpenId.Provider.UI
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOpenAuth.OpenId.Provider.UI')
-rw-r--r--src/DotNetOpenAuth.OpenId.Provider.UI/DotNetOpenAuth.OpenId.Provider.UI.csproj53
-rw-r--r--src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpoint.cs236
-rw-r--r--src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpointNormalizationEventArgs.cs64
-rw-r--r--src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs267
-rw-r--r--src/DotNetOpenAuth.OpenId.Provider.UI/Properties/AssemblyInfo.cs54
5 files changed, 674 insertions, 0 deletions
diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/DotNetOpenAuth.OpenId.Provider.UI.csproj b/src/DotNetOpenAuth.OpenId.Provider.UI/DotNetOpenAuth.OpenId.Provider.UI.csproj
new file mode 100644
index 0000000..150afd3
--- /dev/null
+++ b/src/DotNetOpenAuth.OpenId.Provider.UI/DotNetOpenAuth.OpenId.Provider.UI.csproj
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.props))\EnlistmentInfo.props" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.props))' != '' " />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ </PropertyGroup>
+ <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.props" />
+ <PropertyGroup>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{9D0F8866-2131-4C2A-BC0E-16FEA5B50828}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>DotNetOpenAuth</RootNamespace>
+ <AssemblyName>DotNetOpenAuth.OpenId.Provider.UI</AssemblyName>
+ </PropertyGroup>
+ <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.Product.props" />
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="OpenId\Provider\IdentityEndpoint.cs" />
+ <Compile Include="OpenId\Provider\IdentityEndpointNormalizationEventArgs.cs" />
+ <Compile Include="OpenId\Provider\ProviderEndpoint.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\DotNetOpenAuth.Messaging\DotNetOpenAuth.Messaging.csproj">
+ <Project>{60426312-6AE5-4835-8667-37EDEA670222}</Project>
+ <Name>DotNetOpenAuth.Messaging</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\DotNetOpenAuth.OpenId.Provider\DotNetOpenAuth.OpenId.Provider.csproj">
+ <Project>{F8284738-3B5D-4733-A511-38C23F4A763F}</Project>
+ <Name>DotNetOpenAuth.OpenId.Provider</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\DotNetOpenAuth.OpenId\DotNetOpenAuth.OpenId.csproj">
+ <Project>{3896A32A-E876-4C23-B9B8-78E17D134CD3}</Project>
+ <Name>DotNetOpenAuth.OpenId</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Org.Mentalis.Security.Cryptography\Org.Mentalis.Security.Cryptography.csproj">
+ <Project>{26DC877F-5987-48DD-9DDB-E62F2DE0E150}</Project>
+ <Name>Org.Mentalis.Security.Cryptography</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ </ItemGroup>
+ <ItemGroup />
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" />
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " />
+</Project> \ No newline at end of file
diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpoint.cs b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpoint.cs
new file mode 100644
index 0000000..3a18b70
--- /dev/null
+++ b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpoint.cs
@@ -0,0 +1,236 @@
+//-----------------------------------------------------------------------
+// <copyright file="IdentityEndpoint.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using System;
+ using System.ComponentModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Drawing.Design;
+ using System.Web.UI;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// An ASP.NET control that manages the OpenID identity advertising tags
+ /// of a user's Identity Page that allow a relying party web site to discover
+ /// how to authenticate a user.
+ /// </summary>
+ [DefaultProperty("ServerUrl")]
+ [ToolboxData("<{0}:IdentityEndpoint runat=\"server\" ProviderEndpointUrl=\"\" />")]
+ public class IdentityEndpoint : XrdsPublisher {
+ #region Property viewstate keys
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="AutoNormalizeRequest"/> property.
+ /// </summary>
+ private const string AutoNormalizeRequestViewStateKey = "AutoNormalizeRequest";
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="ProviderLocalIdentifier"/> property.
+ /// </summary>
+ private const string ProviderLocalIdentifierViewStateKey = "ProviderLocalIdentifier";
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="ProviderVersion"/> property.
+ /// </summary>
+ private const string ProviderVersionViewStateKey = "ProviderVersion";
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="ProviderEndpointUrl"/> property.
+ /// </summary>
+ private const string ProviderEndpointUrlViewStateKey = "ProviderEndpointUrl";
+
+ #endregion
+
+ /// <summary>
+ /// The default value for the <see cref="ProviderVersion"/> property.
+ /// </summary>
+ private const ProtocolVersion ProviderVersionDefault = ProtocolVersion.V20;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IdentityEndpoint"/> class.
+ /// </summary>
+ public IdentityEndpoint() {
+ }
+
+ /// <summary>
+ /// Fired at each page request so the host web site can return the normalized
+ /// version of the request URI.
+ /// </summary>
+ public event EventHandler<IdentityEndpointNormalizationEventArgs> NormalizeUri;
+
+ #region Properties
+
+ /// <summary>
+ /// Gets or sets the OpenID version supported by the provider.
+ /// If multiple versions are supported, this should be set to the latest
+ /// version that this library and the Provider both support.
+ /// </summary>
+ [Category("Behavior")]
+ [DefaultValue(ProviderVersionDefault)]
+ [Description("The OpenID version supported by the provider.")]
+ public ProtocolVersion ProviderVersion {
+ get {
+ return this.ViewState[ProviderVersionViewStateKey] == null ?
+ ProviderVersionDefault : (ProtocolVersion)this.ViewState[ProviderVersionViewStateKey];
+ }
+
+ set {
+ this.ViewState[ProviderVersionViewStateKey] = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the Provider URL that processes OpenID requests.
+ /// </summary>
+ [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Forms designer property grid only supports primitive types.")]
+ [Bindable(true), Category("Behavior")]
+ [Description("The Provider URL that processes OpenID requests.")]
+ [UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
+ public string ProviderEndpointUrl {
+ get {
+ return (string)ViewState[ProviderEndpointUrlViewStateKey];
+ }
+
+ set {
+ UriUtil.ValidateResolvableUrl(Page, DesignMode, value);
+ ViewState[ProviderEndpointUrlViewStateKey] = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the Identifier that is controlled by the Provider.
+ /// </summary>
+ [Bindable(true)]
+ [Category("Behavior")]
+ [Description("The user Identifier that is controlled by the Provider.")]
+ public string ProviderLocalIdentifier {
+ get {
+ return (string)ViewState[ProviderLocalIdentifierViewStateKey];
+ }
+
+ set {
+ UriUtil.ValidateResolvableUrl(Page, DesignMode, value);
+ ViewState[ProviderLocalIdentifierViewStateKey] = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether every incoming request
+ /// will be checked for normalized form and redirected if it is not.
+ /// </summary>
+ /// <remarks>
+ /// <para>If set to true (and it should be), you should also handle the <see cref="NormalizeUri"/>
+ /// event and apply your own policy for normalizing the URI.</para>
+ /// If multiple <see cref="IdentityEndpoint"/> controls are on a single page (to support
+ /// multiple versions of OpenID for example) then only one of them should have this
+ /// property set to true.
+ /// </remarks>
+ [Bindable(true)]
+ [Category("Behavior")]
+ [Description("Whether every incoming request will be checked for normalized form and redirected if it is not. If set to true, consider handling the NormalizeUri event.")]
+ public bool AutoNormalizeRequest {
+ get { return (bool)(ViewState[AutoNormalizeRequestViewStateKey] ?? false); }
+ set { ViewState[AutoNormalizeRequestViewStateKey] = value; }
+ }
+ #endregion
+
+ /// <summary>
+ /// Gets the protocol to use for advertising OpenID on the identity page.
+ /// </summary>
+ internal Protocol Protocol {
+ get { return Protocol.Lookup(this.ProviderVersion); }
+ }
+
+ /// <summary>
+ /// Checks the incoming request and invokes a browser redirect if the URL has not been normalized.
+ /// </summary>
+ /// <seealso cref="IdentityEndpointNormalizationEventArgs.NormalizedIdentifier"/>
+ protected virtual void OnNormalize() {
+ UriIdentifier userSuppliedIdentifier = MessagingUtilities.GetRequestUrlFromContext();
+ var normalizationArgs = new IdentityEndpointNormalizationEventArgs(userSuppliedIdentifier);
+
+ var normalizeUri = this.NormalizeUri;
+ if (normalizeUri != null) {
+ normalizeUri(this, normalizationArgs);
+ } else {
+ // Do some best-guess normalization.
+ normalizationArgs.NormalizedIdentifier = BestGuessNormalization(normalizationArgs.UserSuppliedIdentifier);
+ }
+
+ // If we have a normalized form, we should use it.
+ // We compare path and query with case sensitivity and host name without case sensitivity deliberately,
+ // and the fragment will be asserted or cleared by the OP during authentication.
+ if (normalizationArgs.NormalizedIdentifier != null &&
+ (!String.Equals(normalizationArgs.NormalizedIdentifier.Host, normalizationArgs.UserSuppliedIdentifier.Host, StringComparison.OrdinalIgnoreCase) ||
+ !String.Equals(normalizationArgs.NormalizedIdentifier.PathAndQuery, normalizationArgs.UserSuppliedIdentifier.PathAndQuery, StringComparison.Ordinal))) {
+ Page.Response.Redirect(normalizationArgs.NormalizedIdentifier.AbsoluteUri);
+ }
+ }
+
+ /// <summary>
+ /// Checks the incoming request and invokes a browser redirect if the URL has not been normalized.
+ /// </summary>
+ /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ protected override void OnLoad(EventArgs e) {
+ // Perform URL normalization BEFORE calling base.OnLoad, to keep
+ // our base XrdsPublisher from over-eagerly responding with an XRDS
+ // document before we've redirected.
+ if (this.AutoNormalizeRequest && !this.Page.IsPostBack) {
+ this.OnNormalize();
+ }
+
+ base.OnLoad(e);
+ }
+
+ /// <summary>
+ /// Renders OpenID identity tags.
+ /// </summary>
+ /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param>
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")]
+ protected override void Render(HtmlTextWriter writer) {
+ Uri requestUrlBeforeRewrites = MessagingUtilities.GetRequestUrlFromContext();
+ base.Render(writer);
+ if (!string.IsNullOrEmpty(this.ProviderEndpointUrl)) {
+ writer.WriteBeginTag("link");
+ writer.WriteAttribute("rel", this.Protocol.HtmlDiscoveryProviderKey);
+ writer.WriteAttribute("href", new Uri(requestUrlBeforeRewrites, this.Page.ResolveUrl(this.ProviderEndpointUrl)).AbsoluteUri);
+ writer.Write(">");
+ writer.WriteEndTag("link");
+ writer.WriteLine();
+ }
+ if (!string.IsNullOrEmpty(this.ProviderLocalIdentifier)) {
+ writer.WriteBeginTag("link");
+ writer.WriteAttribute("rel", Protocol.HtmlDiscoveryLocalIdKey);
+ writer.WriteAttribute("href", new Uri(requestUrlBeforeRewrites, this.Page.ResolveUrl(this.ProviderLocalIdentifier)).AbsoluteUri);
+ writer.Write(">");
+ writer.WriteEndTag("link");
+ writer.WriteLine();
+ }
+ }
+
+ /// <summary>
+ /// Normalizes the URL by making the path and query lowercase, and trimming trailing slashes.
+ /// </summary>
+ /// <param name="uri">The URI to normalize.</param>
+ /// <returns>The normalized URI.</returns>
+ [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "FxCop is probably right, but we've been lowercasing host names for normalization elsewhere in the project for a long time now.")]
+ private static Uri BestGuessNormalization(Uri uri) {
+ UriBuilder uriBuilder = new UriBuilder(uri);
+ uriBuilder.Path = uriBuilder.Path.ToLowerInvariant();
+
+ // Ensure no trailing slash unless it is the only element of the path.
+ if (uriBuilder.Path != "/") {
+ uriBuilder.Path = uriBuilder.Path.TrimEnd('/');
+ }
+
+ // We trim the ? from the start of the query when we reset it because
+ // the UriBuilder.Query setter automatically prepends one, and we don't
+ // want to double them up.
+ uriBuilder.Query = uriBuilder.Query.TrimStart('?').ToLowerInvariant();
+ return uriBuilder.Uri;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpointNormalizationEventArgs.cs b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpointNormalizationEventArgs.cs
new file mode 100644
index 0000000..d190792
--- /dev/null
+++ b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpointNormalizationEventArgs.cs
@@ -0,0 +1,64 @@
+//-----------------------------------------------------------------------
+// <copyright file="IdentityEndpointNormalizationEventArgs.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using System;
+
+ /// <summary>
+ /// The event arguments passed to the <see cref="IdentityEndpoint.NormalizeUri"/> event handler.
+ /// </summary>
+ public class IdentityEndpointNormalizationEventArgs : EventArgs {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IdentityEndpointNormalizationEventArgs"/> class.
+ /// </summary>
+ /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
+ internal IdentityEndpointNormalizationEventArgs(UriIdentifier userSuppliedIdentifier) {
+ this.UserSuppliedIdentifier = userSuppliedIdentifier;
+ }
+
+ /// <summary>
+ /// Gets or sets the portion of the incoming page request URI that is relevant to normalization.
+ /// </summary>
+ /// <remarks>
+ /// This identifier should be used to look up the user whose identity page is being queried.
+ /// It MAY be set in case some clever web server URL rewriting is taking place that ASP.NET
+ /// does not know about but your site does. If this is the case this property should be set
+ /// to whatever the original request URL was.
+ /// </remarks>
+ public Uri UserSuppliedIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the normalized form of the user's identifier, according to the host site's policy.
+ /// </summary>
+ /// <remarks>
+ /// <para>This should be set to some constant value for an individual user.
+ /// For example, if <see cref="UserSuppliedIdentifier"/> indicates that identity page
+ /// for "BOB" is being called up, then the following things should be considered:</para>
+ /// <list>
+ /// <item>Normalize the capitalization of the URL: for example, change http://provider/BOB to
+ /// http://provider/bob.</item>
+ /// <item>Switch to HTTPS is it is offered: change http://provider/bob to https://provider/bob.</item>
+ /// <item>Strip off the query string if it is not part of the canonical identity:
+ /// https://provider/bob?timeofday=now becomes https://provider/bob</item>
+ /// <item>Ensure that any trailing slash is either present or absent consistently. For example,
+ /// change https://provider/bob/ to https://provider/bob.</item>
+ /// </list>
+ /// <para>When this property is set, the <see cref="IdentityEndpoint"/> control compares it to
+ /// the request that actually came in, and redirects the browser to use the normalized identifier
+ /// if necessary.</para>
+ /// <para>Using the normalized identifier in the request is <i>very</i> important as it
+ /// helps the user maintain a consistent identity across sites and across site visits to an individual site.
+ /// For example, without normalizing the URL, Bob might sign into a relying party site as
+ /// http://provider/bob one day and https://provider/bob the next day, and the relying party
+ /// site <i>should</i> interpret Bob as two different people because the URLs are different.
+ /// By normalizing the URL at the Provider's identity page for Bob, whichever URL Bob types in
+ /// from day-to-day gets redirected to a normalized form, so Bob is seen as the same person
+ /// all the time, which is of course what Bob wants.
+ /// </para>
+ /// </remarks>
+ public Uri NormalizedIdentifier { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs
new file mode 100644
index 0000000..90dedc2
--- /dev/null
+++ b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs
@@ -0,0 +1,267 @@
+//-----------------------------------------------------------------------
+// <copyright file="ProviderEndpoint.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Diagnostics.Contracts;
+ using System.Text;
+ using System.Web;
+ using System.Web.UI;
+ using System.Web.UI.WebControls;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// An OpenID Provider control that automatically responds to certain
+ /// automated OpenID messages, and routes authentication requests to
+ /// custom code via an event handler.
+ /// </summary>
+ [DefaultEvent("AuthenticationChallenge")]
+ [ToolboxData("<{0}:ProviderEndpoint runat='server' />")]
+ public class ProviderEndpoint : Control {
+ /// <summary>
+ /// The key used to store the pending authentication request in the ASP.NET session.
+ /// </summary>
+ private const string PendingRequestKey = "pendingRequest";
+
+ /// <summary>
+ /// The default value for the <see cref="Enabled"/> property.
+ /// </summary>
+ private const bool EnabledDefault = true;
+
+ /// <summary>
+ /// The view state key in which to store the value of the <see cref="Enabled"/> property.
+ /// </summary>
+ private const string EnabledViewStateKey = "Enabled";
+
+ /// <summary>
+ /// Backing field for the <see cref="Provider"/> property.
+ /// </summary>
+ private static OpenIdProvider provider;
+
+ /// <summary>
+ /// The lock that must be obtained when initializing the provider field.
+ /// </summary>
+ private static object providerInitializerLock = new object();
+
+ /// <summary>
+ /// Fired when an incoming OpenID request is an authentication challenge
+ /// that must be responded to by the Provider web site according to its
+ /// own user database and policies.
+ /// </summary>
+ public event EventHandler<AuthenticationChallengeEventArgs> AuthenticationChallenge;
+
+ /// <summary>
+ /// Fired when an incoming OpenID message carries extension requests
+ /// but is not regarding any OpenID identifier.
+ /// </summary>
+ public event EventHandler<AnonymousRequestEventArgs> AnonymousRequest;
+
+ /// <summary>
+ /// Gets or sets the <see cref="OpenIdProvider"/> instance to use for all instances of this control.
+ /// </summary>
+ /// <value>The default value is an <see cref="OpenIdProvider"/> instance initialized according to the web.config file.</value>
+ public static OpenIdProvider Provider {
+ get {
+ Contract.Ensures(Contract.Result<OpenIdProvider>() != null);
+ if (provider == null) {
+ lock (providerInitializerLock) {
+ if (provider == null) {
+ provider = CreateProvider();
+ }
+ }
+ }
+
+ return provider;
+ }
+
+ set {
+ Contract.Requires<ArgumentNullException>(value != null);
+ provider = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets an incoming OpenID authentication request that has not yet been responded to.
+ /// </summary>
+ /// <remarks>
+ /// This request is stored in the ASP.NET Session state, so it will survive across
+ /// redirects, postbacks, and transfers. This allows you to authenticate the user
+ /// yourself, and confirm his/her desire to authenticate to the relying party site
+ /// before responding to the relying party's authentication request.
+ /// </remarks>
+ public static IAuthenticationRequest PendingAuthenticationRequest {
+ get {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
+ Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired);
+ Contract.Ensures(Contract.Result<IAuthenticationRequest>() == null || PendingRequest != null);
+ return HttpContext.Current.Session[PendingRequestKey] as IAuthenticationRequest;
+ }
+
+ set {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
+ Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired);
+ HttpContext.Current.Session[PendingRequestKey] = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets an incoming OpenID anonymous request that has not yet been responded to.
+ /// </summary>
+ /// <remarks>
+ /// This request is stored in the ASP.NET Session state, so it will survive across
+ /// redirects, postbacks, and transfers. This allows you to authenticate the user
+ /// yourself, and confirm his/her desire to provide data to the relying party site
+ /// before responding to the relying party's request.
+ /// </remarks>
+ public static IAnonymousRequest PendingAnonymousRequest {
+ get {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
+ Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired);
+ Contract.Ensures(Contract.Result<IAnonymousRequest>() == null || PendingRequest != null);
+ return HttpContext.Current.Session[PendingRequestKey] as IAnonymousRequest;
+ }
+
+ set {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
+ Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired);
+ HttpContext.Current.Session[PendingRequestKey] = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets an incoming OpenID request that has not yet been responded to.
+ /// </summary>
+ /// <remarks>
+ /// This request is stored in the ASP.NET Session state, so it will survive across
+ /// redirects, postbacks, and transfers. This allows you to authenticate the user
+ /// yourself, and confirm his/her desire to provide data to the relying party site
+ /// before responding to the relying party's request.
+ /// </remarks>
+ public static IHostProcessedRequest PendingRequest {
+ get {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
+ Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired);
+ return HttpContext.Current.Session[PendingRequestKey] as IHostProcessedRequest;
+ }
+
+ set {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
+ Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired);
+ HttpContext.Current.Session[PendingRequestKey] = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether or not this control should
+ /// be listening for and responding to incoming OpenID requests.
+ /// </summary>
+ [Category("Behavior"), DefaultValue(EnabledDefault)]
+ public bool Enabled {
+ get {
+ return ViewState[EnabledViewStateKey] == null ?
+ EnabledDefault : (bool)ViewState[EnabledViewStateKey];
+ }
+
+ set {
+ ViewState[EnabledViewStateKey] = value;
+ }
+ }
+
+ /// <summary>
+ /// Sends the response for the <see cref="PendingAuthenticationRequest"/> and clears the property.
+ /// </summary>
+ public static void SendResponse() {
+ var pendingRequest = PendingRequest;
+ PendingRequest = null;
+ Provider.Respond(pendingRequest);
+ }
+
+ /// <summary>
+ /// Checks for incoming OpenID requests, responds to ones it can
+ /// respond to without policy checks, and fires events for custom
+ /// handling of the ones it cannot decide on automatically.
+ /// </summary>
+ /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ protected override void OnLoad(EventArgs e) {
+ base.OnLoad(e);
+
+ // There is the unusual scenario that this control is hosted by
+ // an ASP.NET web page that has other UI on it to that the user
+ // might see, including controls that cause a postback to occur.
+ // We definitely want to ignore postbacks, since any openid messages
+ // they contain will be old.
+ if (this.Enabled && !this.Page.IsPostBack) {
+ // Use the explicitly given state store on this control if there is one.
+ // Then try the configuration file specified one. Finally, use the default
+ // in-memory one that's built into OpenIdProvider.
+ // determine what incoming message was received
+ IRequest request = Provider.GetRequest();
+ if (request != null) {
+ PendingRequest = null;
+
+ // process the incoming message appropriately and send the response
+ IAuthenticationRequest idrequest;
+ IAnonymousRequest anonRequest;
+ if ((idrequest = request as IAuthenticationRequest) != null) {
+ PendingAuthenticationRequest = idrequest;
+ this.OnAuthenticationChallenge(idrequest);
+ } else if ((anonRequest = request as IAnonymousRequest) != null) {
+ PendingAnonymousRequest = anonRequest;
+ if (!this.OnAnonymousRequest(anonRequest)) {
+ // This is a feature not supported by the OP, so
+ // go ahead and set disapproved so we can send a response.
+ Logger.OpenId.Warn("An incoming anonymous OpenID request message was detected, but the ProviderEndpoint.AnonymousRequest event is not handled, so returning cancellation message to relying party.");
+ anonRequest.IsApproved = false;
+ }
+ }
+ if (request.IsResponseReady) {
+ Provider.Respond(request);
+ PendingAuthenticationRequest = null;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Fires the <see cref="AuthenticationChallenge"/> event.
+ /// </summary>
+ /// <param name="request">The request to include in the event args.</param>
+ protected virtual void OnAuthenticationChallenge(IAuthenticationRequest request) {
+ var authenticationChallenge = this.AuthenticationChallenge;
+ if (authenticationChallenge != null) {
+ authenticationChallenge(this, new AuthenticationChallengeEventArgs(request));
+ }
+ }
+
+ /// <summary>
+ /// Fires the <see cref="AnonymousRequest"/> event.
+ /// </summary>
+ /// <param name="request">The request to include in the event args.</param>
+ /// <returns><c>true</c> if there were any anonymous request handlers.</returns>
+ protected virtual bool OnAnonymousRequest(IAnonymousRequest request) {
+ var anonymousRequest = this.AnonymousRequest;
+ if (anonymousRequest != null) {
+ anonymousRequest(this, new AnonymousRequestEventArgs(request));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Creates the default OpenIdProvider to use.
+ /// </summary>
+ /// <returns>The new instance of OpenIdProvider.</returns>
+ private static OpenIdProvider CreateProvider() {
+ Contract.Ensures(Contract.Result<OpenIdProvider>() != null);
+ return new OpenIdProvider(OpenIdElement.Configuration.Provider.ApplicationStore.CreateInstance(OpenIdProvider.HttpApplicationStore));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.OpenId.Provider.UI/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..e5cab0f
--- /dev/null
+++ b/src/DotNetOpenAuth.OpenId.Provider.UI/Properties/AssemblyInfo.cs
@@ -0,0 +1,54 @@
+//-----------------------------------------------------------------------
+// <copyright file="AssemblyInfo.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+// We DON'T put an AssemblyVersionAttribute in here because it is generated in the build.
+
+using System;
+using System.Diagnostics.Contracts;
+using System.Net;
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Security.Permissions;
+using System.Web.UI;
+
+[assembly: TagPrefix("DotNetOpenAuth.OpenId.Provider", "op")]
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DotNetOpenAuth OpenID")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("DotNetOpenAuth")]
+[assembly: AssemblyCopyright("Copyright © 2008")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: NeutralResourcesLanguage("en-US")]
+[assembly: CLSCompliant(true)]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("7d73990c-47c0-4256-9f20-a893add9e289")]
+
+[assembly: ContractVerification(true)]
+
+#if StrongNameSigned
+// See comment at top of this file. We need this so that strong-naming doesn't
+// keep this assembly from being useful to shared host (medium trust) web sites.
+[assembly: AllowPartiallyTrustedCallers]
+
+[assembly: InternalsVisibleTo("DotNetOpenAuth.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")]
+#else
+[assembly: InternalsVisibleTo("DotNetOpenAuth.Test")]
+#endif