Set cookie not appearing in Axios and Fetch - javascript

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.

Related

Error in with fetch call "net::ERR_CONNECTION_RESET" and "Uncaught (in promise) TypeError: Failed to fetch"

I'm doing a fetch in react and getting these errors and I cannot figure out how to fix it. I am using TypeScript and a C# rest service. It works fine in postman but having these issues in the client.
I have tried disabling all browser extensions and tried other browsers also. This did not work.
I'm expecting to receive a status "201" back from the REST Call.
on button click
<Button className="w-100 btn btn-lg btn-primary" type='submit'onClick={e => {e.preventDefault() handleForm()}}>Register</Button>
javascript:
` async function handleForm() {
console.log(JSON.stringify({ ...registration }))
const endpoint = 'http://localhost:44309/api/Users/Register';
const data = {
email: registration.email,
userName: registration.username,
password: registration.password,
confirmPassword: registration.passwordConfirmation,
userTypeId: 4
};
// Default options are marked with *
const response = await fetch(endpoint,
{
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return response.json();
}`
here is the C# rest method:
` [HttpPost("[action]")]
public IActionResult Register([FromBody] ApplicationUser applicationUser)
{
var userExists = _dbContext.AppUsers.FirstOrDefault(u => u.Email == applicationUser.Email);
//todo: add validation code
if (userExists != null)
{
return BadRequest("User with the same email address already exists");
}
applicationUser.Password = HashService.HashPassword(applicationUser.Password);
#if (!DEBUG)
applicationUser.ConfirmPassword = "True";
#endif
_dbContext.AppUsers.Add(applicationUser);
_dbContext.SaveChanges();
return StatusCode(StatusCodes.Status201Created);
}`
There was a few issues with this error that I found. 1) was a cors issue, so I added cors to my c# code. 2) My API was using https and the react client was running under http. I needed to configure the create-react-app to run over https instead of http once I got that configured it worked great. I had a difficult time getting the https to work on windows on the client until I found this post. https://medium.com/#praveenmobdev/localhost-as-https-with-reactjs-app-on-windows-a1270d7fbd1f

Javascript: multiple fetches

I have the following problem. I have a server who implemented his own authentication process. For that they take the credentials as body.
async function authenticate(){
var cred = JSON.stringify({
"email": "user#test.de",
"password": "1234"
});
const response = await fetch("http://.../rest/auth/login", {
method: 'POST',
mode: 'cors',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
body: cred
})
}
The response has the code 200 and says "login successfull!" and has a SET-COOKIE header has
the session cookie.
Now I've a second request with which I load the actual data. But when I try this and I get the response code 401: Unauthorized.
async function getData(){
const now = new Date()
const response = await fetch(`http://.../rest/....`)
console.log(response)
const data = await JSON.parse(response)
console.log(data)
}
authenticate()
.then((res) => {
console.log(res)
getData().then(reso => console.log(reso))
})
Im not sure how to handle this problem.
I've already checked all responses and everything worked except that the second request doesnt use the Cookie in their request. I've also tried to use the WithCredentials=true option but without success.
EDIT
I changed the credentials from
credentials: 'cross-origin' -> credentials: 'include'
Since im calling an extern Server from localhost.
But i get still an 401 Error.

Why is Axios not using the Content-Type header and converting the request method to GET when PATCHing to a specific URL?

I have inherited a codebase using Axios, and I am otherwise unfamiliar with the library. This is a Node application, and I'm attempting to send a PATCH request to a third party API. Axios is configured using the following:
const axios = require('axios').create({
baseURL: process.env.API_URL,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
auth: {
username: process.env.API_USER,
password: process.env.API_PW,
},
});
I then try to make the following PATCH request:
const data = {
fields: {
field_a: 'yes',
field_b: 'no',
},
};
try {
const res = await axios.patch(`/user/${user.id}`, data, {
headers: {
'Content-Type': 'application/json'
}
});
return res;
} catch (err){
console.error(err);
}
From what I can see I'm just redefining the Content-Type header when making the patch call, but that was just an attempt to figure this out. It doesn't work either way. What I see in the response object's config property is the following (most of it is excluded):
{
headers: {
Accept: "application/json"
User-Agent: "axios/0.19.0"
},
method: 'patch',
}
Looking at the request property of the same response object I see that the method there is listed as "GET" with the Content-Type header also not listed there. It appears as though the Content-Type header is being stripped and the method is being changed to GET.
If I change nothing but the URL destination to /userWRONGPATH/${user.id} I receive, as expected, a 404 response, but the response object's config data includes this:
{
headers: {
Accept: "application/json"
Content-Length: 105
Content-Type: "application/json"
User-Agent: "axios/0.19.0"
}
}
The response object's request method is now the expected 'PATCH'. I am unsure why the patch method would work for other paths if that is in fact what is happening here.
Hello I think that the problem could be related of send the header again in Axios you define a config and that is added to all the requests.
This is an example that I use to order the project with axios.
// Axios custom config
const axiosInstance = axios.create({
baseURL: urlBase,
// timeout: 1000,
headers: { 'Content-type': 'application/json' },
});
export const apiPatchRequest = (url, id, obj) => (
axiosInstance.patch(`${url}/${id}`, obj)
);

