summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj6
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs9
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.resx3
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs74
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AnonymousRequestEventArgs.cs32
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs88
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs131
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IAnonymousRequest.cs25
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IAuthenticationRequest.cs32
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs45
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs5
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs76
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs26
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationStatus.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs24
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs273
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs223
21 files changed, 765 insertions, 332 deletions
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index a6069b4..1f3df12 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -368,10 +368,15 @@
<Compile Include="OpenId\Messages\SignedResponseRequest.cs" />
<Compile Include="OpenId\NoDiscoveryIdentifier.cs" />
<Compile Include="OpenId\OpenIdUtilities.cs" />
+ <Compile Include="OpenId\Provider\AnonymousRequest.cs" />
+ <Compile Include="OpenId\Provider\AnonymousRequestEventArgs.cs" />
<Compile Include="OpenId\Provider\AuthenticationChallengeEventArgs.cs" />
<Compile Include="OpenId\Provider\AuthenticationRequest.cs" />
<Compile Include="OpenId\Provider\AutoResponsiveRequest.cs" />
+ <Compile Include="OpenId\Provider\HostProcessedRequest.cs" />
+ <Compile Include="OpenId\Provider\IAnonymousRequest.cs" />
<Compile Include="OpenId\Provider\IAuthenticationRequest.cs" />
+ <Compile Include="OpenId\Provider\IHostProcessedRequest.cs" />
<Compile Include="OpenId\Provider\IdentityEndpoint.cs" />
<Compile Include="OpenId\Provider\IdentityEndpointNormalizationEventArgs.cs" />
<Compile Include="OpenId\Provider\IErrorReporting.cs" />
@@ -418,6 +423,7 @@
<Compile Include="OpenId\RelyingParty\OpenIdLogin.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdMobileTextBox.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdTextBox.cs" />
+ <Compile Include="OpenId\RelyingParty\PositiveAnonymousResponse.cs" />
<Compile Include="OpenId\RelyingParty\PositiveAuthenticationResponse.cs" />
<Compile Include="OpenId\RelyingParty\AuthenticationStatus.cs" />
<Compile Include="OpenId\RelyingParty\FailedAuthenticationResponse.cs" />
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs
index c8ffae8..31a2da5 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs
@@ -57,6 +57,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
if (fields.ContainsKey(protocol.openid.identity)) {
message = new CheckIdRequest(protocol.Version, recipient.Location, authMode);
} else {
+ ErrorUtilities.VerifyProtocol(!fields.ContainsKey(protocol.openid.claimed_id), OpenIdStrings.IdentityAndClaimedIdentifierMustBeBothPresentOrAbsent);
message = new SignedResponseRequest(protocol.Version, recipient.Location, authMode);
}
} else if (string.Equals(mode, protocol.Args.Mode.cancel) ||
@@ -66,6 +67,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
if (fields.ContainsKey(protocol.openid.identity)) {
message = new PositiveAssertionResponse(protocol.Version, recipient.Location);
} else {
+ ErrorUtilities.VerifyProtocol(!fields.ContainsKey(protocol.openid.claimed_id), OpenIdStrings.IdentityAndClaimedIdentifierMustBeBothPresentOrAbsent);
message = new IndirectSignedResponse(protocol.Version, recipient.Location);
}
} else if (string.Equals(mode, protocol.Args.Mode.check_authentication)) {
diff --git a/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs
index f03422c..99a7c5a 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs
@@ -31,12 +31,13 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// </summary>
/// <param name="request">The request that the relying party sent.</param>
/// <param name="channel">The channel to use to simulate construction of the user_setup_url, if applicable. May be null, but the user_setup_url will not be constructed.</param>
- internal NegativeAssertionResponse(CheckIdRequest request, Channel channel)
+ internal NegativeAssertionResponse(SignedResponseRequest request, Channel channel)
: base(request, GetMode(request)) {
// If appropriate, and when we're provided with a channel to do it,
// go ahead and construct the user_setup_url
if (this.Version.Major < 2 && request.Immediate && channel != null) {
- this.UserSetupUrl = ConstructUserSetupUrl(request, channel);
+ // All requests are CheckIdRequests in OpenID 1.x, so this cast should be safe.
+ this.UserSetupUrl = ConstructUserSetupUrl((CheckIdRequest)request, channel);
}
}
@@ -130,7 +131,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// </summary>
/// <param name="request">The request that we're responding to.</param>
/// <returns>The value of the openid.mode parameter to use.</returns>
- private static string GetMode(CheckIdRequest request) {
+ private static string GetMode(SignedResponseRequest request) {
ErrorUtilities.VerifyArgumentNotNull(request, "request");
Protocol protocol = Protocol.Lookup(request.Version);
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
index a6e02fe..033ea54 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
@@ -259,6 +259,15 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Looks up a localized string similar to The openid.identity and openid.claimed_id parameters must either be both present or both absent from the message..
+ /// </summary>
+ internal static string IdentityAndClaimedIdentifierMustBeBothPresentOrAbsent {
+ get {
+ return ResourceManager.GetString("IdentityAndClaimedIdentifierMustBeBothPresentOrAbsent", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The Provider requested association type &apos;{0}&apos; and session type &apos;{1}&apos;, which are not compatible with each other..
/// </summary>
internal static string IncompatibleAssociationAndSessionTypes {
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
index 95fe655..2b934f5 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
@@ -301,4 +301,7 @@ Discovered endpoint info:
<data name="UnsupportedChannelConfiguration" xml:space="preserve">
<value>This feature is unavailable due to an unrecognized channel configuration.</value>
</data>
+ <data name="IdentityAndClaimedIdentifierMustBeBothPresentOrAbsent" xml:space="preserve">
+ <value>The openid.identity and openid.claimed_id parameters must either be both present or both absent from the message.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs
new file mode 100644
index 0000000..546db9f
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs
@@ -0,0 +1,74 @@
+//-----------------------------------------------------------------------
+// <copyright file="AnonymousRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// Provides access to a host Provider to read an incoming extension-only checkid request message,
+ /// and supply extension responses or a cancellation message to the RP.
+ /// </summary>
+ internal class AnonymousRequest : HostProcessedRequest, IAnonymousRequest {
+ /// <summary>
+ /// The extension-response message to send, if the host site chooses to send it.
+ /// </summary>
+ private readonly IndirectSignedResponse positiveResponse;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AnonymousRequest"/> class.
+ /// </summary>
+ /// <param name="provider">The provider that received the request.</param>
+ /// <param name="request">The incoming authentication request message.</param>
+ internal AnonymousRequest(OpenIdProvider provider, SignedResponseRequest request)
+ : base(provider, request) {
+ Contract.Requires(provider != null);
+ Contract.Requires(!(request is CheckIdRequest), "Instantiate " + typeof(AuthenticationRequest).Name + " to handle this kind of message.");
+ ErrorUtilities.VerifyInternal(!(request is CheckIdRequest), "Instantiate {0} to handle this kind of message.", typeof(AuthenticationRequest).Name);
+
+ this.positiveResponse = new IndirectSignedResponse(request);
+ }
+
+ #region IAnonymousRequest Members
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the user approved sending any data to the relying party.
+ /// </summary>
+ /// <value><c>true</c> if approved; otherwise, <c>false</c>.</value>
+ public bool? IsApproved { get; set; }
+
+ #endregion
+
+ #region Request members
+
+ /// <summary>
+ /// Gets a value indicating whether the response is ready to be sent to the user agent.
+ /// </summary>
+ /// <remarks>
+ /// This property returns false if there are properties that must be set on this
+ /// request instance before the response can be sent.
+ /// </remarks>
+ public override bool IsResponseReady {
+ get { return this.IsApproved.HasValue; }
+ }
+
+ /// <summary>
+ /// Gets the response message, once <see cref="IsResponseReady"/> is <c>true</c>.
+ /// </summary>
+ protected override IProtocolMessage ResponseMessage {
+ get {
+ if (this.IsApproved.HasValue) {
+ return this.IsApproved.Value ? (IProtocolMessage)this.positiveResponse : this.NegativeResponse;
+ } else {
+ return null;
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequestEventArgs.cs b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequestEventArgs.cs
new file mode 100644
index 0000000..cdd5311
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequestEventArgs.cs
@@ -0,0 +1,32 @@
+//-----------------------------------------------------------------------
+// <copyright file="AnonymousRequestEventArgs.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using System;
+ using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The event arguments that include details of the incoming request.
+ /// </summary>
+ public class AnonymousRequestEventArgs : EventArgs {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AnonymousRequestEventArgs"/> class.
+ /// </summary>
+ /// <param name="request">The incoming OpenID request.</param>
+ internal AnonymousRequestEventArgs(IAnonymousRequest request) {
+ Contract.Requires(request != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ this.Request = request;
+ }
+
+ /// <summary>
+ /// Gets the incoming OpenID request.
+ /// </summary>
+ public IAnonymousRequest Request { get; private set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
index 34ade49..7a547dd 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
@@ -6,10 +6,7 @@
namespace DotNetOpenAuth.OpenId.Provider {
using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Text;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
@@ -19,28 +16,23 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// requests.
/// </summary>
[Serializable]
- internal class AuthenticationRequest : Request, IAuthenticationRequest {
+ internal class AuthenticationRequest : HostProcessedRequest, IAuthenticationRequest {
/// <summary>
/// The positive assertion to send, if the host site chooses to send it.
/// </summary>
private readonly PositiveAssertionResponse positiveResponse;
/// <summary>
- /// The negative assertion to send, if the host site chooses to send it.
- /// </summary>
- private readonly NegativeAssertionResponse negativeResponse;
-
- /// <summary>
/// Initializes a new instance of the <see cref="AuthenticationRequest"/> class.
/// </summary>
/// <param name="provider">The provider that received the request.</param>
/// <param name="request">The incoming authentication request message.</param>
internal AuthenticationRequest(OpenIdProvider provider, CheckIdRequest request)
- : base(request) {
+ : base(provider, request) {
+ Contract.Requires(provider != null);
ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
this.positiveResponse = new PositiveAssertionResponse(request);
- this.negativeResponse = new NegativeAssertionResponse(request, provider.Channel);
if (this.ClaimedIdentifier == Protocol.ClaimedIdentifierForOPIdentifier &&
Protocol.ClaimedIdentifierForOPIdentifier != null) {
@@ -71,29 +63,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
#region IAuthenticationRequest Properties
/// <summary>
- /// Gets the version of OpenID being used by the relying party that sent the request.
- /// </summary>
- public ProtocolVersion RelyingPartyVersion {
- get { return Protocol.Lookup(this.RequestMessage.Version).ProtocolVersion; }
- }
-
- /// <summary>
- /// Gets a value indicating whether the consumer demands an immediate response.
- /// If false, the consumer is willing to wait for the identity provider
- /// to authenticate the user.
- /// </summary>
- public bool Immediate {
- get { return this.RequestMessage.Immediate; }
- }
-
- /// <summary>
- /// Gets the URL the consumer site claims to use as its 'base' address.
- /// </summary>
- public Realm Realm {
- get { return this.RequestMessage.Realm; }
- }
-
- /// <summary>
/// Gets a value indicating whether the Provider should help the user
/// select a Claimed Identifier to send back to the relying party.
/// </summary>
@@ -197,7 +166,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
protected override IProtocolMessage ResponseMessage {
get {
if (this.IsAuthenticated.HasValue) {
- return this.IsAuthenticated.Value ? (IProtocolMessage)this.positiveResponse : this.negativeResponse;
+ return this.IsAuthenticated.Value ? (IProtocolMessage)this.positiveResponse : this.NegativeResponse;
} else {
return null;
}
@@ -231,53 +200,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
this.positiveResponse.ClaimedIdentifier = builder.Uri;
}
- /// <summary>
- /// Gets a value indicating whether verification of the return URL claimed by the Relying Party
- /// succeeded.
- /// </summary>
- /// <param name="requestHandler">The request handler to use to perform relying party discovery.</param>
- /// <returns>
- /// <c>true</c> if the Relying Party passed discovery verification; <c>false</c> otherwise.
- /// </returns>
- /// <remarks>
- /// Return URL verification is only attempted if this property is queried.
- /// The result of the verification is cached per request so calling this
- /// property getter multiple times in one request is not a performance hit.
- /// See OpenID Authentication 2.0 spec section 9.2.1.
- /// </remarks>
- public bool IsReturnUrlDiscoverable(IDirectWebRequestHandler requestHandler) {
- ErrorUtilities.VerifyArgumentNotNull(requestHandler, "requestHandler");
-
- ErrorUtilities.VerifyInternal(this.Realm != null, "Realm should have been read or derived by now.");
- try {
- foreach (var returnUrl in Realm.Discover(requestHandler, false)) {
- Realm discoveredReturnToUrl = returnUrl.ReturnToEndpoint;
-
- // The spec requires that the return_to URLs given in an RPs XRDS doc
- // do not contain wildcards.
- if (discoveredReturnToUrl.DomainWildcard) {
- Logger.Yadis.WarnFormat("Realm {0} contained return_to URL {1} which contains a wildcard, which is not allowed.", Realm, discoveredReturnToUrl);
- continue;
- }
-
- // Use the same rules as return_to/realm matching to check whether this
- // URL fits the return_to URL we were given.
- if (discoveredReturnToUrl.Contains(this.RequestMessage.ReturnTo)) {
- // no need to keep looking after we find a match
- return true;
- }
- }
- } catch (ProtocolException ex) {
- // Don't do anything else. We quietly fail at return_to verification and return false.
- Logger.Yadis.InfoFormat("Relying party discovery at URL {0} failed. {1}", Realm, ex);
- } catch (WebException ex) {
- // Don't do anything else. We quietly fail at return_to verification and return false.
- Logger.Yadis.InfoFormat("Relying party discovery at URL {0} failed. {1}", Realm, ex);
- }
-
- return false;
- }
-
#endregion
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
new file mode 100644
index 0000000..f4f42a4
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
@@ -0,0 +1,131 @@
+//-----------------------------------------------------------------------
+// <copyright file="HostProcessedRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// A base class from which identity and non-identity RP requests can derive.
+ /// </summary>
+ internal abstract class HostProcessedRequest : Request, IHostProcessedRequest {
+ /// <summary>
+ /// The negative assertion to send, if the host site chooses to send it.
+ /// </summary>
+ private readonly NegativeAssertionResponse negativeResponse;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HostProcessedRequest"/> class.
+ /// </summary>
+ /// <param name="provider">The provider that received the request.</param>
+ /// <param name="request">The incoming request message.</param>
+ protected HostProcessedRequest(OpenIdProvider provider, SignedResponseRequest request)
+ : base(request) {
+ Contract.Requires(provider != null);
+
+ this.negativeResponse = new NegativeAssertionResponse(request, provider.Channel);
+ }
+
+ #region IHostProcessedRequest Properties
+
+ /// <summary>
+ /// Gets the version of OpenID being used by the relying party that sent the request.
+ /// </summary>
+ public ProtocolVersion RelyingPartyVersion {
+ get { return Protocol.Lookup(this.RequestMessage.Version).ProtocolVersion; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the consumer demands an immediate response.
+ /// If false, the consumer is willing to wait for the identity provider
+ /// to authenticate the user.
+ /// </summary>
+ public bool Immediate {
+ get { return this.RequestMessage.Immediate; }
+ }
+
+ /// <summary>
+ /// Gets the URL the consumer site claims to use as its 'base' address.
+ /// </summary>
+ public Realm Realm {
+ get { return this.RequestMessage.Realm; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the negative response.
+ /// </summary>
+ protected NegativeAssertionResponse NegativeResponse {
+ get { return this.negativeResponse; }
+ }
+
+ /// <summary>
+ /// Gets the original request message.
+ /// </summary>
+ /// <value>This may be null in the case of an unrecognizable message.</value>
+ protected new SignedResponseRequest RequestMessage {
+ get { return (SignedResponseRequest)base.RequestMessage; }
+ }
+
+ #region IHostProcessedRequest Methods
+
+ /// <summary>
+ /// Gets a value indicating whether verification of the return URL claimed by the Relying Party
+ /// succeeded.
+ /// </summary>
+ /// <param name="requestHandler">The request handler to use to perform relying party discovery.</param>
+ /// <returns>
+ /// <c>true</c> if the Relying Party passed discovery verification; <c>false</c> otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Return URL verification is only attempted if this property is queried.
+ /// The result of the verification is cached per request so calling this
+ /// property getter multiple times in one request is not a performance hit.
+ /// See OpenID Authentication 2.0 spec section 9.2.1.
+ /// </remarks>
+ public bool IsReturnUrlDiscoverable(IDirectWebRequestHandler requestHandler) {
+ ErrorUtilities.VerifyArgumentNotNull(requestHandler, "requestHandler");
+
+ ErrorUtilities.VerifyInternal(this.Realm != null, "Realm should have been read or derived by now.");
+ try {
+ foreach (var returnUrl in Realm.Discover(requestHandler, false)) {
+ Realm discoveredReturnToUrl = returnUrl.ReturnToEndpoint;
+
+ // The spec requires that the return_to URLs given in an RPs XRDS doc
+ // do not contain wildcards.
+ if (discoveredReturnToUrl.DomainWildcard) {
+ Logger.Yadis.WarnFormat("Realm {0} contained return_to URL {1} which contains a wildcard, which is not allowed.", Realm, discoveredReturnToUrl);
+ continue;
+ }
+
+ // Use the same rules as return_to/realm matching to check whether this
+ // URL fits the return_to URL we were given.
+ if (discoveredReturnToUrl.Contains(this.RequestMessage.ReturnTo)) {
+ // no need to keep looking after we find a match
+ return true;
+ }
+ }
+ } catch (ProtocolException ex) {
+ // Don't do anything else. We quietly fail at return_to verification and return false.
+ Logger.Yadis.InfoFormat("Relying party discovery at URL {0} failed. {1}", Realm, ex);
+ } catch (WebException ex) {
+ // Don't do anything else. We quietly fail at return_to verification and return false.
+ Logger.Yadis.InfoFormat("Relying party discovery at URL {0} failed. {1}", Realm, ex);
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IAnonymousRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/IAnonymousRequest.cs
new file mode 100644
index 0000000..ec2c175
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Provider/IAnonymousRequest.cs
@@ -0,0 +1,25 @@
+//-----------------------------------------------------------------------
+// <copyright file="IAnonymousRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// Instances of this interface represent incoming extension-only requests.
+ /// This interface provides the details of the request and allows setting
+ /// the response.
+ /// </summary>
+ public interface IAnonymousRequest : IHostProcessedRequest {
+ /// <summary>
+ /// Gets or sets a value indicating whether the user approved sending any data to the relying party.
+ /// </summary>
+ /// <value><c>true</c> if approved; otherwise, <c>false</c>.</value>
+ bool? IsApproved { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IAuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/IAuthenticationRequest.cs
index b1ef269..bb837b5 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/IAuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/IAuthenticationRequest.cs
@@ -15,24 +15,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// This interface provides the details of the request and allows setting
/// the response.
/// </summary>
- public interface IAuthenticationRequest : IRequest {
- /// <summary>
- /// Gets the version of OpenID being used by the relying party that sent the request.
- /// </summary>
- ProtocolVersion RelyingPartyVersion { get; }
-
- /// <summary>
- /// Gets a value indicating whether the consumer demands an immediate response.
- /// If false, the consumer is willing to wait for the identity provider
- /// to authenticate the user.
- /// </summary>
- bool Immediate { get; }
-
- /// <summary>
- /// Gets the URL the consumer site claims to use as its 'base' address.
- /// </summary>
- Realm Realm { get; }
-
+ public interface IAuthenticationRequest : IHostProcessedRequest {
/// <summary>
/// Gets a value indicating whether the Provider should help the user
/// select a Claimed Identifier to send back to the relying party.
@@ -109,18 +92,5 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// request before the <see cref="ClaimedIdentifier"/> property is set.
/// </exception>
void SetClaimedIdentifierFragment(string fragment);
-
- /// <summary>
- /// Attempts to perform relying party discovery of the return URL claimed by the Relying Party.
- /// </summary>
- /// <param name="requestHandler">The request handler to use to perform relying party discovery.</param>
- /// <returns>
- /// <c>true</c> if the Relying Party passed discovery verification; <c>false</c> otherwise.
- /// </returns>
- /// <remarks>
- /// <para>Return URL verification is only attempted if this method is called.</para>
- /// <para>See OpenID Authentication 2.0 spec section 9.2.1.</para>
- /// </remarks>
- bool IsReturnUrlDiscoverable(IDirectWebRequestHandler requestHandler);
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs
new file mode 100644
index 0000000..31479a1
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs
@@ -0,0 +1,45 @@
+//-----------------------------------------------------------------------
+// <copyright file="IHostProcessedRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// Interface exposing incoming messages to the OpenID Provider that
+ /// require interaction with the host site.
+ /// </summary>
+ public interface IHostProcessedRequest : IRequest {
+ /// <summary>
+ /// Gets the version of OpenID being used by the relying party that sent the request.
+ /// </summary>
+ ProtocolVersion RelyingPartyVersion { get; }
+
+ /// <summary>
+ /// Gets the URL the consumer site claims to use as its 'base' address.
+ /// </summary>
+ Realm Realm { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether the consumer demands an immediate response.
+ /// If false, the consumer is willing to wait for the identity provider
+ /// to authenticate the user.
+ /// </summary>
+ bool Immediate { get; }
+
+ /// <summary>
+ /// Attempts to perform relying party discovery of the return URL claimed by the Relying Party.
+ /// </summary>
+ /// <param name="requestHandler">The request handler to use to perform relying party discovery.</param>
+ /// <returns>
+ /// <c>true</c> if the Relying Party passed discovery verification; <c>false</c> otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Return URL verification is only attempted if this method is called.</para>
+ /// <para>See OpenID Authentication 2.0 spec section 9.2.1.</para>
+ /// </remarks>
+ bool IsReturnUrlDiscoverable(IDirectWebRequestHandler requestHandler);
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
index c3780a9..7bb404c 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
@@ -204,6 +204,11 @@ namespace DotNetOpenAuth.OpenId.Provider {
return new AuthenticationRequest(this, checkIdMessage);
}
+ var extensionOnlyRequest = incomingMessage as SignedResponseRequest;
+ if (extensionOnlyRequest != null) {
+ return new AnonymousRequest(this, extensionOnlyRequest);
+ }
+
var checkAuthMessage = incomingMessage as CheckAuthenticationRequest;
if (checkAuthMessage != null) {
return new AutoResponsiveRequest(incomingMessage, new CheckAuthenticationResponse(checkAuthMessage, this));
diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
index bec510b..91f10e6 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
@@ -27,7 +27,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// The key used to store the pending authentication request in the ASP.NET session.
/// </summary>
- private const string PendingAuthenticationRequestKey = "pendingAuthenticationRequestKey";
+ private const string PendingRequestKey = "pendingRequest";
/// <summary>
/// The default value for the <see cref="Enabled"/> property.
@@ -52,6 +52,12 @@ namespace DotNetOpenAuth.OpenId.Provider {
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>
@@ -76,8 +82,36 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// before responding to the relying party's authentication request.
/// </remarks>
public static IAuthenticationRequest PendingAuthenticationRequest {
- get { return HttpContext.Current.Session[PendingAuthenticationRequestKey] as IAuthenticationRequest; }
- set { HttpContext.Current.Session[PendingAuthenticationRequestKey] = value; }
+ get { return HttpContext.Current.Session[PendingRequestKey] as IAuthenticationRequest; }
+ set { 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 { return HttpContext.Current.Session[PendingRequestKey] as IAnonymousRequest; }
+ set { 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 { return HttpContext.Current.Session[PendingRequestKey] as IHostProcessedRequest; }
+ set { HttpContext.Current.Session[PendingRequestKey] = value; }
}
/// <summary>
@@ -100,8 +134,8 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// Sends the response for the <see cref="PendingAuthenticationRequest"/> and clears the property.
/// </summary>
public static void SendResponse() {
- Provider.SendResponse(PendingAuthenticationRequest);
- PendingAuthenticationRequest = null;
+ Provider.SendResponse(PendingRequest);
+ PendingRequest = null;
}
/// <summary>
@@ -125,13 +159,22 @@ namespace DotNetOpenAuth.OpenId.Provider {
// determine what incoming message was received
IRequest request = provider.GetRequest();
if (request != null) {
+ PendingRequest = null;
+
// process the incoming message appropriately and send the response
- if (!request.IsResponseReady) {
- var idrequest = (IAuthenticationRequest)request;
+ IAuthenticationRequest idrequest;
+ IAnonymousRequest anonRequest;
+ if ((idrequest = request as IAuthenticationRequest) != null) {
PendingAuthenticationRequest = idrequest;
this.OnAuthenticationChallenge(idrequest);
- } else {
- PendingAuthenticationRequest = null;
+ } 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.SendResponse(request);
@@ -154,6 +197,21 @@ namespace DotNetOpenAuth.OpenId.Provider {
}
/// <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>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
index 2e22aec..1f470b2 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
@@ -133,6 +133,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets or sets a value indicating whether this request only carries extensions
+ /// and is not a request to verify that the user controls some identifier.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this request is merely a carrier of extensions and is not
+ /// about an OpenID identifier; otherwise, <c>false</c>.
+ /// </value>
+ public bool IsExtensionOnly { get; set; }
+
+ /// <summary>
/// Gets information about the OpenId Provider, as advertised by the
/// OpenId discovery documents found at the <see cref="ClaimedIdentifier"/>
/// location.
@@ -401,16 +411,22 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Creates the authentication request message to send to the Provider,
+ /// Creates the request message to send to the Provider,
/// based on the properties in this instance.
/// </summary>
/// <returns>The message to send to the Provider.</returns>
- private CheckIdRequest CreateRequestMessage() {
+ private SignedResponseRequest CreateRequestMessage() {
Association association = this.GetAssociation();
- CheckIdRequest request = new CheckIdRequest(this.endpoint.Protocol.Version, this.endpoint.ProviderEndpoint, this.Mode);
- request.ClaimedIdentifier = this.endpoint.ClaimedIdentifier;
- request.LocalIdentifier = this.endpoint.ProviderLocalIdentifier;
+ SignedResponseRequest request;
+ if (!this.IsExtensionOnly) {
+ CheckIdRequest authRequest = new CheckIdRequest(this.endpoint.Protocol.Version, this.endpoint.ProviderEndpoint, this.Mode);
+ authRequest.ClaimedIdentifier = this.endpoint.ClaimedIdentifier;
+ authRequest.LocalIdentifier = this.endpoint.ProviderLocalIdentifier;
+ request = authRequest;
+ } else {
+ request = new SignedResponseRequest(this.endpoint.Protocol.Version, this.endpoint.ProviderEndpoint, this.Mode);
+ }
request.Realm = this.Realm;
request.ReturnTo = this.ReturnToUrl;
request.AssociationHandle = association != null ? association.Handle : null;
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationStatus.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationStatus.cs
index c67355d..d9e5d0a 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationStatus.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationStatus.cs
@@ -33,5 +33,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// Authentication is completed successfully.
/// </summary>
Authenticated,
+
+ /// <summary>
+ /// The Provider sent a message that did not contain an identity assertion,
+ /// but may carry OpenID extensions.
+ /// </summary>
+ ExtensionsOnly,
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs
index 4892092..0bfcaf5 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs
@@ -63,6 +63,30 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
bool IsDirectedIdentity { get; }
/// <summary>
+ /// Gets or sets a value indicating whether this request only carries extensions
+ /// and is not a request to verify that the user controls some identifier.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this request is merely a carrier of extensions and is not
+ /// about an OpenID identifier; otherwise, <c>false</c>.
+ /// </value>
+ /// <remarks>
+ /// <para>Although OpenID is first and primarily an authentication protocol, its extensions
+ /// can be interesting all by themselves. For instance, a relying party might want
+ /// to know that its user is over 21 years old, or perhaps a member of some organization.
+ /// OpenID extensions can provide this, without any need for asserting the identity of the user.</para>
+ /// <para>Constructing an OpenID request for only extensions can be done by calling
+ /// <see cref="OpenIdRelyingParty.CreateRequest(Identifier)"/> with any valid OpenID identifier
+ /// (claimed identifier or OP identifier). But once this property is set to <c>true</c>,
+ /// the claimed identifier value in the request is not included in the transmitted message.</para>
+ /// <para>It is anticipated that an RP would only issue these types of requests to OPs that
+ /// trusts to make assertions regarding the individual holding an account at that OP, so it
+ /// is not likely that the RP would allow the user to type in an arbitrary claimed identifier
+ /// without checking that it resolved to an OP endpoint the RP has on a trust whitelist.</para>
+ /// </remarks>
+ bool IsExtensionOnly { get; set; }
+
+ /// <summary>
/// Gets information about the OpenId Provider, as advertised by the
/// OpenId discovery documents found at the <see cref="ClaimedIdentifier"/>
/// location.
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
index 4bcaf10..3762602 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -342,8 +342,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
var message = this.Channel.ReadFromRequest(httpRequestInfo);
PositiveAssertionResponse positiveAssertion;
NegativeAssertionResponse negativeAssertion;
+ IndirectSignedResponse positiveExtensionOnly;
if ((positiveAssertion = message as PositiveAssertionResponse) != null) {
return new PositiveAuthenticationResponse(positiveAssertion, this);
+ } else if ((positiveExtensionOnly = message as IndirectSignedResponse) != null) {
+ return new PositiveAnonymousResponse(positiveExtensionOnly);
} else if ((negativeAssertion = message as NegativeAssertionResponse) != null) {
return new NegativeAuthenticationResponse(negativeAssertion);
} else if (message != null) {
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
index c86647a..1eea315 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
@@ -1033,8 +1033,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
case AuthenticationStatus.Failed:
this.OnFailed(response);
break;
+ case AuthenticationStatus.ExtensionsOnly:
default:
- throw new InvalidOperationException("Unexpected response status code.");
+ // The NotApplicable (extension-only assertion) is NOT one that we support
+ // in this control because that scenario is primarily interesting to RPs
+ // that are asking a specific OP, and it is not user-initiated as this textbox
+ // is designed for.
+ throw new InvalidOperationException(MessagingStrings.UnexpectedMessageReceivedOfMany);
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs
new file mode 100644
index 0000000..a28de12
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs
@@ -0,0 +1,273 @@
+//-----------------------------------------------------------------------
+// <copyright file="PositiveAnonymousResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Web;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// Wraps an extension-only response from the OP in an <see cref="IAuthenticationResponse"/> instance
+ /// for public consumption by the host web site.
+ /// </summary>
+ internal class PositiveAnonymousResponse : IAuthenticationResponse {
+ /// <summary>
+ /// Backin field for the <see cref="Response"/> property.
+ /// </summary>
+ private readonly IndirectSignedResponse response;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PositiveAnonymousResponse"/> class.
+ /// </summary>
+ /// <param name="response">The response message.</param>
+ protected internal PositiveAnonymousResponse(IndirectSignedResponse response) {
+ Contract.Requires(response != null);
+ ErrorUtilities.VerifyArgumentNotNull(response, "response");
+
+ this.response = response;
+ }
+
+ #region IAuthenticationResponse Properties
+
+ /// <summary>
+ /// Gets the Identifier that the end user claims to own. For use with user database storage and lookup.
+ /// May be null for some failed authentications (i.e. failed directed identity authentications).
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// This is the secure identifier that should be used for database storage and lookup.
+ /// It is not always friendly (i.e. =Arnott becomes =!9B72.7DD1.50A9.5CCD), but it protects
+ /// user identities against spoofing and other attacks.
+ /// </para>
+ /// <para>
+ /// For user-friendly identifiers to display, use the
+ /// <see cref="FriendlyIdentifierForDisplay"/> property.
+ /// </para>
+ /// </remarks>
+ public virtual Identifier ClaimedIdentifier {
+ get { return null; }
+ }
+
+ /// <summary>
+ /// Gets a user-friendly OpenID Identifier for display purposes ONLY.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// <para>
+ /// This <i>should</i> be put through <see cref="HttpUtility.HtmlEncode(string)"/> before
+ /// sending to a browser to secure against javascript injection attacks.
+ /// </para>
+ /// <para>
+ /// This property retains some aspects of the user-supplied identifier that get lost
+ /// in the <see cref="ClaimedIdentifier"/>. For example, XRIs used as user-supplied
+ /// identifiers (i.e. =Arnott) become unfriendly unique strings (i.e. =!9B72.7DD1.50A9.5CCD).
+ /// For display purposes, such as text on a web page that says "You're logged in as ...",
+ /// this property serves to provide the =Arnott string, or whatever else is the most friendly
+ /// string close to what the user originally typed in.
+ /// </para>
+ /// <para>
+ /// If the user-supplied identifier is a URI, this property will be the URI after all
+ /// redirects, and with the protocol and fragment trimmed off.
+ /// If the user-supplied identifier is an XRI, this property will be the original XRI.
+ /// If the user-supplied identifier is an OpenID Provider identifier (i.e. yahoo.com),
+ /// this property will be the Claimed Identifier, with the protocol stripped if it is a URI.
+ /// </para>
+ /// <para>
+ /// It is <b>very</b> important that this property <i>never</i> be used for database storage
+ /// or lookup to avoid identity spoofing and other security risks. For database storage
+ /// and lookup please use the <see cref="ClaimedIdentifier"/> property.
+ /// </para>
+ /// </remarks>
+ public virtual string FriendlyIdentifierForDisplay {
+ get { return null; }
+ }
+
+ /// <summary>
+ /// Gets the detailed success or failure status of the authentication attempt.
+ /// </summary>
+ public virtual AuthenticationStatus Status {
+ get { return AuthenticationStatus.ExtensionsOnly; }
+ }
+
+ /// <summary>
+ /// Gets the details regarding a failed authentication attempt, if available.
+ /// This will be set if and only if <see cref="Status"/> is <see cref="AuthenticationStatus.Failed"/>.
+ /// </summary>
+ /// <value></value>
+ public Exception Exception {
+ get { return null; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the positive extension-only message the Relying Party received that this instance wraps.
+ /// </summary>
+ protected internal IndirectSignedResponse Response {
+ get { return this.response; }
+ }
+
+ #region IAuthenticationResponse methods
+
+ /// <summary>
+ /// Gets a callback argument's value that was previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.
+ /// </summary>
+ /// <param name="key">The name of the parameter whose value is sought.</param>
+ /// <returns>
+ /// The value of the argument, or null if the named parameter could not be found.
+ /// </returns>
+ /// <remarks>
+ /// Callback parameters are only available if they are complete and untampered with
+ /// since the original request message (as proven by a signature).
+ /// If the relying party is operating in stateless mode <c>null</c> is always
+ /// returned since the callback arguments could not be signed to protect against
+ /// tampering.
+ /// </remarks>
+ public string GetCallbackArgument(string key) {
+ if (this.response.ReturnToParametersSignatureValidated) {
+ return this.response.GetReturnToArgument(key);
+ } else {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Gets all the callback arguments that were previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
+ /// of the return_to URL.
+ /// </summary>
+ /// <returns>A name-value dictionary. Never null.</returns>
+ /// <remarks>
+ /// Callback parameters are only available if they are complete and untampered with
+ /// since the original request message (as proven by a signature).
+ /// If the relying party is operating in stateless mode an empty dictionary is always
+ /// returned since the callback arguments could not be signed to protect against
+ /// tampering.
+ /// </remarks>
+ public IDictionary<string, string> GetCallbackArguments() {
+ if (this.response.ReturnToParametersSignatureValidated) {
+ var args = new Dictionary<string, string>();
+
+ // Return all the return_to arguments, except for the OpenID-supporting ones.
+ // The only arguments that should be returned here are the ones that the host
+ // web site adds explicitly.
+ foreach (string key in this.response.GetReturnToParameterNames().Where(key => !OpenIdRelyingParty.IsOpenIdSupportingParameter(key))) {
+ args[key] = this.response.GetReturnToArgument(key);
+ }
+
+ return args;
+ } else {
+ return EmptyDictionary<string, string>.Instance;
+ }
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response.
+ /// </summary>
+ /// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension&lt;T&gt;"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public T GetExtension<T>() where T : IOpenIdMessageExtension {
+ return this.response.SignedExtensions.OfType<T>().FirstOrDefault();
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response.
+ /// </summary>
+ /// <param name="extensionType">Type of the extension to look for in the response.</param>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="GetUntrustedExtension"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public IOpenIdMessageExtension GetExtension(Type extensionType) {
+ ErrorUtilities.VerifyArgumentNotNull(extensionType, "extensionType");
+ return this.response.SignedExtensions.OfType<IOpenIdMessageExtension>().Where(ext => extensionType.IsInstanceOfType(ext)).FirstOrDefault();
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response, without
+ /// requiring it to be signed by the Provider.
+ /// </summary>
+ /// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension&lt;T&gt;"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public T GetUntrustedExtension<T>() where T : IOpenIdMessageExtension {
+ return this.response.Extensions.OfType<T>().FirstOrDefault();
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response.
+ /// </summary>
+ /// <param name="extensionType">Type of the extension to look for in the response.</param>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="GetExtension"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ public IOpenIdMessageExtension GetUntrustedExtension(Type extensionType) {
+ ErrorUtilities.VerifyArgumentNotNull(extensionType, "extensionType");
+ return this.response.Extensions.OfType<IOpenIdMessageExtension>().Where(ext => extensionType.IsInstanceOfType(ext)).FirstOrDefault();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
index 32980f5..e3740db 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
@@ -5,14 +5,11 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId.RelyingParty {
- using System;
- using System.Collections.Generic;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
using System.Linq;
- using System.Text;
using System.Web;
using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.Extensions;
using DotNetOpenAuth.OpenId.Messages;
/// <summary>
@@ -20,17 +17,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// for public consumption by the host web site.
/// </summary>
[DebuggerDisplay("Status: {Status}, ClaimedIdentifier: {ClaimedIdentifier}")]
- internal class PositiveAuthenticationResponse : IAuthenticationResponse {
- /// <summary>
- /// The positive assertion message the Relying Party received that this instance wraps.
- /// </summary>
- private readonly PositiveAssertionResponse response;
-
- /// <summary>
- /// The relying party that created this request object.
- /// </summary>
- private readonly OpenIdRelyingParty relyingParty;
-
+ internal class PositiveAuthenticationResponse : PositiveAnonymousResponse {
/// <summary>
/// The OpenID service endpoint reconstructed from the assertion message.
/// </summary>
@@ -47,22 +34,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="response">The positive assertion response that was just received by the Relying Party.</param>
/// <param name="relyingParty">The relying party.</param>
- internal PositiveAuthenticationResponse(PositiveAssertionResponse response, OpenIdRelyingParty relyingParty) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ internal PositiveAuthenticationResponse(PositiveAssertionResponse response, OpenIdRelyingParty relyingParty)
+ : base(response) {
+ Contract.Requires(relyingParty != null);
ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
- this.response = response;
- this.relyingParty = relyingParty;
-
this.endpoint = ServiceEndpoint.CreateForClaimedIdentifier(
- this.response.ClaimedIdentifier,
- this.response.GetReturnToArgument(AuthenticationRequest.UserSuppliedIdentifierParameterName),
- this.response.LocalIdentifier,
- new ProviderEndpointDescription(this.response.ProviderEndpoint, this.response.Version),
+ this.Response.ClaimedIdentifier,
+ this.Response.GetReturnToArgument(AuthenticationRequest.UserSuppliedIdentifierParameterName),
+ this.Response.LocalIdentifier,
+ new ProviderEndpointDescription(this.Response.ProviderEndpoint, this.Response.Version),
null,
null);
- this.VerifyDiscoveryMatchesAssertion();
+ this.VerifyDiscoveryMatchesAssertion(relyingParty);
}
#region IAuthenticationResponse Properties
@@ -83,14 +68,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <see cref="FriendlyIdentifierForDisplay"/> property.
/// </para>
/// </remarks>
- public Identifier ClaimedIdentifier {
+ public override Identifier ClaimedIdentifier {
get { return this.endpoint.ClaimedIdentifier; }
}
/// <summary>
/// Gets a user-friendly OpenID Identifier for display purposes ONLY.
/// </summary>
- /// <value></value>
/// <remarks>
/// <para>
/// This <i>should</i> be put through <see cref="HttpUtility.HtmlEncode(string)"/> before
@@ -117,203 +101,38 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// and lookup please use the <see cref="ClaimedIdentifier"/> property.
/// </para>
/// </remarks>
- public string FriendlyIdentifierForDisplay {
+ public override string FriendlyIdentifierForDisplay {
get { return this.endpoint.FriendlyIdentifierForDisplay; }
}
/// <summary>
/// Gets the detailed success or failure status of the authentication attempt.
/// </summary>
- /// <value></value>
- public AuthenticationStatus Status {
+ public override AuthenticationStatus Status {
get { return AuthenticationStatus.Authenticated; }
}
- /// <summary>
- /// Gets the details regarding a failed authentication attempt, if available.
- /// This will be set if and only if <see cref="Status"/> is <see cref="AuthenticationStatus.Failed"/>.
- /// </summary>
- /// <value></value>
- public Exception Exception {
- get { return null; }
- }
-
#endregion
/// <summary>
/// Gets the positive assertion response message.
/// </summary>
- internal PositiveAssertionResponse Response {
- get { return this.response; }
- }
-
- #region IAuthenticationResponse methods
-
- /// <summary>
- /// Gets a callback argument's value that was previously added using
- /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.
- /// </summary>
- /// <param name="key">The name of the parameter whose value is sought.</param>
- /// <returns>
- /// The value of the argument, or null if the named parameter could not be found.
- /// </returns>
- /// <remarks>
- /// Callback parameters are only available if they are complete and untampered with
- /// since the original request message (as proven by a signature).
- /// If the relying party is operating in stateless mode <c>null</c> is always
- /// returned since the callback arguments could not be signed to protect against
- /// tampering.
- /// </remarks>
- public string GetCallbackArgument(string key) {
- if (this.response.ReturnToParametersSignatureValidated) {
- return this.response.GetReturnToArgument(key);
- } else {
- return null;
- }
- }
-
- /// <summary>
- /// Gets all the callback arguments that were previously added using
- /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
- /// of the return_to URL.
- /// </summary>
- /// <returns>A name-value dictionary. Never null.</returns>
- /// <remarks>
- /// Callback parameters are only available if they are complete and untampered with
- /// since the original request message (as proven by a signature).
- /// If the relying party is operating in stateless mode an empty dictionary is always
- /// returned since the callback arguments could not be signed to protect against
- /// tampering.
- /// </remarks>
- public IDictionary<string, string> GetCallbackArguments() {
- if (this.response.ReturnToParametersSignatureValidated) {
- var args = new Dictionary<string, string>();
-
- // Return all the return_to arguments, except for the OpenID-supporting ones.
- // The only arguments that should be returned here are the ones that the host
- // web site adds explicitly.
- foreach (string key in this.response.GetReturnToParameterNames().Where(key => !OpenIdRelyingParty.IsOpenIdSupportingParameter(key))) {
- args[key] = this.response.GetReturnToArgument(key);
- }
-
- return args;
- } else {
- return EmptyDictionary<string, string>.Instance;
- }
- }
-
- /// <summary>
- /// Tries to get an OpenID extension that may be present in the response.
- /// </summary>
- /// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
- /// <returns>
- /// The extension, if it is found. Null otherwise.
- /// </returns>
- /// <remarks>
- /// <para>Extensions are returned only if the Provider signed them.
- /// Relying parties that do not care if the values were modified in
- /// transit should use the <see cref="GetUntrustedExtension&lt;T&gt;"/> method
- /// in order to allow the Provider to not sign the extension. </para>
- /// <para>Unsigned extensions are completely unreliable and should be
- /// used only to prefill user forms since the user or any other third
- /// party may have tampered with the data carried by the extension.</para>
- /// <para>Signed extensions are only reliable if the relying party
- /// trusts the OpenID Provider that signed them. Signing does not mean
- /// the relying party can trust the values -- it only means that the values
- /// have not been tampered with since the Provider sent the message.</para>
- /// </remarks>
- public T GetExtension<T>() where T : IOpenIdMessageExtension {
- return this.response.SignedExtensions.OfType<T>().FirstOrDefault();
- }
-
- /// <summary>
- /// Tries to get an OpenID extension that may be present in the response.
- /// </summary>
- /// <param name="extensionType">Type of the extension to look for in the response.</param>
- /// <returns>
- /// The extension, if it is found. Null otherwise.
- /// </returns>
- /// <remarks>
- /// <para>Extensions are returned only if the Provider signed them.
- /// Relying parties that do not care if the values were modified in
- /// transit should use the <see cref="GetUntrustedExtension"/> method
- /// in order to allow the Provider to not sign the extension. </para>
- /// <para>Unsigned extensions are completely unreliable and should be
- /// used only to prefill user forms since the user or any other third
- /// party may have tampered with the data carried by the extension.</para>
- /// <para>Signed extensions are only reliable if the relying party
- /// trusts the OpenID Provider that signed them. Signing does not mean
- /// the relying party can trust the values -- it only means that the values
- /// have not been tampered with since the Provider sent the message.</para>
- /// </remarks>
- public IOpenIdMessageExtension GetExtension(Type extensionType) {
- ErrorUtilities.VerifyArgumentNotNull(extensionType, "extensionType");
- return this.response.SignedExtensions.OfType<IOpenIdMessageExtension>().Where(ext => extensionType.IsInstanceOfType(ext)).FirstOrDefault();
- }
-
- /// <summary>
- /// Tries to get an OpenID extension that may be present in the response, without
- /// requiring it to be signed by the Provider.
- /// </summary>
- /// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
- /// <returns>
- /// The extension, if it is found. Null otherwise.
- /// </returns>
- /// <remarks>
- /// <para>Extensions are returned whether they are signed or not.
- /// Use the <see cref="GetExtension&lt;T&gt;"/> method to retrieve
- /// extension responses only if they are signed by the Provider to
- /// protect against tampering. </para>
- /// <para>Unsigned extensions are completely unreliable and should be
- /// used only to prefill user forms since the user or any other third
- /// party may have tampered with the data carried by the extension.</para>
- /// <para>Signed extensions are only reliable if the relying party
- /// trusts the OpenID Provider that signed them. Signing does not mean
- /// the relying party can trust the values -- it only means that the values
- /// have not been tampered with since the Provider sent the message.</para>
- /// </remarks>
- public T GetUntrustedExtension<T>() where T : IOpenIdMessageExtension {
- return this.response.Extensions.OfType<T>().FirstOrDefault();
- }
-
- /// <summary>
- /// Tries to get an OpenID extension that may be present in the response.
- /// </summary>
- /// <param name="extensionType">Type of the extension to look for in the response.</param>
- /// <returns>
- /// The extension, if it is found. Null otherwise.
- /// </returns>
- /// <remarks>
- /// <para>Extensions are returned whether they are signed or not.
- /// Use the <see cref="GetExtension"/> method to retrieve
- /// extension responses only if they are signed by the Provider to
- /// protect against tampering. </para>
- /// <para>Unsigned extensions are completely unreliable and should be
- /// used only to prefill user forms since the user or any other third
- /// party may have tampered with the data carried by the extension.</para>
- /// <para>Signed extensions are only reliable if the relying party
- /// trusts the OpenID Provider that signed them. Signing does not mean
- /// the relying party can trust the values -- it only means that the values
- /// have not been tampered with since the Provider sent the message.</para>
- /// </remarks>
- public IOpenIdMessageExtension GetUntrustedExtension(Type extensionType) {
- ErrorUtilities.VerifyArgumentNotNull(extensionType, "extensionType");
- return this.response.Extensions.OfType<IOpenIdMessageExtension>().Where(ext => extensionType.IsInstanceOfType(ext)).FirstOrDefault();
+ protected internal new PositiveAssertionResponse Response {
+ get { return (PositiveAssertionResponse)base.Response; }
}
- #endregion
-
/// <summary>
/// Verifies that the positive assertion data matches the results of
/// discovery on the Claimed Identifier.
/// </summary>
+ /// <param name="relyingParty">The relying party.</param>
/// <exception cref="ProtocolException">
/// Thrown when the Provider is asserting that a user controls an Identifier
/// when discovery on that Identifier contradicts what the Provider says.
/// This would be an indication of either a misconfigured Provider or
/// an attempt by someone to spoof another user's identity with a rogue Provider.
/// </exception>
- private void VerifyDiscoveryMatchesAssertion() {
+ private void VerifyDiscoveryMatchesAssertion(OpenIdRelyingParty relyingParty) {
Logger.OpenId.Debug("Verifying assertion matches identifier discovery results...");
// While it LOOKS like we're performing discovery over HTTP again
@@ -325,8 +144,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// is signed by the RP before it's considered reliable. In 1.x stateless mode, this RP
// doesn't (and can't) sign its own return_to URL, so its cached discovery information
// is merely a hint that must be verified by performing discovery again here.
- var discoveryResults = this.response.ClaimedIdentifier.Discover(this.relyingParty.WebRequestHandler);
- ErrorUtilities.VerifyProtocol(discoveryResults.Contains(this.endpoint), OpenIdStrings.IssuedAssertionFailsIdentifierDiscovery, this.endpoint, discoveryResults.ToStringDeferred(true));
+ var discoveryResults = this.Response.ClaimedIdentifier.Discover(relyingParty.WebRequestHandler);
+ ErrorUtilities.VerifyProtocol(
+ discoveryResults.Contains(this.endpoint),
+ OpenIdStrings.IssuedAssertionFailsIdentifierDiscovery,
+ this.endpoint,
+ discoveryResults.ToStringDeferred(true));
}
}
}