/// <summary>
        /// Gets the services for an identifier that are described by an external XRDS document.
        /// </summary>
        /// <param name="xrds">The XRD elements to search for described-by services.</param>
        /// <param name="identifier">The identifier under discovery.</param>
        /// <param name="requestHandler">The request handler.</param>
        /// <returns>The discovered services.</returns>
        private static IEnumerable <IdentifierDiscoveryResult> GetExternalServices(IEnumerable <XrdElement> xrds, UriIdentifier identifier, IDirectWebRequestHandler requestHandler)
        {
            Contract.Requires <ArgumentNullException>(xrds != null);
            Contract.Requires <ArgumentNullException>(identifier != null);
            Contract.Requires <ArgumentNullException>(requestHandler != null);
            Contract.Ensures(Contract.Result <IEnumerable <IdentifierDiscoveryResult> >() != null);

            var results = new List <IdentifierDiscoveryResult>();

            foreach (var serviceElement in GetDescribedByServices(xrds))
            {
                var templateNode      = serviceElement.Node.SelectSingleNode("google:URITemplate", serviceElement.XmlNamespaceResolver);
                var nextAuthorityNode = serviceElement.Node.SelectSingleNode("google:NextAuthority", serviceElement.XmlNamespaceResolver);
                if (templateNode != null)
                {
                    Uri    externalLocation = new Uri(templateNode.Value.Trim().Replace("{%uri}", Uri.EscapeDataString(identifier.Uri.AbsoluteUri)));
                    string nextAuthority    = nextAuthorityNode != null?nextAuthorityNode.Value.Trim() : identifier.Uri.Host;

                    try {
                        var          externalXrdsResponse = GetXrdsResponse(identifier, requestHandler, externalLocation);
                        XrdsDocument externalXrds         = new XrdsDocument(XmlReader.Create(externalXrdsResponse.ResponseStream));
                        ValidateXmlDSig(externalXrds, identifier, externalXrdsResponse, nextAuthority);
                        results.AddRange(GetXrdElements(externalXrds, identifier).CreateServiceEndpoints(identifier, identifier));
                    } catch (ProtocolException ex) {
                        Logger.Yadis.WarnFormat("HTTP GET error while retrieving described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
                    } catch (XmlException ex) {
                        Logger.Yadis.ErrorFormat("Error while parsing described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
                    }
                }
            }

            return(results);
        }
Exemple #2
0
        /// <summary>
        /// Searches for an XRDS document at the realm URL, and if found, searches
        /// for a description of a relying party endpoints (OpenId login pages).
        /// </summary>
        /// <param name="allowRedirects">
        /// Whether redirects may be followed when discovering the Realm.
        /// This may be true when creating an unsolicited assertion, but must be
        /// false when performing return URL verification per 2.0 spec section 9.2.1.
        /// </param>
        /// <returns>The details of the endpoints if found, otherwise null.</returns>
        internal IEnumerable <DotNetOpenId.Provider.RelyingPartyReceivingEndpoint> Discover(bool allowRedirects)
        {
            // Attempt YADIS discovery
            DiscoveryResult yadisResult = Yadis.Yadis.Discover(UriWithWildcardChangedToWww, false);

            if (yadisResult != null)
            {
                if (!allowRedirects && yadisResult.NormalizedUri != yadisResult.RequestUri)
                {
                    // Redirect occurred when it was not allowed.
                    throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
                                                            Strings.RealmCausedRedirectUponDiscovery, yadisResult.RequestUri));
                }
                if (yadisResult.IsXrds)
                {
                    try {
                        XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText);
                        return(xrds.FindRelyingPartyReceivingEndpoints());
                    } catch (XmlException ex) {
                        throw new OpenIdException(Strings.InvalidXRDSDocument, ex);
                    }
                }
            }
            return(new RelyingPartyReceivingEndpoint[0]);
        }
Exemple #3
0
        /// <summary>
        /// Performs discovery on the specified identifier.
        /// </summary>
        /// <param name="identifier">The identifier to perform discovery on.</param>
        /// <param name="requestHandler">The means to place outgoing HTTP requests.</param>
        /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param>
        /// <returns>
        /// A sequence of service endpoints yielded by discovery.  Must not be null, but may be empty.
        /// </returns>
        public IEnumerable <IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain)
        {
            abortDiscoveryChain = false;
            var uriIdentifier = identifier as UriIdentifier;

            if (uriIdentifier == null)
            {
                return(Enumerable.Empty <IdentifierDiscoveryResult>());
            }

            var endpoints = new List <IdentifierDiscoveryResult>();

            // Attempt YADIS discovery
            DiscoveryResult yadisResult = Yadis.Discover(requestHandler, uriIdentifier, identifier.IsDiscoverySecureEndToEnd);

            if (yadisResult != null)
            {
                if (yadisResult.IsXrds)
                {
                    try {
                        XrdsDocument xrds          = new XrdsDocument(yadisResult.ResponseText);
                        var          xrdsEndpoints = xrds.XrdElements.CreateServiceEndpoints(yadisResult.NormalizedUri, uriIdentifier);

                        // Filter out insecure endpoints if high security is required.
                        if (uriIdentifier.IsDiscoverySecureEndToEnd)
                        {
                            xrdsEndpoints = xrdsEndpoints.Where(se => se.ProviderEndpoint.IsTransportSecure());
                        }
                        endpoints.AddRange(xrdsEndpoints);
                    } catch (XmlException ex) {
                        Logger.Yadis.Error("Error while parsing the XRDS document.  Falling back to HTML discovery.", ex);
                    }
                }

                // Failing YADIS discovery of an XRDS document, we try HTML discovery.
                if (endpoints.Count == 0)
                {
                    var htmlEndpoints = new List <IdentifierDiscoveryResult>(DiscoverFromHtml(yadisResult.NormalizedUri, uriIdentifier, yadisResult.ResponseText));
                    if (htmlEndpoints.Any())
                    {
                        Logger.Yadis.DebugFormat("Total services discovered in HTML: {0}", htmlEndpoints.Count);
                        Logger.Yadis.Debug(htmlEndpoints.ToStringDeferred(true));
                        endpoints.AddRange(htmlEndpoints.Where(ep => !uriIdentifier.IsDiscoverySecureEndToEnd || ep.ProviderEndpoint.IsTransportSecure()));
                        if (endpoints.Count == 0)
                        {
                            Logger.Yadis.Info("No HTML discovered endpoints met the security requirements.");
                        }
                    }
                    else
                    {
                        Logger.Yadis.Debug("HTML discovery failed to find any endpoints.");
                    }
                }
                else
                {
                    Logger.Yadis.Debug("Skipping HTML discovery because XRDS contained service endpoints.");
                }
            }
            return(endpoints);
        }
