Axios sending double CSRF-Token [CSRF-Token mismatch] - javascript

I have a basic laravel/vue with sanctum implementation. The problem is straight forward:
I'm sending a token-request and logging-in the user. The server sends back a new token. Axios adds this token, but adds another token that is always the same and expired.
Code:
await APIClient.get("/sanctum/csrf-cookie")
return APIClient.post("/api/user/login", payload);
DevTools/Network tab:
csrf-cookie request => response-headers has the valid XSRF-TOKEN
login request => request-headers, SET-COOKIE property has XSRF-TOKEN (old expired value) ; laravel_session ; XSRF-TOKEN (new valid value)
My problem is with the old value. There's no place in my code for this to be added at all.
This's my axios client
const APIClient = axios.create({
baseURL: constants.PATHS.url,
withCredentials: true, // required to handle the CSRF token
});
Your help is much appreciated.

Sorry I don't quite understand your question, do you not know how to add (old expired value) or (new valid value) to the header of axios?
I recommend using axios as a property of window. It is easier to change its properties:
window.axios.defaults.headers.common['token'] = "aaa"; //add a header

I have found an actual solution. You should clear the cookies of the document whenever you logout, in this way, the token will always be the new one and the old one cannot be duplicated. I've seen this bug happens in some cases.

Related

Is there a way to fetch data only when token Is available?

Good day all,
I have a little issue in my react code and I hope someone here would be able to help me fix it.
Basically I am fetching data from an endpoint (multiple)
Am using axios and react query.
In my get request, I have security headers and one is Authorization.
User is authorized by a token sent to session storage when he / she login.
I get the token from session storage and place it in my authorization header.
My problem now is before the user login, the token === null
and my get request runs before the user logins leaving it with an empty token.
When the user finally logins, no data is fetched because what is in the authorization header is null but when I refresh the page, the data is fetched because at that time the token was already delivered and not = null.
I don't know if you fully understand cause am pretty bad at explaining but if you do, is there anyway to fix the issue.
thanks...
If you're using RTK, you may use skip:
const [skip, setSkip] = useState(true);
const { isUninitialized, isSuccess, isLoading } = useSomeQuery(token, {
skip
});
and set skip to false when the token is available
since the token is a dependency to your fetch, it should go to the queryKey. Then you can disable the query via the enabled option for as long as the token is null.
const token = useTokenFromLocalStorage()
useQuery(
["user", token],
() => fetchUser(token),
{
enabled: !!token
}
)
if the token updates (changes from null to a value), and that re-renders your component, the query will run automatically because the query key changes. The query will not run if the token is null.

How to properly deal with JWT in headers

