How can I add a Basic Auth Header to my SOAP? - c#

I'm trying to send a Authentication header through to a WSDL service that does not have
the authentication requirement specified on the WSDL.
How can I add the Auth header to the call for the web service ?
Code I have been working with:
using System;
using System.Web.Services.Protocols;
namespace TesteSoap
{
class MainClass
{
public static void Main (string[] args)
{
WSDLService Service = new WSDLService();
/* How can I add authentication to the call of this webservice ? */
Service.get();
Console.WriteLine ("Hello World!");
}
}
}

This problem is questioned in other link but in a different way related to the WSDL including the question for user/password.
link
They aren't the same question but the problem is related.
Michaelis.MockService is the Webservice library extracted you may see an example on how to do this in: link Mono project website.
Michaelis.MockService service = new Michaelis.MockService();
// Create the network credentials and assign
// them to the service credentials
NetworkCredential netCredential = new NetworkCredential("Inigo.Montoya", "Ykmfptd");
Uri uri = new Uri(service.Url);
ICredentials credentials = netCredential.GetCredential(uri, "Basic");
service.Credentials = credentials;
// Be sure to set PreAuthenticate to true or else
// authentication will not be sent.
service.PreAuthenticate = true;
// Make the web service call.
service.Method();

Related

C# WCF Service Get Status Code in Client from One way Service

I have a WCF service which has a method named ArchiveFile(string fileName) which basically archives files. I have created a proxy project using svcutil and added its reference created in my client application and is consuming the service as follows:
var binding = new WSHttpBinding { Security = new WSHttpSecurity() { Mode = SecurityMode.None } };
var address = new EndpointAddress(this.TargetUrl);
var fileService = new FileServiceClient(binding, address);'
I want to know how do I determine the Http Status Code (200 - OK or any other) for the WCF Service call.
We can get the http status code through WebOperationContext Class:
WebOperationContext statuscode = WebOperationContext.Current;
Console.WriteLine(statuscode.OutgoingResponse.StatusCode);
For more information about WebOperationContext,please refer to the following link:
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.web.weboperationcontext?view=netframework-4.8

Python Read Emails

My teammate uses the above code in C# (.NET) to extract email details from his inbox. If you notice it does not require any credential.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Exchange.WebServices.Data;
namespace ConsoleApplication1 {
class Program
{
static void Main(string[] args)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.AutodiscoverUrl("FirstName.LastName#company.com", RedirectionUrlValidationCallback);
if (service != null)
{
FindItemsResults<Item> resultout = service.FindItems(WellKnownFolderName.Inbox, new ItemView(10));
foreach (Item item in resultout.Items)
{
EmailMessage message = EmailMessage.Bind(service, item.Id);
String subject = message.Subject.ToString();
Console.Write(subject);
String fromwhom = message.From.Address.ToString();
Console.Write(fromwhom);
}
}
}
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;
}
}
}
I need to perform the same steps but in Python. i.e read email details.
My attempt
from exchangelib import Account, Configuration, Credentials, DELEGATE, IMPERSONATION, NTLM
email = 'FirstName.LastName#company.com'
creds = Credentials(email, "")
account = Account(email, autodiscover=True, credentials = creds)
Error:
AutoDiscoverFailed: All steps in the autodiscover protocol failed
With Autodiscover false
from exchangelib import Account, Configuration, Credentials, DELEGATE, IMPERSONATION, NTLM
email = 'FirstName.LastName#company.com'
creds = Credentials(email, "")
config = Configuration(server = "domain.com", credentials=creds)
account = Account(email, autodiscover=False, config = config)
Error:
Wrong username or password for https;//domain.com/EWS/Exchange.asmx
I can access the https;//domain.com/EWS/Exchange.asmx via url, without entering any credential.
Note: I am fairly good in Python with no knowledge of C#.
I believe there are different ways of approaching authentication in Python; here's one.
I've worked on a similar project, where I try to print out an email [web automation], for which I used selenium and a webdriver to access a yahoo mail. This project included page inspection -- another approach. The code isn't optimised and the final output is not what I intended yet, but you could understand the idea at least.
Good Luck!

Reading WebService Wsdl using c#.Net