Exemple #4
0
        /// <summary>
        /// Enumerates the XRDS service elements that describe OpenID Relying Party return_to URLs
        /// that can receive authentication assertions.
        /// </summary>
        /// <param name="xrds">The XrdsDocument instance to use in this process.</param>
        /// <returns>A sequence of service elements.</returns>
        private static IEnumerable <ServiceElement> FindReturnToServices(this XrdsDocument xrds)
        {
            Requires.NotNull(xrds, "xrds");

            return(from xrd in xrds.XrdElements
                   from service in xrd.OpenIdRelyingPartyReturnToServices
                   select service);
        }
Exemple #5
0
		/// <summary>
		/// Searches for an XRDS document at the realm URL, and if found, searches
		/// for a description of a relying party endpoints (OpenId login pages).
		/// </summary>
		/// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param>
		/// <param name="allowRedirects">Whether redirects may be followed when discovering the Realm.
		/// This may be true when creating an unsolicited assertion, but must be
		/// false when performing return URL verification per 2.0 spec section 9.2.1.</param>
		/// <returns>
		/// The details of the endpoints if found; or <c>null</c> if no service document was discovered.
		/// </returns>
		internal virtual IEnumerable<RelyingPartyEndpointDescription> DiscoverReturnToEndpoints(IDirectWebRequestHandler requestHandler, bool allowRedirects) {
			XrdsDocument xrds = this.Discover(requestHandler, allowRedirects);
			if (xrds != null) {
				return xrds.FindRelyingPartyReceivingEndpoints();
			}

			return null;
		}
Exemple #6
0
        /// <summary>
        /// Finds the icons the relying party wants an OP to display as part of authentication,
        /// per the UI extension spec.
        /// </summary>
        /// <param name="xrds">The XrdsDocument to search.</param>
        /// <returns>A sequence of the icon URLs in preferred order.</returns>
        internal static IEnumerable <Uri> FindRelyingPartyIcons(this XrdsDocument xrds)
        {
            Requires.NotNull(xrds, "xrds");

            return(from xrd in xrds.XrdElements
                   from service in xrd.OpenIdRelyingPartyIcons
                   from uri in service.UriElements
                   select uri.Uri);
        }
        /// <summary>
        /// Enumerates the XRDS service elements that describe OpenID Relying Party return_to URLs
        /// that can receive authentication assertions.
        /// </summary>
        /// <param name="xrds">The XrdsDocument instance to use in this process.</param>
        /// <returns>A sequence of service elements.</returns>
        private static IEnumerable <ServiceElement> FindReturnToServices(this XrdsDocument xrds)
        {
            Contract.Requires <ArgumentNullException>(xrds != null);
            Contract.Ensures(Contract.Result <IEnumerable <ServiceElement> >() != null);

            return(from xrd in xrds.XrdElements
                   from service in xrd.OpenIdRelyingPartyReturnToServices
                   select service);
        }
Exemple #8
0
        /// <summary>
        /// Downloads the XRDS document for this XRI.
        /// </summary>
        /// <param name="requestHandler">The request handler.</param>
        /// <returns>The XRDS document.</returns>
        private XrdsDocument DownloadXrds(IDirectWebRequestHandler requestHandler)
        {
            XrdsDocument doc;

            using (var xrdsResponse = Yadis.Request(requestHandler, this.XrdsUrl, this.IsDiscoverySecureEndToEnd)) {
                doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream));
            }
            ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed);
            return(doc);
        }
        /// <summary>
        /// Finds the icons the relying party wants an OP to display as part of authentication,
        /// per the UI extension spec.
        /// </summary>
        /// <param name="xrds">The XrdsDocument to search.</param>
        /// <returns>A sequence of the icon URLs in preferred order.</returns>
        internal static IEnumerable <Uri> FindRelyingPartyIcons(this XrdsDocument xrds)
        {
            Contract.Requires <ArgumentNullException>(xrds != null);
            Contract.Ensures(Contract.Result <IEnumerable <Uri> >() != null);

            return(from xrd in xrds.XrdElements
                   from service in xrd.OpenIdRelyingPartyIcons
                   from uri in service.UriElements
                   select uri.Uri);
        }