I'm trying to set up JWT in my project (NodeJs Express for the backend, and Javascript in frontend).
So in my backend, I send my token like this when the user logs in :
[...]
return res.send({ token, id: userId })
[...]
And in my frontend, I set the Bearer token in my header like this :
const data = {
email,
password
}
await instance.post('/signin', data)
.then((res) => {
instance.defaults.headers.common['authorization'] = `Bearer ${res.data.token}`
window.location = './account.html'
})
.catch((err) => console.log(err))
My questions are :
Did I do it correctly ?
When I'm redirected to the account.html page, how do I retrieve the token that was set while the log in was made ?
When you assign to instance.defaults.headers.common['authorization'] then subsequent requests made via Ajax using whatever library (I'm guessing Axios) that is from that page will include the authorization header.
Assigning a new value to window.location will trigger navigation and discard the instance object (with the assigned header value).
(As a rule of thumb, if you are going to assign a new value to window.location after making an Ajax request then you should probably replace the Ajax request with a regular form submission).
You could assign the data to somewhere where you can read it in the account.html page (such as local storage).
That won't be available to the server for the request for account.html itself though.
You could assign the data to a cookie. Then it would be available for the request for account.html, but in a cookie and not an authorisation header.
Note that using JWTs in authorization headers is something generally done in Single Page Applications and not traditional multi-page websites.

Cannot create httponly cookie containing jwt in ASP.NET Core and React

I'm trying to implement an authentication scheme in my app. The controller, more specifically the method, responsible for checking user's credentials and generating jwt which it has to put into the httponly cookie afterward looks as follows
[HttpPost]
[Route("authenticate")]
public async Task<IActionResult> Authenticate([FromBody] User user)
{
var response = await _repository.User.Authenticate(user.Login, user.Password);
if (!response) return Forbid();
var claims = new List<Claim>
{
new Claim("value1", user.Login)
};
string token = _jwtService.GenerateJwt(claims);
HttpContext.Response.Cookies.Append(
"SESSION_TOKEN",
"Bearer " + token,
new CookieOptions
{
Expires = DateTime.Now.AddDays(7),
HttpOnly = true,
Secure = false
});
return Ok();
}
I tested this method in Postman - everything works gently and correctly in there. The cookie is being created as well. Moreover, recently I created an app using Angular where I was using the same authentication method, but with Angular's HTTP module the cookie was being created all the time. Here is what that method looks like in my React app with the usage of Axios
export const authenticate = async (login, password) => {
return await axiosLocal.post('/api/auth/authenticate',
{login, password}).then(response => {
return response.status === 200;
}, () => {
return false;
});
Everything I'm getting in response trying to log in is response code 200. I'm pretty sure it's something about Axios's settings.
Also if someone's curios the variable "axiosLocal" contains the baseURL to the API.
- Update 1
Ok. If I'm not mistaken in order to set a cookie from the response I have to send all the requests with { withCredentials: true } option. But when I'm trying to do that the request is being blocked by CORS, although I had already set a cors policy which has to allow processing requests from any origin like that
app.UseCors(builder => builder.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials());
I just had the same issue. I fixed it.
Problem:
In browsers, the httpOnly cookie was received and not returned to the server
In Postman working
// Problemable server code for settings httpOnly cookie
Response.Cookies.Append("refreshToken", refreshToken.Token, new CookieOptions
{
HttpOnly = true,
Expires = DateTime.UtcNow.AddDays(7),
});
Solution:
On the server .AllowCredentials() and
.SetOriginAllowed(host => true) or
.WithOrigins("https://localhost:3000")
On the client (react, axios) withCredentials:true in the headers
If still not working open the Network tab in DevTools in Chrome(current v.91.0.4472.124), select the failed request and when you put the mouse over the yellow triangle you can see very detailed information why the cookie is blocked.
// End server code for setting httpOnly cookie after following the DevTools warnings
Response.Cookies.Append("refreshToken", refreshToken.Token, new CookieOptions
{
HttpOnly = true,
Expires = DateTime.UtcNow.AddDays(7),
IsEssential=true,
SameSite=SameSiteMode.None,
Secure=true,
});
Finally solved. Passing .SetIsOriginAllowed(host => true) instead of .AllowAnyOrigin() to CORS settings with { withCredentials: true } as an option in Axios request helped me.

Nuxt asyncdata axios on refresh loses auth token

I am calling a get api that gets an array of mail data. It works fine on postman. When I use asyncdata method to get the array. It only works once if user refreshes the page I get 401 error. I pull token from cookies just fine. Normally on non asyncData I do this to set up the header
this.$axios.setHeader('Authorization','Bearer ' + this.$store.state.token);
this.$axios.$post('upload/avatar',formData,{
headers: {'content-type': 'multipart/form-data'}
}).then(res =>{
}).catch(err => console.error(err));{
}
}
This works fine and has no issues
but my asnycData is like this
asyncData(context){
//Cookie has to be read for async to work for now if user disables cookies breaks this page
let token = Cookie.get('token');
context.app.$axios.setHeader('Authorization',`Bearer ${token}`);
return context.app.$axios.$get('get/all/mail').then(mailData =>{
console.log(context.app.$axios.defaults);
let mailMap = [];
//create array to load mail data in
for(let key in mailData){
mailMap.push({...mailData[key]});
}
return{
mailArray:mailMap
}
}).catch(e =>console.error(e));
}
I am trying to make a simple inbox page that can send , delete , and draft messages.
The problem is probably due to the fact that since asyncData is running from the server, it'll lose any browser cookies.
If you're using axios, the nuxt community has setup a middleware module that can be used to automatically inject browser cookies into server requests.

How to set username and password in axios get method header

I want to fetch some data from a server via axios in my react project. When i put the url on browser and hit enter browser ask me username and password and after that, i can see the json data. But i dont know how to set the password and username in axios header in a get method. I have searched it in many forums and pages,especially this link didin't help me: Sending axios get request with authorization header . So finally i tried (many things before this, but i was more confused):
componentDidMount() {
axios.get('http://my_url/api/stb', {auth: {
username: 'usrnm',
password: 'pswrd'
}})
.then(function(response) {
console.log(response.data);
console.log(response.headers['Authorization']);
}).catch(err => console.log(err));
}
And i can not get anything. I get this error in console:
Error: Network Error
Stack trace:
createError#http://localhost:3000/static/js/bundle.js:2195:15
handleError#http://localhost:3000/static/js/bundle.js:1724:14
Actually, the api documentation mentioned that with these words:
If there is no header or not correct data - server's answer will
contain HTTP status 401 Unauthorized and message:
< {"status":"ERROR","results":"","error":"401 Unauthorized request"}
For successful authentification is sufficient to add in every request
header to the API:
Authorization: Basic <base64encode("login":"password")>
The weird thing is, when i use postman, the response send me a "401 unauthorized" response below the body section. But i can not see any 401 errors in browser's console.
Ok i found the solution. As i mentioned in the comments that i wrote for my question, there was a cors problem also. And i figured out that cors problem was appearing because of that i can not authorize correctly. So cors is a nature result of my question. Whatever.. I want to share my solution and i hope it helps another people because i couldent find a clear authorization example with react and axios.
I installed base-64 library via npm and:
componentDidMount() {
const tok = 'my_username:my_password';
const hash = Base64.encode(tok);
const Basic = 'Basic ' + hash;
axios.get('http://my_url/api/stb', {headers : { 'Authorization' : Basic }})
.then(function(response) {
console.log(response.data);
console.log(response.headers['Authorization']);
}).catch(err => console.log(err));
}
And dont forget to get Authorization in single quotes and dont struggle for hours like me :)

Categories