Programmatically configure WCF client binding to access SSL + Soap 1.1 + Basic Auth - wcf

I'm trying to write a C# WCF client (generated by svcutil from wsdl) to access a CXF (java) service implementing the same wsdl.
The service is working fine but I'm having trouble connecting to it on my C# client because the CXF is configured with SSL + Soap 1.1 + Basic Auth.
So far I've tried the following:
Why would Basic Auth not work with my WCF client to Java SOAP Web Service?
new BasicHttpBinding()
{
Security =
{
Mode = BasicHttpSecurityMode.Transport,
Transport =
{
ClientCredentialType = HttpClientCredentialType.Basic,
ProxyCredentialType = HttpProxyCredentialType.None
},
Message =
{
ClientCredentialType = BasicHttpMessageCredentialType.UserName,
AlgorithmSuite = SecurityAlgorithmSuite.Default
}
}
}
var client = new WebServiceClient(binding, endpoint);
client.ClientCredentials.UserName.UserName = username;
client.ClientCredentials.UserName.Password = password;
But it doesn't seem to send the Auth Header correctly.
I've also tried adding the header manually as outlined by http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/4f8ab001-dafa-4347-bc41-95255ecc9230. but I am not satisfied that this being the best solution.
Can any WCF expert outline a way of creating a binding programmatically that supports SSL + Soap 1.1 + Basic Auth?
Following is the header sent with WCF
System.Net Information: 0 : [13620] ConnectStream#64929093 - Sending headers
{
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://api.test.com/services/GetThings"
Host: api.test.com
Content-Length: 552
Expect: 100-continue
Accept-Encoding: gzip, deflate
}
While this is the proper header sent by SoapUI
Mon May 13 15:33:08 EDT 2013:DEBUG:>> "Accept-Encoding: gzip,deflate[\r][\n]"
Mon May 13 15:33:08 EDT 2013:DEBUG:>> "Content-Type: text/xml;charset=UTF-8[\r][\n]"
Mon May 13 15:33:08 EDT 2013:DEBUG:>> "SOAPAction: "http://api.test.com/services/GetThings"[\r][\n]"
Mon May 13 15:33:08 EDT 2013:DEBUG:>> "Authorization: Basic bXliVlcHJpbQbWwOTkxMjg=[\r][\n]"
Mon May 13 15:33:08 EDT 2013:DEBUG:>> "Content-Length: 317[\r][\n]"
Mon May 13 15:33:08 EDT 2013:DEBUG:>> "Host: api.test.com[\r][\n]"
Mon May 13 15:33:08 EDT 2013:DEBUG:>> "Connection: Keep-Alive[\r][\n]"
Mon May 13 15:33:08 EDT 2013:DEBUG:>> "User-Agent: Apache-HttpClient/4.1.1 (java 1.5)[\r][\n]"

Related

xero api fails with unauthorized (401 or 403) after calling auth, refresh and gettenants when calling getinvoices

