curl POST works but ionic this.http.post doesn't - http

I am struggling to get an ionic this.http.post to work.
If I use this curl in my terminal it works great:
curl -v -X POST \
https://myuser-name:ijF3Ui7VYVbbSejmwsnVVo#appdb.mysite.com:5984/_session \
-d 'name=app&password=ijF3Ui7VYVbbSejmwsnVVo'
It gives me the following output:
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 37.1.96.50...
* TCP_NODELAY set
* Connected to app.mysite.com (37.1.96.49) port 5984 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate: app.mysite.com
* Server certificate: COMODO RSA Domain Validation Secure Server CA
* Server certificate: COMODO RSA Certification Authority
* Server auth using Basic with user 'myuser-name'
> POST /_session HTTP/1.1
> Host: app.mysite.com:5984
> Authorization: Basic cDpkTUQySzg0a2lqRjNVaTdWWVZiYlNlam13c25WVm8=
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Length: 52
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 52 out of 52 bytes
< HTTP/1.1 200 OK
< Set-Cookie: AuthSession=ZWhzLWFwcDo1OUFENThGRjruBtcPzHcqc1sC9WXrcWI7R27_Mg; Version=1; Secure; Path=/; HttpOnly
< Server: CouchDB/1.6.1 (Erlang OTP/18)
< Date: Mon, 04 Sep 2017 13:45:35 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 43
< Cache-Control: must-revalidate
<
{"ok":true,"name":null,"roles":["_admin"]}
* Connection #0 to host app.mysite.com left intact
My ionic POST code looks like this:
login(callerName:string):any
// Make sure we have a CouchDB session so that PouchDB can access the CouchDB database
{
console.log('Authentication: login(): Login function called from ' + callerName);
return new Promise(resolve => {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
let credentials = {
name: COUCHDB_USER,
password: COUCHDB_PASSWORD
};
let result = {
success: false,
data: []
};
console.log('Authentication: login(): credentials = ' + JSON.stringify(credentials));
// NOTE:
//
// If POST is called with COUCHDB_SERVER with no auth in the url I get the error: Response with status: 401 Unauthorized for URL: https://app.mysite.com:5984/_session"
//
// If POST is called with COUCHDB_SERVER WITH auth in url I get the error: Response with status: 0 for URL: null
// This 'might' mean:
// Timeout from server
// Request not sent
// Requesting an unreachable url
// ...
// This WORKS with curl in terminal
//
// With auth in url: https://myuser-name:ijF3Ui7VYVbbSejmwsnVVo#app.mysite:5984/_session
// Without auth in url: https://app.mysite.com:5984/_session
//
this.http.post(COUCHDB_SERVER + '/_session', JSON.stringify(credentials), {headers: headers})
.subscribe(res => {
var details = res.json();
console.log('Authentication: login(): SuperLogin successful login: res = ' + JSON.stringify(details));
result.success = true;
result.data = details;
resolve(result);
},
(err) => {
console.log('Authentication: login(): Login failed err = ' + err);
let details = err.json();
result.success = false;
result.data = details;
resolve(result);
});
});
}
If I try the POST in ionic with no auth in the url I get a sensible error message:
Response with status: 401 Unauthorized for URL: https://app.mysite.com:5984/_session"
But if I add auth to the url I get an error message that doesn't tell me what the problem is:
Response with status: 0 for URL: null
I can't work out why it works with curl but not within ionic http.post.
I have the same problem whether I run ionic serve or I run the app on an iPhone.
UPDATE
I have run the ionic App in Chrome and now have a better error:
error: "unauthorized", reason: "Authentication required."
So it is clear I am not getting the POST request correct but can't see why.

The authentication failed in ionic because the usage of this.http.post is incorrect: the second parameter should be HTTP request body object (JSON, the credential object), not a string. Please refer to https://angular.io/guide/http for example.
The code to send HTTP request would be:
this.http.post(COUCHDB_SERVER + '/_session', credentials, {headers: headers})...
It works in curl, but not in ionic -- That's because the Content-Type of the HTTP request sent by curl is application/x-www-form-urlencoded, and curl's syntax is correct.
Shall I add auth to the URL? -- I guess it means the myuser-name:ijF3Ui7VYVbbSejmwsnVVo# part in the URL. The answer is No: It works in curl (add Authorization header in request) but it won't work in browser, please check Pass username and password in URL for HTTP Basic Auth for details.
Update: It seems Basic authentication is forced in CouchDB. In order to satisfy it, Authorization header can be added manually in HTTP request:
headers.append('Authorization', 'Basic ' + window.btoa(username + ':' + password))