Exemple #10
0
        XrdsDocument downloadXrds()
        {
            var          xrdsResponse = UntrustedWebRequest.Request(XrdsUrl);
            XrdsDocument doc          = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream));

            if (!doc.IsXrdResolutionSuccessful)
            {
                throw new OpenIdException(Strings.XriResolutionFailed);
            }
            return(doc);
        }
Exemple #11
0
        /// <summary>
        /// Searches for an XRDS document at the realm URL, and if found, searches
        /// for a description of a relying party endpoints (OpenId login pages).
        /// </summary>
        /// <param name="hostFactories">The host factories.</param>
        /// <param name="allowRedirects">Whether redirects may be followed when discovering the Realm.
        /// This may be true when creating an unsolicited assertion, but must be
        /// false when performing return URL verification per 2.0 spec section 9.2.1.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// The details of the endpoints if found; or <c>null</c> if no service document was discovered.
        /// </returns>
        internal virtual async Task <IEnumerable <RelyingPartyEndpointDescription> > DiscoverReturnToEndpointsAsync(IHostFactories hostFactories, bool allowRedirects, CancellationToken cancellationToken)
        {
            XrdsDocument xrds = await this.DiscoverAsync(hostFactories, allowRedirects, cancellationToken);

            if (xrds != null)
            {
                return(xrds.FindRelyingPartyReceivingEndpoints());
            }

            return(null);
        }
        private static void ValidateXmlDSig(XrdsDocument document, UriIdentifier identifier, IncomingWebResponse response, string signingHost)
        {
            Requires.NotNull(document, "document");
            Requires.NotNull(identifier, "identifier");
            Requires.NotNull(response, "response");

            var signatureNode = document.Node.SelectSingleNode("/xrds:XRDS/ds:Signature", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(signatureNode != null, OpenIdStrings.MissingElement, "Signature");
            var signedInfoNode = signatureNode.SelectSingleNode("ds:SignedInfo", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(signedInfoNode != null, OpenIdStrings.MissingElement, "SignedInfo");
            ErrorUtilities.VerifyProtocol(
                signedInfoNode.SelectSingleNode("ds:CanonicalizationMethod[@Algorithm='http://docs.oasis-open.org/xri/xrd/2009/01#canonicalize-raw-octets']", document.XmlNamespaceResolver) != null,
                OpenIdStrings.UnsupportedCanonicalizationMethod);
            ErrorUtilities.VerifyProtocol(
                signedInfoNode.SelectSingleNode("ds:SignatureMethod[@Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1']", document.XmlNamespaceResolver) != null,
                OpenIdStrings.UnsupportedSignatureMethod);
            var certNodes = signatureNode.Select("ds:KeyInfo/ds:X509Data/ds:X509Certificate", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(certNodes.Count > 0, OpenIdStrings.MissingElement, "X509Certificate");
            var certs = certNodes.Cast <XPathNavigator>().Select(n => new X509Certificate2(Convert.FromBase64String(n.Value.Trim()))).ToList();

            // Verify that we trust the signer of the certificates.
            // Start by trying to validate just the certificate used to sign the XRDS document,
            // since we can do that with partial trust.
            Logger.OpenId.Debug("Verifying that we trust the certificate used to sign the discovery document.");
            if (!certs[0].Verify())
            {
                // We couldn't verify just the signing certificate, so try to verify the whole certificate chain.
                try {
                    Logger.OpenId.Debug("Verifying the whole certificate chain.");
                    VerifyCertChain(certs);
                    Logger.OpenId.Debug("Certificate chain verified.");
                } catch (SecurityException) {
                    Logger.Yadis.Warn("Signing certificate verification failed and we have insufficient code access security permissions to perform certificate chain validation.");
                    ErrorUtilities.ThrowProtocol(OpenIdStrings.X509CertificateNotTrusted);
                }
            }

            // Verify that the certificate is issued to the host on whom we are performing discovery.
            string hostName = certs[0].GetNameInfo(X509NameType.DnsName, false);

            ErrorUtilities.VerifyProtocol(string.Equals(hostName, signingHost, StringComparison.OrdinalIgnoreCase), OpenIdStrings.MisdirectedSigningCertificate, hostName, signingHost);

            // Verify the signature itself
            byte[] signature = Convert.FromBase64String(response.Headers["Signature"]);
            var    provider  = (RSACryptoServiceProvider)certs.First().PublicKey.Key;

            byte[] data = new byte[response.ResponseStream.Length];
            response.ResponseStream.Seek(0, SeekOrigin.Begin);
            response.ResponseStream.Read(data, 0, data.Length);
            ErrorUtilities.VerifyProtocol(provider.VerifyData(data, "SHA1", signature), OpenIdStrings.InvalidDSig);
        }
        /// <summary>
        /// Downloads the XRDS document for this XRI.
        /// </summary>
        /// <param name="identifier">The identifier.</param>
        /// <param name="requestHandler">The request handler.</param>
        /// <returns>The XRDS document.</returns>
        private static XrdsDocument DownloadXrds(XriIdentifier identifier, IDirectWebRequestHandler requestHandler)
        {
            Requires.NotNull(identifier, "identifier");
            Requires.NotNull(requestHandler, "requestHandler");
            XrdsDocument doc;

            using (var xrdsResponse = Yadis.Request(requestHandler, GetXrdsUrl(identifier), identifier.IsDiscoverySecureEndToEnd)) {
                var readerSettings = MessagingUtilities.CreateUntrustedXmlReaderSettings();
                doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream, readerSettings));
            }
            ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed);
            return(doc);
        }
        /// <summary>
        /// Downloads the XRDS document for this XRI.
        /// </summary>
        /// <param name="identifier">The identifier.</param>
        /// <param name="requestHandler">The request handler.</param>
        /// <returns>The XRDS document.</returns>
        private static XrdsDocument DownloadXrds(XriIdentifier identifier, IDirectWebRequestHandler requestHandler)
        {
            Requires.NotNull(identifier, "identifier");
            Requires.NotNull(requestHandler, "requestHandler");
            Contract.Ensures(Contract.Result <XrdsDocument>() != null);
            XrdsDocument doc;

            using (var xrdsResponse = Yadis.Request(requestHandler, GetXrdsUrl(identifier), identifier.IsDiscoverySecureEndToEnd)) {
                doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream));
            }
            ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed);
            return(doc);
        }
        internal override IEnumerable <ServiceEndpoint> Discover()
        {
            List <ServiceEndpoint> endpoints = new List <ServiceEndpoint>();
            // Attempt YADIS discovery
            DiscoveryResult yadisResult = Yadis.Yadis.Discover(this, IsDiscoverySecureEndToEnd);

            if (yadisResult != null)
            {
                if (yadisResult.IsXrds)
                {
                    try {
                        XrdsDocument xrds          = new XrdsDocument(yadisResult.ResponseText);
                        var          xrdsEndpoints = xrds.CreateServiceEndpoints(yadisResult.NormalizedUri);
                        // Filter out insecure endpoints if high security is required.
                        if (IsDiscoverySecureEndToEnd)
                        {
                            xrdsEndpoints = Util.Where(xrdsEndpoints, se => se.IsSecure);
                        }
                        endpoints.AddRange(xrdsEndpoints);
                    } catch (XmlException ex) {
                        //Logger.Error("Error while parsing the XRDS document.  Falling back to HTML discovery.", ex);
                    }
                }
                // Failing YADIS discovery of an XRDS document, we try HTML discovery.
                if (endpoints.Count == 0)
                {
                    var htmlEndpoints = new List <ServiceEndpoint>(DiscoverFromHtml(yadisResult.NormalizedUri, yadisResult.ResponseText));
                    if (htmlEndpoints.Count > 0)
                    {
                        //Logger.DebugFormat("Total services discovered in HTML: {0}", htmlEndpoints.Count);
                        //Logger.Debug(Util.ToString(htmlEndpoints, true));
                        endpoints.AddRange(Util.Where(htmlEndpoints, ep => !IsDiscoverySecureEndToEnd || ep.IsSecure));
                        if (endpoints.Count == 0)
                        {
                            //Logger.Info("No HTML discovered endpoints met the security requirements.");
                        }
                    }
                    else
                    {
                        //Logger.Debug("HTML discovery failed to find any endpoints.");
                    }
                }
                else
                {
                    //Logger.Debug("Skipping HTML discovery because XRDS contained service endpoints.");
                }
            }
            return(endpoints);
        }
