//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
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;
///
/// Wraps an extension-only response from the OP in an instance
/// for public consumption by the host web site.
///
internal class PositiveAnonymousResponse : IAuthenticationResponse {
///
/// Backin field for the property.
///
private readonly IndirectSignedResponse response;
///
/// Information about the OP endpoint that issued this assertion.
///
private readonly IProviderEndpoint provider;
///
/// Initializes a new instance of the class.
///
/// The response message.
protected internal PositiveAnonymousResponse(IndirectSignedResponse response) {
Requires.NotNull(response, "response");
this.response = response;
if (response.ProviderEndpoint != null && response.Version != null) {
this.provider = new ProviderEndpointDescription(response.ProviderEndpoint, response.Version);
}
// Derived types of this are responsible to log an appropriate message for themselves.
if (Logger.OpenId.IsInfoEnabled && this.GetType() == typeof(PositiveAnonymousResponse)) {
Logger.OpenId.Info("Received anonymous (identity-less) positive assertion.");
}
if (response.ProviderEndpoint != null) {
Reporting.RecordEventOccurrence(this, response.ProviderEndpoint.AbsoluteUri);
}
}
#region IAuthenticationResponse Properties
///
/// 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).
///
///
///
/// 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.
///
///
/// For user-friendly identifiers to display, use the
/// property.
///
///
public virtual Identifier ClaimedIdentifier {
get { return null; }
}
///
/// Gets a user-friendly OpenID Identifier for display purposes ONLY.
///
///
///
///
/// This should be put through before
/// sending to a browser to secure against javascript injection attacks.
///
///
/// This property retains some aspects of the user-supplied identifier that get lost
/// in the . 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.
///
///
/// 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.
///
///
/// It is very important that this property never be used for database storage
/// or lookup to avoid identity spoofing and other security risks. For database storage
/// and lookup please use the property.
///
///
public virtual string FriendlyIdentifierForDisplay {
get { return null; }
}
///
/// Gets the detailed success or failure status of the authentication attempt.
///
public virtual AuthenticationStatus Status {
get { return AuthenticationStatus.ExtensionsOnly; }
}
///
/// Gets information about the OpenId Provider, as advertised by the
/// OpenID discovery documents found at the
/// location.
///
///
/// The Provider endpoint that issued the positive assertion;
/// or null if information about the Provider is unavailable.
///
public IProviderEndpoint Provider {
get { return this.provider; }
}
///
/// Gets the details regarding a failed authentication attempt, if available.
/// This will be set if and only if is .
///
///
public Exception Exception {
get { return null; }
}
#endregion
///
/// Gets a value indicating whether trusted callback arguments are available.
///
///
/// We use this internally to avoid logging a warning during a standard snapshot creation.
///
internal bool TrustedCallbackArgumentsAvailable {
get { return this.response.ReturnToParametersSignatureValidated; }
}
///
/// Gets the positive extension-only message the Relying Party received that this instance wraps.
///
protected internal IndirectSignedResponse Response {
get { return this.response; }
}
#region IAuthenticationResponse methods
///
/// Gets a callback argument's value that was previously added using
/// .
///
/// The name of the parameter whose value is sought.
///
/// The value of the argument, or null if the named parameter could not be found.
///
///
/// 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 null is always
/// returned since the callback arguments could not be signed to protect against
/// tampering.
///
public string GetCallbackArgument(string key) {
if (this.response.ReturnToParametersSignatureValidated) {
return this.GetUntrustedCallbackArgument(key);
} else {
Logger.OpenId.WarnFormat(OpenIdStrings.CallbackArgumentsRequireSecretStore, typeof(IRelyingPartyAssociationStore).Name, typeof(OpenIdRelyingParty).Name);
return null;
}
}
///
/// Gets a callback argument's value that was previously added using
/// .
///
/// The name of the parameter whose value is sought.
///
/// The value of the argument, or null if the named parameter could not be found.
///
///
/// Callback parameters are only available even if the RP is in stateless mode,
/// or the callback parameters are otherwise unverifiable as untampered with.
/// Therefore, use this method only when the callback argument is not to be
/// used to make a security-sensitive decision.
///
public string GetUntrustedCallbackArgument(string key) {
return this.response.GetReturnToArgument(key);
}
///
/// Gets all the callback arguments that were previously added using
/// or as a natural part
/// of the return_to URL.
///
/// A name-value dictionary. Never null.
///
/// 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.
///
public IDictionary GetCallbackArguments() {
if (this.response.ReturnToParametersSignatureValidated) {
return this.GetUntrustedCallbackArguments();
} else {
Logger.OpenId.WarnFormat(OpenIdStrings.CallbackArgumentsRequireSecretStore, typeof(IRelyingPartyAssociationStore).Name, typeof(OpenIdRelyingParty).Name);
return EmptyDictionary.Instance;
}
}
///
/// Gets all the callback arguments that were previously added using
/// or as a natural part
/// of the return_to URL.
///
/// A name-value dictionary. Never null.
///
/// 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.
///
public IDictionary GetUntrustedCallbackArguments() {
var args = new Dictionary();
// 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;
}
///
/// Tries to get an OpenID extension that may be present in the response.
///
/// The type of extension to look for in the response message.
///
/// The extension, if it is found. Null otherwise.
///
///
/// 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 method
/// in order to allow the Provider to not sign the extension.
/// 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.
/// 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.
///
public T GetExtension() where T : IOpenIdMessageExtension {
return this.response.SignedExtensions.OfType().FirstOrDefault();
}
///
/// Tries to get an OpenID extension that may be present in the response.
///
/// Type of the extension to look for in the response.
///
/// The extension, if it is found. Null otherwise.
///
///
/// 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 method
/// in order to allow the Provider to not sign the extension.
/// 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.
/// 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.
///
public IOpenIdMessageExtension GetExtension(Type extensionType) {
return this.response.SignedExtensions.OfType().Where(ext => extensionType.IsInstanceOfType(ext)).FirstOrDefault();
}
///
/// Tries to get an OpenID extension that may be present in the response, without
/// requiring it to be signed by the Provider.
///
/// The type of extension to look for in the response message.
///
/// The extension, if it is found. Null otherwise.
///
///
/// Extensions are returned whether they are signed or not.
/// Use the method to retrieve
/// extension responses only if they are signed by the Provider to
/// protect against tampering.
/// 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.
/// 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.
///
public T GetUntrustedExtension() where T : IOpenIdMessageExtension {
return this.response.Extensions.OfType().FirstOrDefault();
}
///
/// Tries to get an OpenID extension that may be present in the response.
///
/// Type of the extension to look for in the response.
///
/// The extension, if it is found. Null otherwise.
///
///
/// Extensions are returned whether they are signed or not.
/// Use the method to retrieve
/// extension responses only if they are signed by the Provider to
/// protect against tampering.
/// 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.
/// 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.
///
public IOpenIdMessageExtension GetUntrustedExtension(Type extensionType) {
return this.response.Extensions.OfType().Where(ext => extensionType.IsInstanceOfType(ext)).FirstOrDefault();
}
#endregion
}
}