Related

ProxyKit and Querystring for WebSocket connection

I'm using ProxyKit to act as a reverse proxy. I need to proxy http and websocket (signalR) traffic so I have the below configuration. I also need to send across a querystring, what do I need to add to the base configuration to also include the querystring, I've tried this:
var redirectTo = "https://proxied-server:5002";
var wssredirectTo = "ws://proxied-server:5002";
app.UseWebSockets();
app.UseWebSocketProxy(context => new Uri(wssredirectTo + context.Request.Path.ToString() + context.Request.QueryString.ToString()),
options => options.AddXForwardedHeaders());
app.RunProxy(context =>
{
var finalUrl = redirectTo + context.Request.Path.ToString();
var finalContext = context.ForwardTo(finalUrl);
finalContext.UpstreamRequest.RequestUri = new Uri(finalUrl);
return finalContext
.CopyXForwardedHeaders()
.AddXForwardedHeaders()
.Send();
});
But I still only see the root url in the proxied server kestrel log:
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 POST https://proxied-server:5002/signal/satellite/negotiate text/plain; charset=UTF-8 0
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 2.3528ms 200 application/json
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET https://proxied-server:5002/signal/satellite
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 2.3958ms 400 text/plain
You should not need to set RequestUri directly 1,2:
app.RunProxy(context => context
.ForwardTo(redirectTo)
.CopyXForwardedHeaders()
.AddXForwardedHeaders()
.Send());
Explanation
ProxyKit already handles context.Request.QueryString in ForwardTo 3:
var uri = new Uri(UriHelper.BuildAbsolute(
upstreamHost.Scheme,
upstreamHost.Host,
upstreamHost.PathBase,
context.Request.Path,
context.Request.QueryString));
References
https://github.com/proxykit/ProxyKit#12-forward-http-requests
https://github.com/proxykit/ProxyKit#233-copying-x-forwarded-headers
https://github.com/proxykit/ProxyKit/blob/4468429f3910111b98d80821fd10824204573053/src/ProxyKit/HttpContextExtensions.cs#L21-L26

ASP.NET 4.5 Rest API's work in Unity Android/iOS build but fails with "Unknown error" in Unity WebGL build

