JavaScript - API request (Access-Control-Allow-Origin error) - javascript

The guy responsible for API requests is gone for the week, so nothing can be done on server side.
fetch("https://url.com/api/login/", {
method: "post",
headers: {
// 'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: JSON.stringify({
username: "test#mail.com",
password: "123"
})
}).then(function (response) {
return response.json();
}).then(function (myJson) {
console.log(myJson);
});
It works on Postman, but as I heard, Postman doesn't follow the same security as browsers, therefore this isn't an issue for Postman. But I doubt this is the case, as the authors php-solution works fine.
This is an example of php-solution that works (He wrote it):
function login($username, $password) {
$curl = curl_init(); curl_setopt_array($curl, array(
CURLOPT_URL => "https://url.com/api/login/",
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_MAXREDIRS => 10,
CURLOPT_POSTFIELDS => "username=".$username."&password=".$password,
CURLOPT_HTTPHEADER => array(
"cache-control: no-cache",
"content-type: application/x-www-form-urlencoded"),
));
$response = curl_exec($curl);
curl_close($curl);
$authdata = json_decode($response);
if ($authdata -> success) {
//success
return true;
} else {
//fail
return false;
}
}
What's missing in my code? How can I make it work like his php solution. (Have no experience in php).
Any help is much appreciated.
EDIT:
What worked on Postman:
Raw json format in Body.
Adding values as Key and Value in x-www-form-urlencoded

To solve this error you can do 3 things:
Add your origin server side.
Run your javascript on the same domain.
Check this answer for disabling same origin policy in chrome. This will allow you to test your code until the guy responsible for de API returns.

Related

Response on Postman works, in pure JS I got: "Error: SyntaxError: Unexpected token < in JSON at position 0"

