Windows Service C#: Accessing Mail. The Autodiscover service couldn't be located - c#

I have tried both the methods below but both return the result "The Autodiscover service couldn't be located."
http://msdn.microsoft.com/en-us/library/gg591267(v=EXCHG.140).aspx
service.Credentials = new NetworkCredential(userData.EmailAddress, userData.Password);
if (userData.AutodiscoverUrl == null)
{
service.AutodiscoverUrl(userData.EmailAddress, RedirectionUrlValidationCallback);
userData.AutodiscoverUrl = service.Url;
}
else
{
service.Url = userData.AutodiscoverUrl;
}
return service;
}
http://code.msdn.microsoft.com/Exchange-2013-Set-pull-14c8360b#content
static ExchangeService GetBinding()
{
// Create the binding.
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP1);
// Define credentials.
service.Credentials = new WebCredentials("myemail#work.com", "password");
// Use the AutodiscoverUrl method to locate the service endpoint.
try
{
service.AutodiscoverUrl("myemail#work.com", RedirectionUrlValidationCallback);
}
catch (AutodiscoverRemoteException ex)
{
Console.WriteLine("Exception thrown: " + ex.Error.Message);
}
// Display the service URL.
Console.WriteLine("AutodiscoverURL: " + service.Url);
return service;
}
In the one instance I enter my email and password, in the other its hard-coded. Both hang when attempting Autodiscoverurl and eventually fail with the message "The Autodiscover service couldn't be located." I added the references as per the tutorials and Autodiscover appears under Microsoft.Exchange.WebServices.dll ... Is there something else I'm missing?

try adding:
service.UseDefaultCredentials = false;
after you set the credentials

I don't know if you have resolved it yet, I bumped into your post as I was searching for a solution for the same (or similar?) problem.
The peculiar thing was:
Since it worked perfectly in Debug mode, but not once installed as Windows service, I changed the 'Log On' settings in the Windows Service Properties (services.msc, right click on installed service, Properties, Log On Tab) and set it to 'Local System account' and checked the 'Allow service to interact with desktop' option.
That did the trick for me.

Related

How to validate Exchange credentials and verify Exchange server communication using EWS managed API?

Using Exchange EWS managed API 2.0, here is a method which I'm using to validate credentials:
public static bool TestExchangeConnetion(UserSettingDTO credential, ServerSettingDTO serverSetting)
{
bool authenticated = false;
Microsoft.Exchange.WebServices.Data.ExchangeService service;
service = new ExchangeService((ExchangeVersion)Enum.Parse(typeof(ExchangeVersion), serverSetting.ExchangeVer));
service.Url = new Uri(serverSetting.ExchangeURL);
service.Credentials = new NetworkCredential(credential.ExchangeUsername, credential.ExchangePassword);
try
{
//make a call to ensure that credentials are working
AlternateIdBase response = service.ConvertId(new AlternateId(IdFormat.EwsId, "Placeholder", credential.ExchangeUsername), IdFormat.EwsId);
}
// The credentials were authenticated. We expect this exception since we are providing intentional bad data for ConvertId
catch (ServiceResponseException)
{
authenticated = true;
}
// The credentials were not authenticated.
catch (ServiceRequestException)
{
authenticated = false;
}
catch (Exception)
{
authenticated = false;
}
return authenticated;
}
This works absolutely fine to validate credentials but I'm looking for a way to differentiate between invalid credentials and Exchange server downtimes. This code returns false for both.
Is it possible to find out if there is an issue communicating to the server (like server downtimes)? The reason for this is I want to notify the user about invalid credentials not server downtimes!
EWS is just a Web-service that runs on IIS (so IIS is looking after authentication) if you just want to check the credentials why don't you just make a Get request against the services.wsdl file or a Get on the Autodiscover endpoint on the CAS server. If the credentails are incorrect that should return a 401 at that point.
Cheers
Glen

EWS exception : "Connection failed. Try later."