I have scoured every possible forum for this and somehow have not gotten my WebGL to consume my ASP.NET 4.5 REST API's.
From what I can tell it is possibly related to WebGL requiring CORS, but even enabling this I cannot get the game to communicate with my API's
So either there's something wrong with the way I have implemented global CORS settings in ASP.NET or something else is breaking.
To be clear these API's are running perfectly well on Android/iOS/Windows builds and even in the editor.
What I have done so far:
Installed the Microsoft CORS build as recommended by Microsoft's documentation relating to it, then added the following code to the WebAPIConfig class in Visual Studio:
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
////new code
config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
This is also in my web.config:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
</customHeaders>
</httpProtocol>
I need these settings global so I used the "*" as indicated by the documentation to include all domains, method types, and headers because I use ASP.NET token authentication for my API.
Here is a code snippet that gets the token in the Unity project (just to be clear, this works on other platforms, only throws an error in a WebGL build)
public IEnumerator login()
{
string url = API.ROUTEPATH + API.TOKEN;
WWWForm form = new WWWForm();
form.AddField("grant_type", "password");
form.AddField("username", API.APIUSERNAME);
form.AddField("password", API.APIPASSWORD);
UnityWebRequest uwr = UnityWebRequest.Post(url, form);
uwr.SetRequestHeader("Content-Type", "application/json");
yield return uwr.SendWebRequest();
try
{
if (uwr.isNetworkError)
{
Debug.Log(uwr.error);
}
else
{
APIAuthToken returnauth = JsonUtility.FromJson<APIAuthToken>(uwr.downloadHandler.text);
if (!string.IsNullOrEmpty(returnauth.access_token))
{
API.hasAuth = true;
API.token = returnauth.access_token;
Debug.Log(returnauth.access_token);
}
}
}
catch
{
}
}
uwr.error produces the following, very helpful error: Unknown Error So I'm not even sure if it is CORS related, it's just my best guess based on the research I have done, but even with multiple different implementations of it I still sit with the same error. So if it's not a problem with the API's and with my Unity code please just ignore the ASP.NET code snippet.
cURL - A simple curl -I <endpoint> or curl -X OPTIONS -v <endpoint> can reveal a ton of information about what is happening related to CORS. It can allow you to set different origins, check preflight responses, and more.
"Let's say you have a backend API that uses cookies for session management. Your game works great when testing on your own domain, but breaks horribly once you host the files on Kongregate due to the fact that your API requests are now cross-domain and subject to strict CORS rules."
Is this your problem?
Problably on both sides if things are not set up properly will refuse to send cookies, but its good, its mean you have the control to allow what domains your sessions cookies will be sent to.
So probably you need first to configure the server to allow multiplies origins but make sure to validate the value against a whitelist so that you aren't just enabling your session cookies to be sent to any origin domain.
Example on a Node Express with CORS middleware(game ID 12345) and an origin whitelist below:
express = require('express')
var cors = require('cors')
var app = express()
var whitelist = ['https://game12345.konggames.com'];
var corsOptions = {
credentials: true,
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
};
app.use(cors(corsOptions));
app.options('*', cors(corsOptions)); // Enable options for preflight
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(8080, () => console.log(`Example app listening on port 8080!`))
cURL command to check the headers for an OPTIONS preflight request from an origin in the whitelist array:
curl -X OPTIONS -H"Origin: https://game12345.konggames.com" -v http://localhost:8080/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> OPTIONS / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
> Origin: https://game12345.konggames.com
>
< HTTP/1.1 204 No Content
< X-Powered-By: Express
< Access-Control-Allow-Origin: https://game12345.konggames.com
< Vary: Origin, Access-Control-Request-Headers
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
< Content-Length: 0
< Date: Tue, 24 Sep 2019 22:04:08 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
instruct the client to include cookies when it makes a cross-domain request,If the preflight response did not include Access-Control-Allow-Credentials: true, or if your Access-Control-Allow-Access is set to a wildcard (*) then the cookies will not be sent and you are likely to see errors in your browser's Javascript console:
Access to XMLHttpRequest at 'https://api.mygamebackend.com' from origin 'https://game54321.konggames.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Unity's UnityWebRequest and the older WWW classes use XMLHttpRequest under the hood to fetch data from remote servers. Since there is no option to set the withCredentials flag to true, we have to perform a pretty dirty hack when initializing our application in order to turn that on for the appropriate requests.
In your WebGL template or generated index.html:
<script>
XMLHttpRequest.prototype.originalOpen = XMLHttpRequest.prototype.open;
var newOpen = function(_, url) {
var original = this.originalOpen.apply(this, arguments);
if (url.indexOf('https://api.mygamebackend.com') === 0) {
this.withCredentials = true;
}
return original;
}
XMLHttpRequest.prototype.open = newOpen;
</script>
This snippet of code overrides the open method of XMLHttpRequest so that we can conditionally set withCredentials equal to true when desired. Once this is in place, cross-origin cookies should begin working between the Kongregate-hosted iframe domain and the game's backend servers!
info taken from here
also looks nice for this

Webservice and POST requests

I have a web service written is ASP.NET which when tested with Postman with JSON content works fine. JSON is sent as Body is Postman
When trying to implement the same in Nativescript using HTTP Post, it fails.
When checking the IIS logs, the cs-method for Postman is correct as POST, but the cs-method for my Nativescript code is GET. An example from the IIS log entry is below:
2019-07-01 12:42:52 xxx.xxx.xxx.xxx GET /Service.asmx/SaveAnswers -
443 - xxx.xxx.xxx.xxx
Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_13_6)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Brackets/1.14.0+Chrome/51.0.2704.103+Safari/537.36
- 200 0 0 62 2019-07-01 12:57:19 xxx.xxx.xxx.xxx POST /Service.asmx/SaveAnswers - 443 - xxx.xxx.xxx.xxx PostmanRuntime/7.15.0 -
200 0 0 359
My code to upload the JSON text is:-
function uploadJson(jsontxt){
console.log(jsontxt);
http.request = ({
url: "https://myURL/Service.asmx/SaveAnswers",
method: "POST",
headers: {"Content-Type": "application/json"},
content: jsontxt
}).then((response) => {
// var result = response.content.toJSON();
// console.log(result);
console.log("Finished upload");
}, (e) =>{
console.log("JSON upload error: "+e);
})
}
Changed the log IP address and my URL for security. I do not get any errors but do not understand how postman is POST and my script is GET in the logs

Bad request returned when using the DELETE curl method with Guzzle and sendgrid