Exemple #16
0
        /// <summary>
        /// Performs discovery on the Identifier.
        /// </summary>
        /// <param name="requestHandler">The web request handler to use for discovery.</param>
        /// <returns>
        /// An initialized structure containing the discovered provider endpoint information.
        /// </returns>
        internal override IEnumerable <ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler)
        {
            List <ServiceEndpoint> endpoints = new List <ServiceEndpoint>();

            // Attempt YADIS discovery
            DiscoveryResult yadisResult = Yadis.Discover(requestHandler, this, IsDiscoverySecureEndToEnd);

            if (yadisResult != null)
            {
                if (yadisResult.IsXrds)
                {
                    XrdsDocument xrds          = new XrdsDocument(yadisResult.ResponseText);
                    var          xrdsEndpoints = xrds.CreateServiceEndpoints(yadisResult.NormalizedUri, this);

                    // Filter out insecure endpoints if high security is required.
                    if (IsDiscoverySecureEndToEnd)
                    {
                        xrdsEndpoints = xrdsEndpoints.Where(se => se.IsSecure);
                    }
                    endpoints.AddRange(xrdsEndpoints);
                }

                // Failing YADIS discovery of an XRDS document, we try HTML discovery.
                if (endpoints.Count == 0)
                {
                    var htmlEndpoints = new List <ServiceEndpoint>(DiscoverFromHtml(yadisResult.NormalizedUri, this, yadisResult.ResponseText));
                    if (htmlEndpoints.Any())
                    {
                        Logger.DebugFormat("Total services discovered in HTML: {0}", htmlEndpoints.Count);
                        Logger.Debug(htmlEndpoints.ToStringDeferred(true));
                        endpoints.AddRange(htmlEndpoints.Where(ep => !IsDiscoverySecureEndToEnd || ep.IsSecure));
                        if (endpoints.Count == 0)
                        {
                            Logger.Info("No HTML discovered endpoints met the security requirements.");
                        }
                    }
                    else
                    {
                        Logger.Debug("HTML discovery failed to find any endpoints.");
                    }
                }
                else
                {
                    Logger.Debug("Skipping HTML discovery because XRDS contained service endpoints.");
                }
            }
            return(endpoints);
        }