Every time I try to call the server, I get an error code : ErrorConnectionFailed with Connection failed. Try later. message.
I suspect that it comes from the fact that the credentials of service are empty. Although I have no idea why. If I create the credentials manually using my windows account login and password, it works fine : new WebCredentials(login, password, domain);
I have a console program that works fine (see below), but it does not on a web site.
static void Main(string[] args)
{
var service = GetContextualService(email);
EmailMessage email = EmailMessage.Bind(service, new ItemId(validEmailId));
Console.ReadKey();
}
private static ExchangeService GetContextualService(string email)
{
ExchangeService service = new ExchangeService();
// I don't even need credentials on a Console program to make it work
//service.Credentials = new WebCredentials(CredentialCache.DefaultCredentials);
//service.UseDefaultCredentials = true;
service.AutodiscoverUrl(email, RedirectionUrlValidationCallback);
return service;
}
private static bool RedirectionUrlValidationCallback(string redirectionUrl)
{
// The default for the validation callback is to reject the URL.
bool result = false;
Uri redirectionUri = new Uri(redirectionUrl);
// Validate the contents of the redirection URL. In this simple validation
// callback, the redirection URL is considered valid if it is using HTTPS
// to encrypt the authentication credentials.
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}
While using on a website even with new WebCredentials(CredentialCache.DefaultCredentials);, it returns an exception. (see below)
private ExchangeService GetContextualService(string email)
{
ExchangeService service = new ExchangeService();
service.Credentials = new WebCredentials(CredentialCache.DefaultCredentials);
//service.UseDefaultCredentials = true;
service.AutodiscoverUrl(email, RedirectionUrlValidationCallback);
return service;
}
[HttpPost]
public List<InternetMessageHeader> GetMailHeader(JObject data)
{
ExchangeService service = GetContextualService(data.GetValue("email").Value<string>());
ItemId id = new ItemId(data.GetValue("mailId").Value<string>());
// EXCEPTION BELOW
EmailMessage email = EmailMessage.Bind(service, id);
return email.InternetMessageHeaders.ToList();
}
Why does any call to EWS returns me an exception ?
Why is it working fine on a console program and not on a web server ?
Any thought is welcome !
Strictly based on the code you posted, all I can say is that when you call AutoDiscoverUrl() it may not be talking to the same server that you need to talk to with EWS. Altho typically AD and EWS are on the CAS, it's possible (I think) to get an EWS URL that points to some other server. I've not been in the code in the EWS Editor in some time, but if it does not call the Managed API, it might do AD slightly differently. I'd suggest calling out the EWS URL before you try the Bind() and seeing if you can paste it into a browser. (It'll redirect you OWA, but the connection will be proven.) I'd also call out the AD URL in the redirection callback so you know who you're talking to. Sorry I can't be of more help.

WCF Rest service Windows authentication via Browser

Given is a wcf rest service which runs with HttpClientCredentialType.Windows and enforces a user to authenticate via kerberos.
private static void Main(string[] args)
{
Type serviceType = typeof (AuthService);
ServiceHost serviceHost = new ServiceHost(serviceType);
WebHttpBinding binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
ServiceEndpoint basicServiceEndPoint = serviceHost.AddServiceEndpoint(typeof(IAuthService), binding, "http://notebook50:87");
basicServiceEndPoint.Behaviors.Add(new WebHttpBehavior());
Console.WriteLine("wcf service started");
serviceHost.Open();
Console.ReadLine();
}
public class AuthService : IAuthService
{
public List<string> GetUserInformation()
{
List<string> userInfo = new List<string>();
userInfo.Add("Environment.User = " + Environment.UserName);
userInfo.Add("Environment.UserDomain = " + Environment.UserDomainName);
if (OperationContext.Current != null && OperationContext.Current.ServiceSecurityContext != null)
{
userInfo.Add("WindowsIdentity = " + OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Name);
userInfo.Add("Auth protocol = " + OperationContext.Current.ServiceSecurityContext.WindowsIdentity.AuthenticationType);
}
else
{
userInfo.Add("WindowsIdentity = empty");
}
WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";
return userInfo;
}
}
[ServiceContract]
public interface IAuthService
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, UriTemplate = "test/")]
List<string> GetUserInformation();
}
When i run this as a console application, and then open the website http://notebook50:87/test/ in internet explorer from another computer, i get a 'bad request' response.
I did enable kerberos logging, and it shows me KDC_ERR_PREAUTH_REQUIRED
I can solve this problem by creating a windows service, and run it under 'Local System account'.
In this case, a client is able to authenticate.
Question: What permission/settings does a user(which runs this wcf service) need in order to get the same behavior as when the application is running as windows service under local system?
Is this related with the Service Principle Name?
It is working now.
It really was a problem with the SPN
At the beginning, I've set the SPN like setpn -A HTTP/notebook50.foo.com, and with this, the kerberos authentication didn't work.
Now, i've set it like setspn -A HTTP/notebook50.foo.com username where username is the user under which the service runs.
From the SPN documentation i've read, it was not clear to me that i have to set the user account in this way.
It would be great if one could explain what happens here, and probably a link to a documentation for this scenario.
You can stop this error pops up via enable the "Do not require Kerberos preauthentication" option for that user account in Active directory users & computers -> properties -> account.

How to work with WCF wsHttpBinding and SSL?

