WCF Consumer not attaching client credentials to header - c#

I have a WCF service that uses username authentication, I have a console app that consumes the service and attempts to access a protected method. I run the code and Fiddler says in the auth tab:
No Proxy-Authorization Header is present.
No Authorization Header is present.
Here is my accessing code:
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Message.AlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Default;
binding.Security.Transport.Realm = "MyRealm";
ServiceReference1.MobileAPIClient serviceProxy = new ServiceReference1.MobileAPIClient(binding, new EndpointAddress("https://xx.xx.xx.xx/InventoryServices.MobileApi.svc"));
serviceProxy.ClientCredentials.UserName.UserName = "test";
serviceProxy.ClientCredentials.UserName.Password = "test123";
serviceProxy.ChannelFactory.Credentials.UserName.UserName = "test";
serviceProxy.ChannelFactory.Credentials.UserName.Password = "test123";
try
{
serviceProxy.Test();
}
catch (Exception ex)
{
var ex2 = ex;
}
Why are the credentials not being attached to the header?

There is handshaking mode in wcf. So client and service exchange with credentials before first request and then they use only session token.
To disable this mode you should set in web.config
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" negotiateServiceCredential="false" establishSecurityContext="false"/>
</security>

Related

Why web services call only succeeds when pre logined through browser atleast once a day on server