We have a SOAP based web service and we are able to read its wsdl when we type in the url in Browser. We sit behind a proxy in our network but its not blocking anything and we are always able to read wsdl using browser.But when we enter the url in Browser say http://ist.services/CoreServices/coreservices?wsdl it asks for username and password which is not same as my windows credentials. So when i enter the username and password shared by the dev team , it returns the wsdl page. Please note that this webservice is developed and deployed on java based server.
How do i do the same in c#.net code and how do i pass the Security Crednetials in DiscoveryClientProtocol? I tried the below code which works for the webservices which doesn't ask for the Security credentials.
// Specify the URL to discover.
string sourceUrl = "http://ist.services/CoreServices/coreservices?wsdl";
string outputDirectory = "C:\\Temp";
DiscoveryClientProtocol client = new DiscoveryClientProtocol();
var credentials = new NetworkCredential("sunuser1", "xxxxxxx", "");
WebProxy proxy = new WebProxy("http://proxy.bingo:8000/", true) { Credentials = credentials };
client.Credentials = credentials;
// Use default credentials to access the URL being discovered.
//client.Credentials = credentials;//CredentialCache.DefaultCredentials;
client.Proxy = proxy;
String DiscoverMode = "DiscoverAny";
String ResolveMode = "ResolveAll";
try
{
DiscoveryDocument doc;
// Check to see if whether the user wanted to read in existing discovery results.
if (DiscoverMode == "ReadAll")
{
DiscoveryClientResultCollection results = client.ReadAll(Path.Combine("C:\\Temp", "results.discomap"));
//SaveMode.Value = "NoSave";
}
else
{
// Check to see if whether the user wants the capability to discover any kind of discoverable document.
if (DiscoverMode == "DiscoverAny")
{
doc = client.DiscoverAny(sourceUrl);
}
else
// Discover only discovery documents, which might contain references to other types of discoverable documents.
{
doc = client.Discover(sourceUrl);
}
// Check to see whether the user wants to resolve all possible references from the supplied URL.
if (ResolveMode == "ResolveAll")
client.ResolveAll();
else
{
// Check to see whether the user wants to resolve references nested more than one level deep.
if (ResolveMode == "ResolveOneLevel")
client.ResolveOneLevel();
else
Console.WriteLine("empty");
}
}
}
catch (Exception e2)
{
//DiscoveryResultsGrid.Columns.Clear();
//Status.Text = e2.Message;
Console.WriteLine(e2.Message);
}
// If documents were discovered, display the results in a data grid.
if (client.Documents.Count > 0)
Console.WriteLine(client);
}
}
Since the code didn't help me much , i opened the fiddler to trace the http calls when i manual read the wsdl in browser and i see it takes the credentials i entered as "Authorization: Basic cGDFDdsfdfsdsfdsgsgfg=" . In fiddler i see three calls with responses 401,302 and 200. But in my c#.net code i don't get the 200 response and it always throws me the 404 error.
I further debugged this and in httpresponse of client object i see the flag status as INVOCATION_FLAGS_INITIALIZED | INVOCATION_FLAGS_NEED_SECURITY
So looks like i need to pass the credentials as Security Credentials rather than Network credentials.
The below code has fixed the issue.
CredentialCache myCredentialCache = new CredentialCache { { new Uri(sourceUrl),
"Basic", networkCredential } };
discoveryClientProtocol.AllowAutoRedirect = true;
discoveryClientProtocol.Credentials = myCredentialCache;

How to get HttpClient to pass credentials along with the request?