Exemple #17
0
        internal override IEnumerable <ServiceEndpoint> Discover()
        {
            List <ServiceEndpoint> endpoints = new List <ServiceEndpoint>();
            // Attempt YADIS discovery
            DiscoveryResult yadisResult = Yadis.Yadis.Discover(this, IsDiscoverySecureEndToEnd);

            if (yadisResult != null)
            {
                if (yadisResult.IsXrds)
                {
                    XrdsDocument xrds          = new XrdsDocument(yadisResult.ResponseText);
                    var          xrdsEndpoints = xrds.CreateServiceEndpoints(yadisResult.NormalizedUri);
                    // Filter out insecure endpoints if high security is required.
                    if (IsDiscoverySecureEndToEnd)
                    {
                        xrdsEndpoints = Util.Where(xrdsEndpoints, se => se.IsSecure);
                    }
                    endpoints.AddRange(xrdsEndpoints);
                }
                // Failing YADIS discovery of an XRDS document, we try HTML discovery.
                if (endpoints.Count == 0)
                {
                    ServiceEndpoint ep = DiscoverFromHtml(yadisResult.NormalizedUri, yadisResult.ResponseText);
                    if (ep != null)
                    {
                        Logger.Debug("HTML discovery found a service endpoint.");
                        Logger.Debug(ep);
                        if (!IsDiscoverySecureEndToEnd || ep.IsSecure)
                        {
                            endpoints.Add(ep);
                        }
                        else
                        {
                            Logger.Info("Skipping HTML discovered endpoint because it is not secure.");
                        }
                    }
                    else
                    {
                        Logger.Debug("HTML discovery failed to find any endpoints.");
                    }
                }
                else
                {
                    Logger.Debug("Skipping HTML discovery because XRDS contained service endpoints.");
                }
            }
            return(endpoints);
        }
        /// <summary>
        /// Performs discovery on the specified identifier.
        /// </summary>
        /// <param name="identifier">The identifier to perform discovery on.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// A sequence of service endpoints yielded by discovery.  Must not be null, but may be empty.
        /// </returns>
        public async Task <IdentifierDiscoveryServiceResult> DiscoverAsync(Identifier identifier, CancellationToken cancellationToken)
        {
            Requires.NotNull(identifier, "identifier");
            Verify.Operation(this.HostFactories != null, Strings.HostFactoriesRequired);
            cancellationToken.ThrowIfCancellationRequested();

            // Google Apps are always URIs -- not XRIs.
            var uriIdentifier = identifier as UriIdentifier;

            if (uriIdentifier == null)
            {
                return(new IdentifierDiscoveryServiceResult(Enumerable.Empty <IdentifierDiscoveryResult>()));
            }

            var results = new List <IdentifierDiscoveryResult>();

            using (var response = await this.GetXrdsResponseAsync(uriIdentifier, cancellationToken)) {
                if (response.Result != null)
                {
                    try {
                        var readerSettings = MessagingUtilities.CreateUntrustedXmlReaderSettings();
                        var responseStream = await response.Result.Content.ReadAsStreamAsync();

                        var document = new XrdsDocument(XmlReader.Create(responseStream, readerSettings));
                        await ValidateXmlDSigAsync(document, uriIdentifier, response.Result, response.SigningHost);

                        var xrds = GetXrdElements(document, uriIdentifier.Uri.Host);

                        // Look for claimed identifier template URIs for an additional XRDS document.
                        results.AddRange(await this.GetExternalServicesAsync(xrds, uriIdentifier, cancellationToken));

                        // If we couldn't find any claimed identifiers, look for OP identifiers.
                        // Normally this would be the opposite (OP Identifiers take precedence over
                        // claimed identifiers, but for Google Apps, XRDS' always have OP Identifiers
                        // mixed in, which the OpenID spec mandate should eclipse Claimed Identifiers,
                        // which would break positive assertion checks).
                        if (results.Count == 0)
                        {
                            results.AddRange(xrds.CreateServiceEndpoints(uriIdentifier, uriIdentifier));
                        }
                    } catch (XmlException ex) {
                        Logger.Yadis.ErrorFormat("Error while parsing XRDS document at {0} pointed to by host-meta: {1}", response.Result.RequestMessage.RequestUri, ex);
                    }
                }
            }

            return(new IdentifierDiscoveryServiceResult(results, abortDiscoveryChain: true));
        }
Exemple #19
0
        /// <summary>
        /// Creates the service endpoints described in this document, useful for requesting
        /// authentication of one of the OpenID Providers that result from it.
        /// </summary>
        /// <param name="xrds">The XrdsDocument instance to use in this process.</param>
        /// <param name="userSuppliedIdentifier">The user-supplied i-name that was used to discover this XRDS document.</param>
        /// <returns>A sequence of OpenID Providers that can assert ownership of the canonical ID given in this document.</returns>
        internal static IEnumerable <ServiceEndpoint> CreateServiceEndpoints(this XrdsDocument xrds, XriIdentifier userSuppliedIdentifier)
        {
            var endpoints = new List <ServiceEndpoint>();

            endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(userSuppliedIdentifier));

            // If any OP Identifier service elements were found, we must not proceed
            // to return any Claimed Identifier services.
            if (endpoints.Count == 0)
            {
                endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(userSuppliedIdentifier));
            }
            Logger.DebugFormat("Total services discovered in XRDS: {0}", endpoints.Count);
            Logger.Debug(endpoints.ToStringDeferred(true));
            return(endpoints);
        }