I have following bindings on server side,
<bindings>
<basicHttpBinding>
<binding name="my_BasicHttpBinding">
<security mode="Transport">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
and client code running in console on same server,
System.Net.NetworkCredential creds = new NetworkCredential("UserName ", "password", "domain");
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows; //.Ntlm; // Basic;
EndpointAddress endpoint = new EndpointAddress("https://example.portal.com + "/_vti_bin/MyServiceFolder/MyService.svc");
ChannelFactory<MyType> factory = new ChannelFactory<MyType>(binding, endpoint);
factory.Credentials.Windows.ClientCredential = creds;
factory.Credentials.UserName.UserName = creds.UserName;
factory.Credentials.UserName.Password = creds.Password;
//factory.Credentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
//factory.Credentials.Windows.AllowNtlm = true;
MyType proxy = factory.CreateChannel();
string result = proxy.MyMethod();
Now it usually throws 401 but when I login through browser using same username to view .svc file and try this code again... IT WORKS. BUT again next morning it won't work till I repeat the steps.
Some more details can be found here but with different settings and scenario -
How to make these web services work, man in middle is KEMP too
Could it be related to SharePoint or authentication protocols or the way Kerborse works etc.. got no clue ?

WCF - Setting Policy for UsernameToken

I received an updated WSDL for a service I'm consuming which has below Policy added
<wsp:Policy wssutil:Id="UsernameToken">
<ns0:SupportingTokens xmlns:ns0="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200512">
<wsp:Policy>
<ns0:UsernameToken ns0:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200512/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<ns0:WssUsernameToken10 />
</wsp:Policy>
</ns0:UsernameToken>
</wsp:Policy>
</ns0:SupportingTokens>
</wsp:Policy>
I have updated my reference by right clicking the Service Reference --> Configure Service option inside Visual Studio. This generated a customBinding replacing my previous basicHttpBinding
<customBinding>
<binding name="myBindingName">
<!-- WsdlImporter encountered unrecognized policy assertions in ServiceDescription 'http://ouaf.oracle.com/webservices/cm/CM-CustConnAcctDetailExtract': -->
<!-- <wsdl:binding name='CM-CustConnAcctDetailExtractSoapBinding'> -->
<!-- <ns0:SupportingTokens xmlns:ns0="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200512">..</ns0:SupportingTokens> -->
<textMessageEncoding messageVersion="Soap11" />
<httpTransport />
</binding>
</customBinding>
Do I need to use this customBinding only? Or is there any option in basicBinding that makes it work?
If I use my basicBinding with TransportWithMessageCredential, I get below error:
The provided URI is Invalid; HTTPS is expected
I ran this using SoapUI. In addition to UserName and Passwrod, I had to supply WSS-PasswordType as PasswordText. Without supplying this parameter, I get an error in SoapUI
Error on verifying message against security policy Error code:1000
I'm not sure how to supply WSS-PasswordType in my basicHttpBinding.
My basicHttpBinding is as below
protected BasicHttpBinding GetBinding()
{
return new BasicHttpBinding()
{
Name = "BasicHttpBinding",
ReceiveTimeout = new TimeSpan(0, 0, 2, 0),
SendTimeout = new TimeSpan(0, 0, 2, 0),
Security =
{
Mode = BasicHttpSecurityMode.TransportCredentialOnly,
Transport =
{
ClientCredentialType = HttpClientCredentialType.Basic,
ProxyCredentialType = HttpProxyCredentialType.None,
Realm = ""
},
Message =
{
ClientCredentialType = BasicHttpMessageCredentialType.UserName,
AlgorithmSuite = SecurityAlgorithmSuite.Default
}
},
MaxBufferSize = Int32.MaxValue,
MaxReceivedMessageSize = Int32.MaxValue,
ReaderQuotas = new XmlDictionaryReaderQuotas()
{
MaxBytesPerRead = 8192
}
};
}
I'm able to work this through by changing my binding to Custom Binding
protected CustomBinding GetCustomBinding()
{
var customBinding = new CustomBinding() { Name = "CustomBinding" };
customBinding.Elements.Add(new TextMessageEncodingBindingElement() { MessageVersion = MessageVersion.Soap11 });
var securityBindingElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
securityBindingElement.AllowInsecureTransport = true;
securityBindingElement.EnableUnsecuredResponse = true;
securityBindingElement.IncludeTimestamp = false;
customBinding.Elements.Add(securityBindingElement);
customBinding.Elements.Add(new HttpTransportBindingElement());
return customBinding;
}

Upload file to Sharepoint from External MVC Web Application

How can I use Sharepoint's default CopyIntoItems method to upload a new file into sharepoint from an external web application by passing in a sharepoint username and password. I don't want to use the default credentials because I'm using SQL server based forms authentication on the MVC web application.
I've tried the following:
Where CopySoapClient is the webservice connected to this url.
http://sharepointaddress/_vti_bin/copy.asmx
Code Sample
public static bool UploadSharePointFile(string file, string destination)
{
bool success = false;
CopySoapClient client = new CopySoapClient();
client.ClientCredentials.Windows.ClientCredential = new NetworkCredential("username", "password", "domain");
try
{
client.Open();
string filename = Path.GetFileName(file);
string destinationUrl = destination + filename;
string[] destinationUrls = { destinationUrl };
FieldInformation i1 = new FieldInformation { DisplayName = "Title", InternalName = "Title", Type = FieldType.Text, Value = filename };
FieldInformation[] info = { i1 };
CopyResult[] result;
byte[] data = File.ReadAllBytes(file);
uint ret = client.CopyIntoItems(filename, destinationUrls, info, data, out result);
if (result != null && result.Length > 0 && result[0].ErrorCode == 0)
success = true;
}
finally
{
if (client.State == System.ServiceModel.CommunicationState.Faulted)
client.Abort();
if (client.State != System.ServiceModel.CommunicationState.Closed)
client.Close();
}
return success;
}
The problem is that I keep getting the following error:
The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'.
When I tried putting this in the web.config in the web service bindings:
<security mode="Transport">
<transport clientCredentialType="Ntlm" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
Then I got the following error:
The provided URI scheme 'http' is invalid; expected 'https'. Parameter name: via
I figured it out. All I need to do was change the security mode in the web.config to
TransportCredentialOnly
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>

With C#, WCF SOAP consumer that uses WSSE plain text authentication?

I have a WCF SOAP consumer that is implemented by Visual Studio 2012 from a WSDL. The WSDL was generated by PeopleTools. The base object is of type System.ServiceModel.ClientBase.
I need the SOAP request to resemble:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://xmlns.oracle.com/Enterprise/Tools/schemas">
<soapenv:Header>
<wsse:Security soap:mustUnderstand="1" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>[plain text username goes here]</wsse:Username>
<wsse:Password>[plain text password goes here]</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<sch:InputParameters>
<Last_Name>Aren</Last_Name>
<First_Name>Cambre</First_Name>
</sch:InputParameters>
</soapenv:Body>
</soapenv:Envelope>
Here's the closest we can get:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
<a:MessageID>urn:uuid:3cc3f2ca-c647-466c-b38b-f2423462c837</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://[internal URL to soap listener]</a:To>
</s:Header>
<s:Body>
<t:RequestSecurityToken Context="uuid-7db82975-2b22-4236-94a1-b3344a0bf04d-1" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</t:TokenType>
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:KeySize>256</t:KeySize>
<t:BinaryExchange ValueType=" http://schemas.xmlsoap.org/ws/2005/02/trust/tlsnego" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">FgMBAFoBAABWAwFQ9IhUFGUO6tCH+0baQ0n/3us//MMXzQA78Udm4xFj5gAAGAAvADUABQAKwBPAFMAJwAoAMgA4ABMABAEAABX/AQABAAAKAAYABAAXABgACwACAQA=</t:BinaryExchange>
</t:RequestSecurityToken>
</s:Body>
</s:Envelope>
You'll notice two problems:
No plaintext WSSE credentials. Passes a binary form of the credentials that the service won't use.
Authentication is in Body, not Header.
The request omits InputParameters.
Here's the essential C# code:
var service = new ServiceWithBizarreNameFromPeoplesoft();
if (service.ClientCredentials == null)
throw new NullReferenceException();
service.ClientCredentials.UserName.UserName = "test";
service.ClientCredentials.UserName.Password = "password";
var binding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential) {Security = new WSHttpSecurity()};
service.Endpoint.Binding = binding;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Mode = SecurityMode.Message;
var input = new InputParameters { Last_Name = "Cambre", First_Name = "Aren" };
var returnData = service.BizarrePeopleSoftNameForMethod(input);
There's no HTTP layer security, and transport is SSL-encrypted. Authentication is only based on the SOAP message.
That is request for WS-SecureConversation token. It is used by WSHttpSecurity by default unless you change its EstablishSecurityContext property to false. Use this binding instead:
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
It will use SOAP 1.1 with UserName token and it will require HTTPS transport.
Edit:
For testing without HTTPS try to use this custom binding:
var securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
securityElement.AllowInsecureTransport = true;
var encodingElement = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
var transportElement = new HttpTransportBindingElement();
var binding = new CustomBinding(securityElement, encodingElement, transportElement);
This looks to me like wsHttpBindings with Transport security using basic username password authentication.
These lines look wrong to me:
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Mode = SecurityMode.Message;
Here's how I would expect to see this configured in your app or web.config
<bindings>
<wsHttpBinding>
<binding name="ws" >
<security mode="Transport">
<transport clientCredentialType="Basic" proxyCredentialType="Basic" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://www.bla.com/webservice" binding="basicHttpBinding" contract="bla.IService" name="ws" />
</client>
Then the code would look like this:
var service = new GeneratedProxyClient("basic");
service.ClientCredentials.UserName.UserName = "test";
service.ClientCredentials.UserName.Password = "password";
var input = new InputParameters { Last_Name = "Cambre", First_Name = "Aren" };
var returnData = service.BizarrePeopleSoftNameForMethod(input);
Might be better explained here --> http://msdn.microsoft.com/en-us/library/ms733775.aspx