I'm trying to use the sendgrid v3 API to purge bounces, it works fine when using CURL in CLI, here is the command:
curl -v -X DELETE -d '{"delete_all": true}' -H "Content-Type: application/json" -H "Authorization: Bearer SG.mykey" "https://api.sendgrid.com/v3/suppression/bounces"
But when trying to launch it with Symfony2 / Guzzle, I am getting a bad request error, however the request seems OK, here is the output of (string) $request:
"""
DELETE /v3/suppression/bounces HTTP/1.1\r\n
Host: api.sendgrid.com\r\n
Authorization: Bearer SG.mykey\r\n
User-Agent: Guzzle/3.9.3 curl/7.35.0 PHP/5.5.9-1ubuntu4.17\r\n
Accept: application/json\r\n
Content-Length: 20\r\n
\r\n
{"delete_all": true}
"""
And the exception:
[Guzzle\Http\Exception\ClientErrorResponseException]
Client error response
[status code] 400
[reason phrase] BAD REQUEST
[url] https://api.sendgrid.com/v3/suppression/bounces
When using the GET method, it works correctly and it returns me all the bounces.
Here is the guzzle code:
$request = $this->httpClient->delete('/v3/suppression/bounces', null, '{"delete_all": true}');
$response = $request->send();
The http client is a service initialized with the https://api.sendgrid.com base URL.
Your problem I think is that you are sending 3 params to delete when it only has two, instead what you need to do is pass the body in options array.
$response = $this->httpClient->delete(
'/v3/suppression/bounces',
[
'body' => json_encode(['delete_all', true]),
'Authorization' => 'Basic ' . base64_encode($username . ':' . $password),
'content-type' => 'application/json'
]
);
Guzzle options docs
Answering to myself. The problem was pretty obvious: The content-type header was not set, the "accept" one was. I didn't care about this header because you don't have to pass it when using the GET method for this API. So now when debugging my request object I have:
"""
DELETE /v3/suppression/bounces HTTP/1.1\r\n
Host: api.sendgrid.com\r\n
Authorization: Bearer SG.mykey\r\n
Content-Type: application/json\r\n
Content-Length: 20\r\n
User-Agent: Guzzle/3.9.3 curl/7.35.0 PHP/5.5.9-1ubuntu4.17\r\n
Accept: application/json\r\n
\r\n
{"delete_all": true}
"""

How to get JSON back from HTTP POST Request (to another domain)

I'm trying to use the API on a website, here's the part of the manual:
Authenticated Sessions (taken from here)
To create an authenticated session, you need to request an authToken from the '/auth' API resource.
URL: http://stage.amee.com/auth (this is not my domain)
Method: POST
Request format: application/x-www-form-urlencoded
Response format: application/xml, application/json
Response code: 200 OK
Response body: Details of the authenticated user, including API
version.
Extra data: "authToken" cookie and header, containing the
authentication token that should be
used for subsequent calls.
Parameters: username / password
Example
Request
POST /auth HTTP/1.1
Accept: application/xml
Content-Type: application/x-www-form-urlencoded
username=my_username&password=my_password
Response
HTTP/1.1 200 OK
Set-Cookie: authToken=1KVARbypAjxLGViZ0Cg+UskZEHmqVkhx/Pm...;
authToken: 1KVARbypAjxLGViZ0Cg+UskZEHmqVkhx/PmEvzkPGp...==
Content-Type: application/xml; charset=UTF-8
QUESTION:
How do I get that to work?
I tried jQuery, but it seems to have problem with XSS. Actual code snippet would be greatly appreciated.
p.s.
All I was looking for was WebClient class in C#
You need to put application/json in your Accept header, this tells the server you want it to respond in that format - not xml.
I am using rails to extract the same authentication token cookie from stage.amee.com/auth as mentioned above. it took a bit of experimentation before I created and customised the correct request object that returned a 200 OK, with the authtoken as a cookie. i haven't found an effective method of reading the request object or I would post exactly what it looks like. here is my ruby code from the app's controller
#define parameters
uri=URI.parse('http://stage.amee.com')
#path = '/auth'
#login_details = 'username=your_username&password=your_password'
#headers = {'Content-Type' => 'application/x-www-form-urlencoded', 'Accept' => 'application/json'}
#create request object
req = Net::HTTP.new(uri.host, uri.port)
#send the request using post, defining the path, body and headers
resp, data = req.post(#path, #login_details, #headers)
#print response details to console
puts "response code = " << resp.code
puts "response inspect = " << resp.inspect
resp.each do |key, val|
puts "response header key : " + key + " = " + val
end
puts "data: " + data

Resources