Exemple #20
0
        /// <summary>
        /// Gets the URL of the RP icon for the OP to display.
        /// </summary>
        /// <param name="realm">The realm of the RP where the authentication request originated.</param>
        /// <param name="hostFactories">The host factories.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// A sequence of the RP's icons it has available for the Provider to display, in decreasing preferred order.
        /// </returns>
        /// <value>The icon URL.</value>
        /// <remarks>
        /// This property is automatically set for the OP with the result of RP discovery.
        /// RPs should set this value by including an entry such as this in their XRDS document.
        /// <example>
        /// &lt;Service xmlns="xri://$xrd*($v*2.0)"&gt;
        /// &lt;Type&gt;http://specs.openid.net/extensions/ui/icon&lt;/Type&gt;
        /// &lt;URI&gt;http://consumer.example.com/images/image.jpg&lt;/URI&gt;
        /// &lt;/Service&gt;
        /// </example>
        /// </remarks>
        public static async Task <IEnumerable <Uri> > GetRelyingPartyIconUrlsAsync(Realm realm, IHostFactories hostFactories, CancellationToken cancellationToken)
        {
            Requires.NotNull(realm, "realm");
            Requires.NotNull(hostFactories, "hostFactories");

            XrdsDocument xrds = await realm.DiscoverAsync(hostFactories, false, cancellationToken);

            if (xrds == null)
            {
                return(Enumerable.Empty <Uri>());
            }
            else
            {
                return(xrds.FindRelyingPartyIcons());
            }
        }
Exemple #21
0
        /// <summary>
        /// Performs discovery on the specified identifier.
        /// </summary>
        /// <param name="identifier">The identifier to perform discovery on.</param>
        /// <param name="requestHandler">The means to place outgoing HTTP requests.</param>
        /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param>
        /// <returns>
        /// A sequence of service endpoints yielded by discovery.  Must not be null, but may be empty.
        /// </returns>
        public IEnumerable <IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain)
        {
            abortDiscoveryChain = false;

            // Google Apps are always URIs -- not XRIs.
            var uriIdentifier = identifier as UriIdentifier;

            if (uriIdentifier == null)
            {
                return(Enumerable.Empty <IdentifierDiscoveryResult>());
            }

            var    results = new List <IdentifierDiscoveryResult>();
            string signingHost;

            using (var response = GetXrdsResponse(uriIdentifier, requestHandler, out signingHost)) {
                if (response != null)
                {
                    try {
                        var readerSettings = MessagingUtilities.CreateUntrustedXmlReaderSettings();
                        var document       = new XrdsDocument(XmlReader.Create(response.ResponseStream, readerSettings));
                        ValidateXmlDSig(document, uriIdentifier, response, signingHost);
                        var xrds = GetXrdElements(document, uriIdentifier.Uri.Host);

                        // Look for claimed identifier template URIs for an additional XRDS document.
                        results.AddRange(GetExternalServices(xrds, uriIdentifier, requestHandler));

                        // If we couldn't find any claimed identifiers, look for OP identifiers.
                        // Normally this would be the opposite (OP Identifiers take precedence over
                        // claimed identifiers, but for Google Apps, XRDS' always have OP Identifiers
                        // mixed in, which the OpenID spec mandate should eclipse Claimed Identifiers,
                        // which would break positive assertion checks).
                        if (results.Count == 0)
                        {
                            results.AddRange(xrds.CreateServiceEndpoints(uriIdentifier, uriIdentifier));
                        }

                        abortDiscoveryChain = true;
                    } catch (XmlException ex) {
                        Logger.Yadis.ErrorFormat("Error while parsing XRDS document at {0} pointed to by host-meta: {1}", response.FinalUri, ex);
                    }
                }
            }

            return(results);
        }
Exemple #22
0
        public void CanGetYadisDocument()
        {
            YadisRequest request  = new YadisRequest(Utility.GetTestUri("yadisexamples/index"));
            XrdsDocument document = request.GetYadisDocument();

            Assert.IsNotNull(document);
            Assert.IsNotNull(document.XrdElements);
            Assert.AreEqual(1, document.XrdElements.Count);
            Assert.IsNotNull(document.XrdElements[0].Services);
            Assert.AreEqual(3, document.XrdElements[0].Services.Count);
            Assert.IsNotNull(document.XrdElements[0].Services[0].Types);
            Assert.AreEqual(5, document.XrdElements[0].Services[0].Types.Count);
            Assert.AreEqual("http://specs.openid.net/auth/2.0/signon", document.XrdElements[0].Services[0].Types[0]);
            Assert.AreEqual("http://jornwildt.myopenid.com/", document.XrdElements[0].Services[0].LocalID);
            Assert.IsNotNull(document.XrdElements[0].Services[0].Uris);
            Assert.AreEqual(1, document.XrdElements[0].Services[0].Uris.Count);
            Assert.AreEqual("http://www.myopenid.com/server", document.XrdElements[0].Services[0].Uris[0].Uri);
        }