I'm a rank noob at this, so excuse my ignorance. I've got an MVC web application to login, get the access and refresh tokens, and tenant list OK. I can even get it to refresh the refresh token. No problems.
When I try to run the GetInvoices endpoint either directly or via the sdk, I get 403 (skd) or 401 from the direct api call.
From the latest run with direct call I get this response
{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content:
System.Net.Http.HttpConnectionResponseContent, Headers:
{
Server: nginx
Strict-Transport-Security: max-age=31536000
WWW-Authenticate: OAuth Realm="api.xero.com"
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache
Date: Wed, 21 Jul 2021 11:19:56 GMT
Connection: close
X-Client-TLS-ver: tls1.2
Content-Type: text/html; charset=utf-8
Content-Length: 95
Expires: Wed, 21 Jul 2021 11:19:56 GMT
}, Trailing Headers:
{
}}
I know that the access token and tenant id used in the GetInvoices step are correct because I checked them against the values pulled in from the auth steps character by character.
The app is being run in Visual Studio 2019, using the self-signed development SSL certificate.
Why is it rejecting the request?
my controllers have the following
private static readonly string Scopes = "openid offline_access profile email accounting.transactions accounting.contacts accounting.attachments";
private static readonly string Scopes = "openid offline_access profile email accounting.transactions accounting.contacts accounting.attachments";
string[] tenant = (string[])TempData.Peek("tenant");
var client = new HttpClient();
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("summaryOnly", "true"),
});
client.DefaultRequestHeaders.Add("Authorization", (string)TempData.Peek("accessToken"));
client.DefaultRequestHeaders.Add("Xero-Tenant-Id", tenant[0]);
client.DefaultRequestHeaders.Add("Accept", "application/json");
var response = await client.PostAsync("https://api.xero.com/api.xro/2.0/Invoices", formContent);
The SDK should handle this for you in the helper methods for the client and OAuth flow but i've included what looks like is missing from just a raw API call below.
Core API call - looks like you need to prefix the token with the Bearer string.
Authorization: "Bearer " + access_token
If you are wanting to use the SDK note that there is a sub Nuget package for OAuth helpers that will help you obtain an access token which you need to pass to core api calls.
https://github.com/XeroAPI/Xero-NetStandard/tree/master/Xero.NetStandard.OAuth2Client
(DOH!) The Tenant returns an Id and a TenantId. I was using the Id.
Thanks to SerKnight and droopsnoot for helping.
I've added code from the OAuth2. The help does not mention to get and cast the return type of RequestAcessTokenAsync.
XeroOAuth2Token authToken = (XeroOAuth2Token)await client.RequestAccessTokenAsync(authorisationCode);
also to check the state on return, you must set in xconfig. mine reads
XeroConfiguration xconfig = new()
{
ClientId = Global.XeroClientID,
ClientSecret = Global.XeroClientSecret,
CallbackUri = new Uri(Global.RedirectURI),
Scope = Global.XeroScopes,
State = Global.XeroState
};
var client = new XeroClient(xconfig);
return Redirect(client.BuildLoginUri());

Two sites same IIS server, RunImpersonation HttpClient passing empty NTLM credential

I have two sites on the same IIS instance. One uses HttpClient to request data from the other. They are both configured with Windows Authentication only. These are both ASPNET Core 3.1.
When I browse to the first site, it authenticates, but when it calls through to the other, it returns a 401. I validated that the user identity is correct.
var baseUri = new Uri(AppSettings.CurrentValue.MyBaseUrl);
var user = (WindowsIdentity)HttpContext.User.Identity;
WindowsIdentity.RunImpersonated(user.AccessToken, () =>
{
AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);
var credentialCache = new CredentialCache {{baseUri, "NTLM", CredentialCache.DefaultNetworkCredentials}};
var httpClientHandler = new HttpClientHandler {Credentials = credentialCache};
using HttpClient httpClient = new HttpClient(httpClientHandler) { BaseAddress = baseUri };
var response = httpClient.GetAsync("/home/test").Result;
var content = response.IsSuccessStatusCode
? response.Content.ReadAsStringAsync().Result
: response.StatusCode.ToString();
}
Wireshark shows the call into the first site, /home/test1, and shows the user credentials being passed correctly,
GET /home/test1 HTTP/1.1
HTTP/1.1 401 Unauthorized (text/html)
GET /home/test1 HTTP/1.1 , NTLMSSP_NEGOTIATE
HTTP/1.1 401 Unauthorized , NTLMSSP_CHALLENGE (text/html)
GET /home/test1 HTTP/1.1 , NTLMSSP_AUTH, User: COMPANY\fbloggs
The same trace shows the jump to the second site, /home/test2, and shows null domain and username,
GET /home/test2 HTTP/1.1
HTTP/1.1 401 Unauthorized (text/html)
GET /home/test2 HTTP/1.1 , NTLMSSP_NEGOTIATE
HTTP/1.1 401 Unauthorized , NTLMSSP_CHALLENGE (text/html)
GET /home/test2 HTTP/1.1 , NTLMSSP_AUTH, ** User: \ **
GET /home/test2 HTTP/1.1\r\n
[Expert Info (Chat/Sequence): GET /home/test2 HTTP/1.1\r\n]
[GET /home/test2 HTTP/1.1\r\n]
[Severity level: Chat]
[Group: Sequence]
Request Method: GET
Request URI: /home/test2
Request Version: HTTP/1.1
Connection: Keep-Alive\r\n
Request-Id: |60fb71bd-482efe66c05094ec.1.\r\n
Host: testserver\r\n
Authorization: NTLM TlRMTVNTUAADAAAAAQDADEIAAAAAAAAAcwAAAAAAAABYAAAAAAAAAFgAAAAaABoAWAAzAAAABYqIogoAY0UAAAAPvaq0nk2I7YcqJmq01EbY20IASDTATGSAGVAALQBXAEUAQgAyADEAAA==\r\n
NTLM Secure Service Provider
NTLMSSP identifier: NTLMSSP
NTLM Message Type: NTLMSSP_AUTH (0x00000003)
Lan Manager Response: 00
NTLM Response: Empty
Domain name: NULL
User name: NULL
Host name: TESTSERVER
Session Key: Empty
Negotiate Flags: 0xa2888a05, Negotiate 56, Negotiate 128, Negotiate Version, Negotiate Target Info, Negotiate Extended Security, Negotiate Always Sign, Negotiate Anonymous, Negotiate NTLM key, Request Target, Negotiate UNICODE
Version 10.0 (Build 17763); NTLM Current Revision 15
Major Version: 10
Minor Version: 0
Build Number: 17763
NTLM Current Revision: 15
MIC: bdaab49e4d88ed872a266ab4d446d8db
HTTP/1.1 401 Unauthorized (text/html)