I have a web application (hosted in IIS) that talks to a Windows service. The Windows service is using the ASP.Net MVC Web API (self-hosted), and so can be communicated with over http using JSON. The web application is configured to do impersonation, the idea being that the user who makes the request to the web application should be the user that the web application uses to make the request to the service. The structure looks like this:
(The user highlighted in red is the user being referred to in the examples below.)
The web application makes requests to the Windows service using an HttpClient:
var httpClient = new HttpClient(new HttpClientHandler()
{
UseDefaultCredentials = true
});
httpClient.GetStringAsync("http://localhost/some/endpoint/");
This makes the request to the Windows service, but does not pass the credentials over correctly (the service reports the user as IIS APPPOOL\ASP.NET 4.0). This is not what I want to happen.
If I change the above code to use a WebClient instead, the credentials of the user are passed correctly:
WebClient c = new WebClient
{
UseDefaultCredentials = true
};
c.DownloadStringAsync(new Uri("http://localhost/some/endpoint/"));
With the above code, the service reports the user as the user who made the request to the web application.
What am I doing wrong with the HttpClient implementation that is causing it to not pass the credentials correctly (or is it a bug with the HttpClient)?
The reason I want to use the HttpClient is that it has an async API that works well with Tasks, whereas the WebClient's asyc API needs to be handled with events.
You can configure HttpClient to automatically pass credentials like this:
var myClient = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
I was also having this same problem. I developed a synchronous solution thanks to the research done by #tpeczek in the following SO article: Unable to authenticate to ASP.NET Web Api service with HttpClient
My solution uses a WebClient, which as you correctly noted passes the credentials without issue. The reason HttpClient doesn't work is because of Windows security disabling the ability to create new threads under an impersonated account (see SO article above.) HttpClient creates new threads via the Task Factory thus causing the error. WebClient on the other hand, runs synchronously on the same thread thereby bypassing the rule and forwarding its credentials.
Although the code works, the downside is that it will not work async.
var wi = (System.Security.Principal.WindowsIdentity)HttpContext.Current.User.Identity;
var wic = wi.Impersonate();
try
{
var data = JsonConvert.SerializeObject(new
{
Property1 = 1,
Property2 = "blah"
});
using (var client = new WebClient { UseDefaultCredentials = true })
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
}
}
catch (Exception exc)
{
// handle exception
}
finally
{
wic.Undo();
}
Note: Requires NuGet package: Newtonsoft.Json, which is the same JSON serializer WebAPI uses.
What you are trying to do is get NTLM to forward the identity on to the next server, which it cannot do - it can only do impersonation which only gives you access to local resources. It won't let you cross a machine boundary. Kerberos authentication supports delegation (what you need) by using tickets, and the ticket can be forwarded on when all servers and applications in the chain are correctly configured and Kerberos is set up correctly on the domain.
So, in short you need to switch from using NTLM to Kerberos.
For more on Windows Authentication options available to you and how they work start at:
http://msdn.microsoft.com/en-us/library/ff647076.aspx
OK, so thanks to all of the contributors above. I am using .NET 4.6 and we also had the same issue. I spent time debugging System.Net.Http, specifically the HttpClientHandler, and found the following:
if (ExecutionContext.IsFlowSuppressed())
{
IWebProxy webProxy = (IWebProxy) null;
if (this.useProxy)
webProxy = this.proxy ?? WebRequest.DefaultWebProxy;
if (this.UseDefaultCredentials || this.Credentials != null || webProxy != null && webProxy.Credentials != null)
this.SafeCaptureIdenity(state);
}
So after assessing that the ExecutionContext.IsFlowSuppressed() might have been the culprit, I wrapped our Impersonation code as follows:
using (((WindowsIdentity)ExecutionContext.Current.Identity).Impersonate())
using (System.Threading.ExecutionContext.SuppressFlow())
{
// HttpClient code goes here!
}
The code inside of SafeCaptureIdenity (not my spelling mistake), grabs WindowsIdentity.Current() which is our impersonated identity. This is being picked up because we are now suppressing flow. Because of the using/dispose this is reset after invocation.
It now seems to work for us, phew!
In .NET Core, I managed to get a System.Net.Http.HttpClient with UseDefaultCredentials = true to pass through the authenticated user's Windows credentials to a back end service by using WindowsIdentity.RunImpersonated.
HttpClient client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true } );
HttpResponseMessage response = null;
if (identity is WindowsIdentity windowsIdentity)
{
await WindowsIdentity.RunImpersonated(windowsIdentity.AccessToken, async () =>
{
var request = new HttpRequestMessage(HttpMethod.Get, url)
response = await client.SendAsync(request);
});
}
It worked for me after I set up a user with internet access in the Windows service.
In my code:
HttpClientHandler handler = new HttpClientHandler();
handler.Proxy = System.Net.WebRequest.DefaultWebProxy;
handler.Proxy.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
.....
HttpClient httpClient = new HttpClient(handler)
....
Ok so I took Joshoun code and made it generic. I am not sure if I should implement singleton pattern on SynchronousPost class. Maybe someone more knowledgeble can help.
Implementation
//I assume you have your own concrete type. In my case I have am using code first with a class called FileCategory
FileCategory x = new FileCategory { CategoryName = "Some Bs"};
SynchronousPost<FileCategory>test= new SynchronousPost<FileCategory>();
test.PostEntity(x, "/api/ApiFileCategories");
Generic Class here. You can pass any type
public class SynchronousPost<T>where T :class
{
public SynchronousPost()
{
Client = new WebClient { UseDefaultCredentials = true };
}
public void PostEntity(T PostThis,string ApiControllerName)//The ApiController name should be "/api/MyName/"
{
//this just determines the root url.
Client.BaseAddress = string.Format(
(
System.Web.HttpContext.Current.Request.Url.Port != 80) ? "{0}://{1}:{2}" : "{0}://{1}",
System.Web.HttpContext.Current.Request.Url.Scheme,
System.Web.HttpContext.Current.Request.Url.Host,
System.Web.HttpContext.Current.Request.Url.Port
);
Client.Headers.Add(HttpRequestHeader.ContentType, "application/json;charset=utf-8");
Client.UploadData(
ApiControllerName, "Post",
Encoding.UTF8.GetBytes
(
JsonConvert.SerializeObject(PostThis)
)
);
}
private WebClient Client { get; set; }
}
My Api classs looks like this, if you are curious
public class ApiFileCategoriesController : ApiBaseController
{
public ApiFileCategoriesController(IMshIntranetUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
public IEnumerable<FileCategory> GetFiles()
{
return UnitOfWork.FileCategories.GetAll().OrderBy(x=>x.CategoryName);
}
public FileCategory GetFile(int id)
{
return UnitOfWork.FileCategories.GetById(id);
}
//Post api/ApileFileCategories
public HttpResponseMessage Post(FileCategory fileCategory)
{
UnitOfWork.FileCategories.Add(fileCategory);
UnitOfWork.Commit();
return new HttpResponseMessage();
}
}
I am using ninject, and repo pattern with unit of work. Anyways, the generic class above really helps.
Set identity's impersonation to true and validateIntegratedModeConfiguration to false in web.config
<configuration>
<system.web>
<authentication mode="Windows" />
<authorization>
<deny users="?" />
</authorization>
<identity impersonate="true"/>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" ></validation>
</system.webServer>
</configuration>
string url = "https://www..com";
System.Windows.Forms.WebBrowser webBrowser = new System.Windows.Forms.WebBrowser();
this.Controls.Add(webBrowser);
webBrowser.ScriptErrorsSuppressed = true;
webBrowser.Navigate(new Uri(url));
var webRequest = WebRequest.Create(url);
webRequest.Headers["Authorization"] = "Basic" + Convert.ToBase64String(Encoding.Default.GetBytes(Program.username + ";" + Program.password));
webRequest.Method = "POST";

Adding SOAP Headers for SQL 2005 HTTP Endpoint web service in Visual Studio 2008

I'm trying to use SOAP headers to allow for SQL Authentication while accessing a webservice published on my SQL 2005 box via HTTP Endpoint. On the endpoint, I've set Authentication = (Basic), Ports = (SSL), and LOGIN_TYPE = MIXED. I'm able to generate the WSDL and consume it just fine in VS utilizing domain credentials. However, when I try to implement SOAP headers to allow for SQL Authentication, i'm running into problems. I've followed MS BOL to the letter (http://msdn.microsoft.com/en-us/library/ms189619(SQL.90).aspx), but for some reason, i'm not sending the SOAP header. I've verified this by using fiddler (http://www.fiddler2.com/fiddler2/) to trap my https messages and look at them. Any help would be greatly appreciated. Included is the code I've been using (the names have been changed to protect the innocent)
namespace ConsoleApplication.WebService
{
class Program
{
static void Main(string[] args)
{
//Prevents error due to self signed cert
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
Stuff stuff = new Stuff();
stuff.DoSomthing();
}
}
public class Stuff
{
[System.Web.Services.Protocols.SoapHeaderAttribute("sqlSecurity")]
public int DoSomthing()
{
Webservice ws = new Webservice();
CredentialCache myCreds = new CredentialCache();
myCreds.Add(new Uri(ws.Url), "Basic", new NetworkCredential("netaccount", "netpass", "domain"));
ws.Credentials = myCreds;
ws.sqlSecurity = new SqlSoapHeader.Security();
ws.sqlSecurity.Username = "sqluser";
ws.sqlSecurity.Password = "sqlpass";
try
{
ws.SelectUserAccountByUserName("someuser");
}
catch (SoapException ex)
{
string txterror = ex.Detail.InnerText;
return 0;
}
return 1;
}
}
public partial class Webservice
{
public SqlSoapHeader.Security sqlSecurity;
}
}
This code utilizes the SqlSoapHeader class as documented in the BOL reference from above.
I error at calling ws.SelectUserAccountByUserName() with an "Execute permission denied" due to the fact that the "netaccount" user doesn't have rights to execute the stored proc. But again, this is because according to the soap message, no header with the sqluser info is being passed.
The SoapHeaderAttribute needs to be added to the web method. It won't do you any good to add it to some arbitrary calling method.

Categories

Resources