Exemple #23
0
        /// <summary>
        /// Gets the URL of the RP icon for the OP to display.
        /// </summary>
        /// <param name="realm">The realm of the RP where the authentication request originated.</param>
        /// <param name="webRequestHandler">The web request handler to use for discovery.
        /// Usually available via <see cref="Channel.WebRequestHandler">OpenIdProvider.Channel.WebRequestHandler</see>.</param>
        /// <returns>
        /// A sequence of the RP's icons it has available for the Provider to display, in decreasing preferred order.
        /// </returns>
        /// <value>The icon URL.</value>
        /// <remarks>
        /// This property is automatically set for the OP with the result of RP discovery.
        /// RPs should set this value by including an entry such as this in their XRDS document.
        /// <example>
        /// &lt;Service xmlns="xri://$xrd*($v*2.0)"&gt;
        /// &lt;Type&gt;http://specs.openid.net/extensions/ui/icon&lt;/Type&gt;
        /// &lt;URI&gt;http://consumer.example.com/images/image.jpg&lt;/URI&gt;
        /// &lt;/Service&gt;
        /// </example>
        /// </remarks>
        public static IEnumerable <Uri> GetRelyingPartyIconUrls(Realm realm, IDirectWebRequestHandler webRequestHandler)
        {
            Contract.Requires(realm != null);
            Contract.Requires(webRequestHandler != null);
            ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
            ErrorUtilities.VerifyArgumentNotNull(webRequestHandler, "webRequestHandler");

            XrdsDocument xrds = realm.Discover(webRequestHandler, false);

            if (xrds == null)
            {
                return(Enumerable.Empty <Uri>());
            }
            else
            {
                return(xrds.FindRelyingPartyIcons());
            }
        }
        private static async Task ValidateXmlDSigAsync(XrdsDocument document, UriIdentifier identifier, HttpResponseMessage response, string signingHost)
        {
            Requires.NotNull(document, "document");
            Requires.NotNull(identifier, "identifier");
            Requires.NotNull(response, "response");

            var signatureNode = document.Node.SelectSingleNode("/xrds:XRDS/ds:Signature", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(signatureNode != null, OpenIdStrings.MissingElement, "Signature");
            var signedInfoNode = signatureNode.SelectSingleNode("ds:SignedInfo", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(signedInfoNode != null, OpenIdStrings.MissingElement, "SignedInfo");
            ErrorUtilities.VerifyProtocol(
                signedInfoNode.SelectSingleNode("ds:CanonicalizationMethod[@Algorithm='http://docs.oasis-open.org/xri/xrd/2009/01#canonicalize-raw-octets']", document.XmlNamespaceResolver) != null,
                OpenIdStrings.UnsupportedCanonicalizationMethod);
            ErrorUtilities.VerifyProtocol(
                signedInfoNode.SelectSingleNode("ds:SignatureMethod[@Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1']", document.XmlNamespaceResolver) != null,
                OpenIdStrings.UnsupportedSignatureMethod);
            var certNodes = signatureNode.Select("ds:KeyInfo/ds:X509Data/ds:X509Certificate", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(certNodes.Count > 0, OpenIdStrings.MissingElement, "X509Certificate");
            var certs = certNodes.Cast <XPathNavigator>().Select(n => new X509Certificate2(Convert.FromBase64String(n.Value.Trim()))).ToList();

            VerifyCertificateChain(certs);

            // Verify that the certificate is issued to the host on whom we are performing discovery.
            string hostName = certs[0].GetNameInfo(X509NameType.DnsName, false);

            ErrorUtilities.VerifyProtocol(string.Equals(hostName, signingHost, StringComparison.OrdinalIgnoreCase), OpenIdStrings.MisdirectedSigningCertificate, hostName, signingHost);

            // Verify the signature itself
            byte[] signature      = Convert.FromBase64String(response.Headers.GetValues("Signature").First());
            var    provider       = (RSACryptoServiceProvider)certs.First().PublicKey.Key;
            var    responseStream = await response.Content.ReadAsStreamAsync();

            byte[] data = new byte[responseStream.Length];
            responseStream.Seek(0, SeekOrigin.Begin);
            await responseStream.ReadAsync(data, 0, data.Length);

            ErrorUtilities.VerifyProtocol(provider.VerifyData(data, "SHA1", signature), OpenIdStrings.InvalidDSig);
        }
        /// <summary>
        /// Downloads the XRDS document for this XRI.
        /// </summary>
        /// <param name="identifier">The identifier.</param>
        /// <param name="hostFactories">The host factories.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// The XRDS document.
        /// </returns>
        private static async Task <XrdsDocument> DownloadXrdsAsync(XriIdentifier identifier, IHostFactories hostFactories, CancellationToken cancellationToken)
        {
            Requires.NotNull(identifier, "identifier");
            Requires.NotNull(hostFactories, "hostFactories");

            XrdsDocument doc;

            using (var xrdsResponse = await Yadis.RequestAsync(GetXrdsUrl(identifier), identifier.IsDiscoverySecureEndToEnd, hostFactories, cancellationToken)) {
                xrdsResponse.EnsureSuccessStatusCode();
                var readerSettings = MessagingUtilities.CreateUntrustedXmlReaderSettings();
                ErrorUtilities.VerifyProtocol(xrdsResponse.Content != null, "XRDS request \"{0}\" returned no response.", GetXrdsUrl(identifier));
                await xrdsResponse.Content.LoadIntoBufferAsync();

                using (var xrdsStream = await xrdsResponse.Content.ReadAsStreamAsync()) {
                    doc = new XrdsDocument(XmlReader.Create(xrdsStream, readerSettings));
                }
            }

            ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed);
            return(doc);
        }
Exemple #26
0
        /// <summary>
        /// Searches for an XRDS document at the realm URL, and if found, searches
        /// for a description of a relying party endpoints (OpenId login pages).
        /// </summary>
        /// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param>
        /// <param name="allowRedirects">Whether redirects may be followed when discovering the Realm.
        /// This may be true when creating an unsolicited assertion, but must be
        /// false when performing return URL verification per 2.0 spec section 9.2.1.</param>
        /// <returns>
        /// The details of the endpoints if found, otherwise null.
        /// </returns>
        internal IEnumerable <RelyingPartyEndpointDescription> Discover(IDirectWebRequestHandler requestHandler, bool allowRedirects)
        {
            // Attempt YADIS discovery
            DiscoveryResult yadisResult = Yadis.Discover(requestHandler, this.UriWithWildcardChangedToWww, false);

            if (yadisResult != null)
            {
                // Detect disallowed redirects, since realm discovery never allows them for security.
                ErrorUtilities.VerifyProtocol(allowRedirects || yadisResult.NormalizedUri == yadisResult.RequestUri, OpenIdStrings.RealmCausedRedirectUponDiscovery, yadisResult.RequestUri);
                if (yadisResult.IsXrds)
                {
                    try {
                        XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText);
                        return(xrds.FindRelyingPartyReceivingEndpoints());
                    } catch (XmlException ex) {
                        throw ErrorUtilities.Wrap(ex, XrdsStrings.InvalidXRDSDocument);
                    }
                }
            }
            return(Enumerable.Empty <RelyingPartyEndpointDescription>());
        }
        /// <summary>
        /// Gets the services for an identifier that are described by an external XRDS document.
        /// </summary>
        /// <param name="xrds">The XRD elements to search for described-by services.</param>
        /// <param name="identifier">The identifier under discovery.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// The discovered services.
        /// </returns>
        private async Task <IEnumerable <IdentifierDiscoveryResult> > GetExternalServicesAsync(IEnumerable <XrdElement> xrds, UriIdentifier identifier, CancellationToken cancellationToken)
        {
            Requires.NotNull(xrds, "xrds");
            Requires.NotNull(identifier, "identifier");

            var results = new List <IdentifierDiscoveryResult>();

            foreach (var serviceElement in GetDescribedByServices(xrds))
            {
                var templateNode      = serviceElement.Node.SelectSingleNode("google:URITemplate", serviceElement.XmlNamespaceResolver);
                var nextAuthorityNode = serviceElement.Node.SelectSingleNode("google:NextAuthority", serviceElement.XmlNamespaceResolver);
                if (templateNode != null)
                {
                    Uri    externalLocation = new Uri(templateNode.Value.Trim().Replace("{%uri}", Uri.EscapeDataString(identifier.Uri.AbsoluteUri)));
                    string nextAuthority    = nextAuthorityNode != null?nextAuthorityNode.Value.Trim() : identifier.Uri.Host;

                    try {
                        using (var externalXrdsResponse = await this.GetXrdsResponseAsync(identifier, externalLocation, cancellationToken)) {
                            var readerSettings = MessagingUtilities.CreateUntrustedXmlReaderSettings();
                            var responseStream = await externalXrdsResponse.Content.ReadAsStreamAsync();

                            XrdsDocument externalXrds = new XrdsDocument(XmlReader.Create(responseStream, readerSettings));
                            await ValidateXmlDSigAsync(externalXrds, identifier, externalXrdsResponse, nextAuthority);

                            results.AddRange(GetXrdElements(externalXrds, identifier).CreateServiceEndpoints(identifier, identifier));
                        }
                    } catch (ProtocolException ex) {
                        Logger.Yadis.WarnFormat("HTTP GET error while retrieving described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
                    } catch (XmlException ex) {
                        Logger.Yadis.ErrorFormat("Error while parsing described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
                    }
                }
            }

            return(results);
        }
 /// <summary>
 /// Gets the XRD elements that have a given CanonicalID.
 /// </summary>
 /// <param name="document">The XRDS document.</param>
 /// <param name="canonicalId">The CanonicalID to match on.</param>
 /// <returns>A sequence of XRD elements.</returns>
 private static IEnumerable <XrdElement> GetXrdElements(XrdsDocument document, string canonicalId)
 {
     // filter to include only those XRD elements describing the host whose host-meta pointed us to this document.
     return(document.XrdElements.Where(xrd => string.Equals(xrd.CanonicalID, canonicalId, StringComparison.Ordinal)));
 }
Exemple #29
0
 /// <summary>
 /// Generates OpenID Providers that can authenticate using directed identity.
 /// </summary>
 /// <param name="xrds">The XrdsDocument instance to use in this process.</param>
 /// <param name="opIdentifier">The OP Identifier entered (and resolved) by the user.</param>
 /// <returns>A sequence of the providers that can offer directed identity services.</returns>
 private static IEnumerable <ServiceEndpoint> GenerateOPIdentifierServiceEndpoints(this XrdsDocument xrds, Identifier opIdentifier)
 {
     return(from service in xrds.FindOPIdentifierServices()
            from uri in service.UriElements
            let protocol = Protocol.FindBestVersion(p => p.OPIdentifierServiceTypeURI, service.TypeElementUris)
                           let providerDescription = new ProviderEndpointDescription(uri.Uri, service.TypeElementUris)
                                                     select ServiceEndpoint.CreateForProviderIdentifier(opIdentifier, providerDescription, service.Priority, uri.Priority));
 }
Exemple #30
0
        /// <summary>
        /// Finds the Relying Party return_to receiving endpoints.
        /// </summary>
        /// <param name="xrds">The XrdsDocument instance to use in this process.</param>
        /// <returns>A sequence of Relying Party descriptors for the return_to endpoints.</returns>
        /// <remarks>
        /// This is useful for Providers to send unsolicited assertions to Relying Parties,
        /// or for Provider's to perform RP discovery/verification as part of authentication.
        /// </remarks>
        internal static IEnumerable <RelyingPartyEndpointDescription> FindRelyingPartyReceivingEndpoints(this XrdsDocument xrds)
        {
            Requires.NotNull(xrds, "xrds");

            return(from service in xrds.FindReturnToServices()
                   from uri in service.UriElements
                   select new RelyingPartyEndpointDescription(uri.Uri, service.TypeElementUris));
        }