I get "kotlinx/coroutines/io/ByteReadChannel" when I try to decode a json on server side with ktor and moshi

In Application.kt I install moshi in Application.module
install(ContentNegotiation){
moshi()
}
I declared a simple test calss and in the route I try to decode the test class:
data class Test(val testString: String)
fun Route.test() {
post (TEST_ENDPOINT) {
val testReceive = call.receive<Test>()
call.respond(testReceive)
}
The request is post with the following Headers and Body:
Accept: */*
Accept-Encoding: gzip, deflate
Content-Type: application/json
Accept-Language: en-gb
{
"testString": "dasdada"
}
Response headers:
HTTP/1.1 500 Internal Server Error
Date: Mon, 05 Oct 2020 11:55:58 GMT
Content-Length: 37
Connection: keep-alive
Content-Type: text/plain; charset=UTF-8
Server: ktor-server-core/1.4.1 ktor-server-core/1.4.1
Response body:
kotlinx/coroutines/io/ByteReadChannel
Any suggestion or comment is appreciated.
Moshi 1.0.1 relies on some outdated Ktor API.
Consider either moving back to Ktor 1.3.2 or (better) use another JSON handler (there are several available out of the box: Gson, Jackson and kotlinx.serialization)

UseExceptionHandler() middleware in .NET Core MVC prevents response headers setting

Startup.cs:
// ...
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Frame-Options", "DENY");
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
context.Response.Headers.Add("Server", "ololo");
await next();
});
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
else { app.UseExceptionHandler("/Home/Error"); }
app.UseStaticFiles();
app.UseAuthentication();
// ...
When everything is fine, I get the following headers, as expected:
HTTP/1.1 200 OK
Connection: close
Date: Mon, 30 Jul 2018 18:39:33 GMT
Content-Type: text/html; charset=utf-8
Server: ololo
Transfer-Encoding: chunked
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
So Server, X-Frame-Options and X-Content-Type-Options headers are overridden.
But if I have an unhandled exception in my code, then I get these headers:
HTTP/1.1 500 Internal Server Error
Connection: close
Date: Mon, 30 Jul 2018 18:35:49 GMT
Content-Type: text/html; charset=utf-8
Server: Kestrel
Cache-Control: no-cache
Pragma: no-cache
Transfer-Encoding: chunked
Expires: -1
So headers are not overridden.
Why is that? Is it by design? Does Exceptions middleware work differently so it doesn't go through the whole pipeline?
dotnet --info
.NET Command Line Tools (2.1.4)
Product Information:
Version: 2.1.4
Commit SHA-1 hash: 5e8add2190
Microsoft .NET Core Shared Framework Host
Version : 2.0.5
Build : 17373eb129b3b05aa18ece963f8795d65ef8ea54
A more reliable way to set the headers in any case would be to use the OnStarting callback. See docs.
Adds a delegate to be invoked just before response headers will be sent to the client.
public async Task Invoke(HttpContext context)
{
context.Response.OnStarting(() =>
{
context.Response.Headers.Add("X-Frame-Options", "DENY");
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
context.Response.Headers.Add("Server", "ololo");
return Task.CompletedTask;
});
await _next(context);
}
OnStarting will be invoked, just before the response headers are written to the wire. This allows you to set the headers after the exception middleware did handle it

How to correctly handle multiple Set-Cookie headers in Hyper?

I'm using Hyper to send HTTP requests, but when multiple cookies are included in the response, Hyper will combine them to one which then fails the parsing procedure.
For example, here's a simple PHP script
<?php
setcookie("hello", "world");
setcookie("foo", "bar");
Response using curl:
$ curl -sLD - http://local.example.com/test.php
HTTP/1.1 200 OK
Date: Sat, 24 Dec 2016 09:24:04 GMT
Server: Apache/2.4.25 (Unix) PHP/7.0.14
X-Powered-By: PHP/7.0.14
Set-Cookie: hello=world
Set-Cookie: foo=bar
Content-Length: 0
Content-Type: text/html; charset=UTF-8
However for the following Rust code:
let client = Client::new();
let response = client.get("http://local.example.com/test.php")
.send()
.unwrap();
println!("{:?}", response);
for header in response.headers.iter() {
println!("{}: {}", header.name(), header.value_string());
}
...the output will be:
Response { status: Ok, headers: Headers { Date: Sat, 24 Dec 2016 09:31:54 GMT, Server: Apache/2.4.25 (Unix) PHP/7.0.14, X-Powered-By: PHP/7.0.14, Set-Cookie: hello=worldfoo=bar, Content-Length: 0, Content-Type: text/html; charset=UTF-8, }, version: Http11, url: "http://local.example.com/test.php", status_raw: RawStatus(200, "OK"), message: Http11Message { is_proxied: false, method: None, stream: Wrapper { obj: Some(Reading(SizedReader(remaining=0))) } } }
Date: Sat, 24 Dec 2016 09:31:54 GMT
Server: Apache/2.4.25 (Unix) PHP/7.0.14
X-Powered-By: PHP/7.0.14
Set-Cookie: hello=worldfoo=bar
Content-Length: 0
Content-Type: text/html; charset=UTF-8
This seems to be really weird to me. I used Wireshark to capture the response and there're two Set-Cookie headers in it. I also checked the Hyper documentation but got no clue...
I noticed Hyper internally uses a VecMap<HeaderName, Item> to store the headers. So they concatenate the them to one? Then how should I divide them into individual cookies afterwards?
I think that Hyper prefers to keep the cookies together in order to make it easier do some extra stuff with them, like checking a cryptographic signature with CookieJar (cf. this implementation outline).
Another reason might be to keep the API simple. Headers in Hyper are indexed by type and you can only get a single instance of that type with Headers::get.
In Hyper, you'd usually access a header by using a corresponding type. In this case the type is SetCookie. For example:
if let Some (&SetCookie (ref cookies)) = response.headers.get() {
for cookie in cookies.iter() {
println! ("Got a cookie. Name: {}. Value: {}.", cookie.name, cookie.value);
}
}
Accessing the raw header value of Set-Cookie makes less sense, because then you'll have to reimplement a proper parsing of quotes and cookie attributes (cf. RFC 6265, 4.1).
P.S. Note that in Hyper 10 the cookie is no longer parsed, because the crate that was used for the parsing triggers the openssl dependency hell.