I need to develop a WCF Hosted in a console app WebService.
I made it work using the Mutual Certificate (service and client) method using SecurityMode.Message.
But now i need to change the Security Mode to SecurityMode.Transport and use the wsHttpBinding with SSL. I made this code to host the service but i cannot get the wsdl with the browser, or execute some webmethod in the console app client.
static void Main()
{
var httpsUri = new Uri("https://localhost:8089/HelloServer");
var binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var host = new ServiceHost(typeof(WcfFederationServer.HelloWorld), httpsUri);
host.AddServiceEndpoint(typeof(WcfFederationServer.IHelloWorld), binding, "", httpsUri);
var mex = new ServiceMetadataBehavior();
mex.HttpsGetEnabled = true;
host.Description.Behaviors.Add(mex);
// Open the service.
host.Open();
Console.WriteLine("Listening on {0}...", httpsUri);
Console.ReadLine();
// Close the service.
host.Close();
}
The service is up, but i cannot get nothing on the https://localhost:8089/HelloServer.
On fiddler the get request via browser shows me this message:
fiddler.network.https> HTTPS handshake to localhost failed. System.IO.IOException
What im missing here?
Thanks
EDIT:
The Console Application Client Code
static void Main()
{
try
{
var client = new HelloWorldHttps.HelloWorldClient();
client.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.TrustedPeople,
X509FindType.FindBySubjectName,
"www.client.com");
Console.WriteLine(client.GetData());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
Getting this error:
Could not establish trust relationship for the SSL/TLS secure channel
When it comes to the service, you need to map the certificate to the specific port as described here
http://msdn.microsoft.com/en-us/library/ms733791(v=vs.110).aspx
As for the client, you need to skip the verification of certificate properties like valid date, the domain by relaxing the certificate acceptance policy. An easiest way would be to accept any certiticate
ServicePointManager.ServerCertificateValidationCallback = (a,b,c,d) => true
You can finetune the acceptance callback according to the docs to best fit your needs.

How do I get the WSDL/description so that I can add WebReference in another project?

I know how to add a WebReference in Visual Studio, easy enough.
I also know how to create a normal ASP.NET Web Service project, but that's not what I am doing here.
So, the WebService I have running looks like this:
try
{
if (host != null)
{
host.Close();
host = null;
}
baseAddress = new Uri("http://example.com:8080");
host = new WebServiceHost(typeof(MyProxy), baseAddress);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
host.Opened += new EventHandler(host_Opened);
host.Closed += new EventHandler(host_Closed);
System.ServiceModel.Description.ServiceEndpoint se = host.AddServiceEndpoint(typeof(IMyProxy), new WebHttpBinding(), baseAddress);
se.Behaviors.Add(new System.ServiceModel.Description.WebHttpBehavior());
host.Open();
}
catch (Exception e)
{
}
// .... stuff ....
[ServiceContract]
public interface IMyProxy
{
[OperationContract]
[WebGet(UriTemplate = "GetArea?searchString={searchString}")]
GetAreaResult GetArea(string searchString);
}
// more stuff of course follows here
The problem is that when I try to add a WebReference to the above service in Visual Studio, I get an error.
"Add Service Reference" --> "Add Web Reference"
and in the URL I write my URL, http://example.com:8080
Then I get "Service ... Endpoint not found." and the error message in the Add Web Reference box:
There was an error downloading 'http://example.com:8080/'. The request
failed with HTTP status 404: Not Found. There was an error downloading
'http://example.com:8080/$metadata'. The request failed with HTTP
status 404: Not Found.
If I open up a web browser and go directly to http://example.com:8080/GetArea the service is called/executed as expected.
So to rephrase the problem shorter: The WSDL/description isn't there, so I cannot add a Web Service reference.
The problem here is that the WebServiceHost will remove the functionality that you are trying to achieve when adding the ServiceMetadataBehavior. Looking in dotPeek (reflector) at the WebServiceHost, inside the OnOpening method there is:
ServiceDebugBehavior serviceDebugBehavior = this.Description.Behaviors.Find<ServiceDebugBehavior>();
if (serviceDebugBehavior != null)
{
serviceDebugBehavior.HttpHelpPageEnabled = false;
serviceDebugBehavior.HttpsHelpPageEnabled = false;
}
ServiceMetadataBehavior metadataBehavior = this.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior != null)
{
metadataBehavior.HttpGetEnabled = false;
metadataBehavior.HttpsGetEnabled = false;
}
The WebServiceHost is designed to be used with REST/JSON services which typically have no defined contract, hence why metadata (mex) is disabled.
If you are trying to create a SOAP based service, you need to use a standard ServiceHost. It looks like this is what you want, as you are trying to add the service reference through VS.
If you are trying to create a REST/JSON service, you can use WebServiceHost.
You will need a ServiceMetadataBehavior added to the host refer MSDN Reference.
// Enable Mex
host.Description.Behaviors.Add(new ServiceMetadataBehavior{ HttpGetEnabled = true });
I think you need to create WSDL or disco file for your webservice,For that run your webservice locally ,as you said it is executed fine.In my case when I execute my webservice I get link on the top of my page as "Service Description" .When you will click here you will get your WSDL file in browser.Another way is to add "?wsdl" at the end of your querystring ,You will get your wsdl file.

Categories