Problem with authentication on java ws

I have application in C# that consume Java WS. Everything worked fine until WS was configured to use authentication. Now I should user login i password to execute methods from WS but I'm not sure how to do it.
I've try
var client = new MyBeanClient();
client.ClientCredentials.UserName.UserName = "admin";
client.ClientCredentials.UserName.Password = "";
client.addConsumer("whatever", "", "", "");
But I get
SecurityMessageException-{"The HTTP request is unauthorized with client
authentication scheme 'Anonymous'. The authentication header received from
the server was 'Negotiate,NTLM'."}
InnerException - (WebException) - {"The remote server returned an error:
(401) Unauthorized."}.
What's wrong?
Thanks
Try this:
var credentialCache = new CredentialCache();
var credentials = new NetworkCredential("username", "password", "domain");
credentialCache.Add(new Uri(client.Url), "NTLM", credentials);
client.Credentials = credentialCache;
client.addConsumer("whatever", "", "", "");
UPDATE:
Sorry in my first post I thought you were using wsdl.exe to generate the client proxy. For a WCF client you need to configure the endpoint:
var basicHttpBinding = new BasicHttpBinding();
basicHttpBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
var endpoint = new EndpointAddress("http://example.com/myWindowsAuthN");
var client = new MyBeanClient(basicHttpBinding, endpoint);
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
client.ChannelFactory.Credentials.Windows.ClientCredential.Domain = "domain";
client.ChannelFactory.Credentials.Windows.ClientCredential.UserName = "username";
client.ChannelFactory.Credentials.Windows.ClientCredential.Password = "password";
UPDATE2:
I've used the following configuration to invoke web services protected with NTLM authentication. In app.config of the client put the following:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="NtlmBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint
address="http://example.com/SomeWindowsAuthenticatedService"
binding="basicHttpBinding"
bindingConfiguration="NtlmBinding"
contract="IOperationContractOfTheService"
name="WSTestSoap" />
</client>
</system.serviceModel>
and then you could set the corresponding credentials before invoking the method:
using (var client = new MyBeanClient())
{
client.ChannelFactory.Credentials.Windows.ClientCredential =
new NetworkCredential("username", "password", "DOMAIN");
client.addConsumer("whatever", "", "", "");
}

Categories

Resources