//----------------------------------------------------------------------- // // 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 } }