I have strange situation, while working with my api.
Workflow for now is that I am sending my JSON to my server. My server is changing one information inside that object and response with modified one JSON. Problem is happening when I am trying to accept my server response in javascript.
Error: SyntaxError: Unexpected token < in JSON at position 0
Also When I am doing the same request in Postman I am getting my response:
{
"message": {
"width": 60,
"height": 60,
"pixels": [
"#f0f0f0",
"#f0f0f0",
"#f0f0f0",
"#222888",
"#f0f0f0",
"#f0f0f0",
"#f0f0f0",
"#f0f0f0",
...
I have on idea why my browser act differently.
Here's my JS code on how I sending and retrieving data:
startLoad() {
fetch('http://localhost:8080/pixel-editor/save', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({picture: this.picture}),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
}
Here's my server code in PHP:
public function save(Request $request) {
$data = json_decode($request->post()->get('picture'), true);
$data['pixels'][3] = "#222888";
header('Content-type: application/json');
echo json_encode( ['message' => $data] );
return 1;
}

Set cookie not appearing in Axios and Fetch

I am using my API endpoint for authentication and setting a http only cookie with Lexik JWT token in the response when testing it with postman everything works fine the cookie gets set properly thing to note is that CORS is enabled with Nelmio Cors Bundle.
nelmio_cors:
defaults:
allow_credentials: true
origin_regex: true
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
allow_headers: ['Access-Control-Allow-Origin', 'X-Requested-With', 'X-HTTP-Method-Override', 'Content-Type', 'Accept']
expose_headers: ['Link']
max_age: 3600
Here is the LexikEvents::AUTHENTICATION_SUCCESS
<?php
namespace App\EventSubscriber;
use Symfony\Component\HttpFoundation\Response;
use Lexik\Bundle\JWTAuthenticationBundle\Events as LexikEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
use Symfony\Component\HttpFoundation\Cookie;
class LexikLoginSuccessSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
LexikEvents::AUTHENTICATION_SUCCESS => ['onAuthenticationSuccess']
];
}
public function onAuthenticationSuccess(AuthenticationSuccessEvent $event)
{
/** #var Response $response */
$response = $event->getResponse();
$hourLater = (new \DateTime())->modify('+1hours');
$cookie = new Cookie('jwt_token', $event->getData()['token'], $hourLater);
$response->headers->setCookie($cookie);
}
}
And at last is the fetch and axios config
const onSubmit = e => {
e.preventDefault();
fetch('http://127.0.0.1:8000/api/get-cookies', {
method: 'POST',
credentials: 'include',
body: JSON.stringify({
username: form.username,
password: form.password,
}),
headers: {
"Content-Type": "application/json",
}
})
.then( response => response.json()).then( json => console.log(json)).catch( error => console.log(error));
Axios.post('http://127.0.0.1:8000/api/get-cookies', {
username: form.username,
password: form.password,
}, {
withCredentials: true,
maxRedirects: 0,
headers: {
"Content-Type": "application/json",
}
}).then(response => console.log(response)).catch( error => console.log(error));
}
After firing the onSubmit function the response is indeed a JWT token but SET COOKIE header is not present and the cookies are not set.
After 3 hours of research in axios docs, reading plenty of similar questions suggesting different fixes and testing I finally came down to these things:
Step 1:
For AXIOS make sure to set the withCredentials in config to true(you are probably missing it if not you're fine just go to next step)
config = { ...yourConfig, withCredentials: true }
Axios.post(url, data, config).then.....
Note that the maxRedirects is not required(code in question)
For FETCH make sure to set credentials in config to "include"(you are probably missing it if not you're fine just go to next step)
config = { ...yourConfig, credentials: "include"
fetch(url, config).then....
Step 2:
This is the fun part, is your server running on something else than localhost domain like a 127.0.0.1:8000 url? Here is the catch Google Chrome and browser based on chrome engine will block cookies from any port postfixed urls(I'm not sure about other browsers but just in case serve your backend on http://localhost - 127.0.0.1:80, use hosts file to map your url to a domain, use localtunnel / ngrok just in case your browser decides to complain about your backend url)
Now you should be all set to store your cross origin http only cookies from response no matter the backend language it should be pretty much the same after you enable CORS.

Redirect page only after success in PHP using Fetch

I'm creating a registration system for a university project. This is using fetch to post the data from the form to a PHP file.
I want to transfer over a $message variable that is echoed at the bottom of my PHP page to know if the registration has been successful, or if an error message has occurred. If I can see that the $message equals my success string, I think I can do an if statement using window.location.href like below? Currently it is redirecting the page for any successful fetch, no matter what the response from PHP is.
I've tried using header("Location: /index.html"); in my PHP file on the success line, but this also didn't work for me. I've spent hours looks for a solution but I really can't get my head around how to pass this variable over.
var myJSON = JSON.stringify(userDetails);
fetch("url/register.php", {
method: "POST",
mode: "no-cors",
cache: "no-cache", /
credentials: "same-origin",
headers: {
"Content-Type": "application/json"
},
redirect: "follow",
referrer: "no-referrer",
body: myJSON
}).then((response) => {
if (response.ok) {
return response.blob();
} else {
throw new Error("Things went poorly.");
}
}).then((response) => {
//Fetch was successful!
window.location.href = "url/index.html";
}).catch((error) => {
console.log(error);
});
You can try this in your php file:
$response = [ message => "the message", success => true ];
echo json_encode($response);
You will receive a JSON response which you will use it to validate if the request was successful.
In your javascript code, you have to parse this JSON response to an literal object like:
fetch(url, {params})
.then(response => response.json())
.then((response) => {
if (response.success) {
// fetch was succesfull!
} else {
// response.message could be used to show what was wrong
throw new Error(response.message);
}
})
One way to accomplish this is to use the URL's query parameters to pass the success message. For example:
window.location.href = "url/index.html?login=success";
Then on the .html page you would have code that looks for the login query parameter and displays something if it's equal to success.

Decompress GZIP string response from a PHP server in NodeJS

I have an existing PHP code that is doing a curl request to a 3rd-party PHP server.
The 3rd-party server returns a GZIP string.
In PHP, I can use gzdecode to decode the gzip string.
How can I do it in NodeJS/Javascript? I tried using decompress-response with no avail.
Also tried using got instead of request, enabled auto-decompress, also doesn't work.
Edit: Also tried zlib and pako, also doesn't work.
Sample Code [ PHP ]
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $params,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 3000000,
CURLOPT_ENCODING => '',
CURLOPT_CUSTOMREQUEST => "GET",
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo false;
} else {
$response = gzdecode($response);
echo $response;
}
This is the solution that works for me.
I used got instead of axios because I can't get it to work there.
I set my request options:
const requestOptions = {
encoding: null, // this is important
headers: {
"Accept-Encoding": "gzip",
}
...
};
Don't forget that encoding: null line, because without that, the response will be automatically converted to a string. ( We need a buffer to make this work )
Then I created a function like this to handle my request:
const zlib = require("zlib");
async function performRequest(url, options) {
try {
const response = await got(url, options);
if (response.headers["content-encoding"] === "gzip") {
const body = response.body;
try {
const dezziped = zlib.gunzipSync(response.body);
response.body = JSON.parse(dezziped.toString());
} catch (error) {
response.body = body;
}
}
return response.body;
} catch (error) {
return error;
}
}
Note: This is a synchronous operation, you can use gunzip instead if you want to do the async way.

Axios POST returns "Network Error" even if status is 200 and there's a response [duplicate]

This question already has answers here:
XMLHttpRequest cannot load XXX No 'Access-Control-Allow-Origin' header
(11 answers)
Closed 4 years ago.
I'm using Vue JS 2.5 with Axios:
"vue": "^2.5.17",
"vue-axios": "^2.1.4",
"axios": "^0.18.0",
I'm trying to make a POST call just like this:
const data = querystring.stringify({
'email': email,
'password': password,
'crossDomain': true, //optional, added by me to test if it helps but it doesn't help
});
var axiosConfig = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
// "Access-Control-Allow-Origin": "*",
'Accept': '*',
}
};
axios.post(url, data, axiosConfig)
.then((response) => {
console.log('response');
console.log(response);
})
.catch((error) => {
console.log('error');
console.log(error);
});
I tried also without the "axiosConfig" parameter. But everytime it enters on the catch, with the message: Error: Network Error
In the browser's Network tab I get a 200 status and a good Response (with a valid json), it basically works but Axios returns error and no response.
The console also outputs this warning: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://url/api/page. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
I tried to make the same call from Postman and it works well. The difference is that Axios is sending the headers: "Origin" and "Referrer" with my localhost:8080, which is different than the API url that I'm calling.
How can I make this call from axios without getting this error? Thanks.
EDIT
It works if I'm using PHP:
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://myurl",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "------WebKitFormBoundaryTrZu0gW\r\nContent-Disposition: form-data; name=\"email\"\r\n\r\nemail\r\n------WebKitFormBoundaryTrZu0gW\r\nContent-Disposition: form-data; name=\"password\"\r\n\r\npassword\r\n------WebKitFormBoundaryTrZu0gW--",
CURLOPT_HTTPHEADER => array(
"Postman-Token: 62ad07e5",
"cache-control: no-cache",
"content-type: multipart/form-data; boundary=----WebKitFormBoundaryTrZu0gW",
"email: email",
"password: password"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
I just copy-pasted the generated Postman call and tried it from a different page and it works well. So it's not a CORS problem, it's a problem with my Javascript call.
You need to enable CORS on your API, which language/framework are you using?
Here's some reference:
https://enable-cors.org/

Categories