How to add an interceptor to each request to keep ie from caching requests

Internet explorer caches http requests. Instead of manually adding a header to each individual function I want to do something like this
axios.interceptors.request.use((config): AxiosRequestConfig => {
return addNoCachingHeader(config);
});
and
const addNoCachingHeader = (config: AxiosRequestConfig): AxiosRequestConfig => {
return { ...config, headers: { ...config.headers, Pragma: "no-cache"} };
};
is there a simple way to keep IE from caching requests without going back through my whole app and adding headers to each individual request?
I think using a generic client approach would be better here.
const client = axios.create({
baseURL,
timeout: 5000,
responseType: 'json',
headers: { Pragma: "no-cache" },
});
And to use in other places import client and call client.get or client.post
if you want to override headers at some point, make this to a function
const client = (headers) => axios.create({
baseURL,
timeout: 5000,
responseType: 'json',
headers,
});
and use as client({ Pragma: 'no-cache' }).get(...)
Create a middleware with the header you want all your requests to use and then have your app use it with app.use(yourHeaderMiddleware(req, res, next)).

When using mode: no-cors for a request, browser isn’t adding request header I’ve set in my frontend code

in my React app, I have the following API POST to allow the user to edit their profile (name and image).
static updateProfile(formData, user_id) {
const request = new Request(`http://localhost:4300/api/v1/profiles/${user_id}`, {
headers: new Headers({
'Authorization': getBearerToken()
}),
mode: 'no-cors',
method: "POST",
body: formData
});
return fetch(request).then(response => {
return response.json();
}).catch(error => {
return error;
});
}
The problem with the above is the header with the Authorization token is not being sent in the POST...
How can I get the Authorization header to be send in the fetch request above?
FYI, for non-multipart forms, the authorization token is sent successfully like so:
static loadProfile(user_id) {
const request = new Request(`http://localhost:4300/api/v1/profiles/${user_id}`, {
headers: new Headers({
'Authorization': getBearerToken(),
'Accept' : 'application/json',
'Content-Type' : 'application/json',
})
});
return fetch(request).then(response => {
return response.json();
}).catch(error => {
return error;
});
}
You can’t use no-cors mode if you set any special request headers, because one of effect of using it for a request is that it tells browsers to not allow your frontend JavaScript code to set any request headers other than CORS-safelisted request-headers. See the spec requirements:
To append a name/value pair to a Headers object (headers), run these steps:
Otherwise, if guard is "request-no-cors" and name/value is not a CORS-safelisted request-header, return.
In that algorithm, return equates to “return without adding that header to the Headers object”.
Authorization isn’t a CORS-safelisted request-header, so your browser won’t allow you to set if you use no-cors mode for a request. Same for Content-Type: application/json.
If the reason you’re trying to use no-cors mode is to avoid some other problem that occurs if you don’t use, the solution is to fix the underlying cause of that other problem. Because no matter what problem you might be trying to solve, no-cors mode isn’t going to turn out to be a solution in the end. It’s just going to create different problems like what you’re hitting now.
By using below code you can make a fetch request with Authorization or bearer
var url = "https://yourUrl";
var bearer = 'Bearer '+ bearer_token;
fetch(url, {
method: 'GET',
withCredentials: true,
credentials: 'include',
headers: {
'Authorization': bearer,
'X-FP-API-KEY': 'iphone',
'Content-Type': 'application/json'}
}).then((responseJson) => {
var items = JSON.parse(responseJson._bodyInit);
})
.catch(error => this.setState({
isLoading: false,
message: 'Something bad happened ' + error
}));

Categories