First of all, I am pretty new to working with Rest APIs and tokens, I have learned the basics of Django and React and I've kinda made some projects using those, so I'm giving a shot at making a website with both of them.
Since, registering is simple with django-rest-auth, it's been handled with just POSTING the user data. However, when making a login, the token was returned and I simply do not know how to handle it. I have searched many articles about tokens, but they were all about JWT which returns an access token and a refresh token. However, django-rest-auth returns a single token and I saved it inside a cookie using a 'universal-cookie' in React. Since I'm making an e-commerce website, and I do want to add some functionality to the website by enabling the users to change their email, last name, first name and username, I do need to know the username of the account as it is unique.
So, I am wondering if it would be alright and a good practice to save the username inside a cookie, again using a universal-cookie. It'd also be appreciated if someone shows me a good article about "what to do with a token" or how to utilize it because I have little to none knowledge about it.
const handleLogin = async (e) => {
e.preventDefault();
const csrftoken = getCookie("csrf");
const url = "http://localhost:8000/rest-auth/login/";
const cookies = new Cookies();
await fetch(url, {
method: "POST",
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username,
password: password,
}),
}).then((response) => {
setResponseCode(response.status);
response.json().then((key) => {
cookies.set("token", key.key);
setIsClicked(true);
});
});
};`
Here is how I've been storing the token I get, and using it from navbar or other react components to check if the user has been logged in or not.
I'm trying to fetch data in my React app from an Django server with Django Rest Framework, I'm using the built in token authentication.
componentDidMount() {
let headers = {
"content-type": "application/json",
"authorization": "Token <token is here>"
};
fetch('http://localhost:8000/api/stats/', {
headers: headers,
})
.then(res => res.json())
.then((data) => {
this.setState({ games: data })
})
.catch(console.log)
}
Inspecting the page in Chrome reveals this
This looks like two separate requests but I'm not sure.
The requests that has status (failed) has the headers I provided in React with in it. This request seems to have failed completely, it did not even reach the server.
The other request that has the status 401 doesn't have the headers I provided. The request did however get a response from the server.
Anyone have an idea what's wrong?
Solved by David Nováks comment:
Installed django-cors-headers in my django project.
Added my React servers hosting address to CORS_ORIGIN_WHITELIST.
I'm building an api with api platform and a front with react (using the react template of apiplatform). I configured authentification and a return to client with httponly cookie which contains the jwt. But when my front does a request, it does not send this cookie... And I absolutly don't know why, I thought it was automaticaly done by browser till it's on same domain.
Here is an example of the network history from my client :
my app is running on https://localhost:3000/
Do you see something wrong in theses request ? Or does anyone has an idea of what it could come from ?
My app and api are using https and have a valid certificate...
If you need any additional info, feel free to ask, and thanks all !!!
I assume you work with either xhr or fetch.
Cookies ignore ports, but cross origin policy does not.
You work with two urls (http://localhost:8443 and http://localhost:3000). So your app is making cross origin request because ports differ.
xhr requires to set its withCredentials property to true in order to send cookies with cross-origin request.
fetch requires its credentials parameter to be set to include.
Server side, set the Access-Control-Allow-Credentials to true.
Also note that your cookie is samesite=strict. In production, if you use two domains for your app and your api, it will never be sent.
The real question here is why using a cookie instead of Authorization header ?
Ok, I didn't know... I've found nothing on it when I was trying to solve my prob.
I'm using cookie httponly because :
I want to try it :D
Lot of security articles says that it's more secure because client api can't access theses cookies, browser manages it. It seems to counter xss and stealth of cookies, but if my cookie is stored with localforage, I think I do not have this problem, but with localStorage I do, no ?
It's cool no ! I've done too many project with classic bearer auth, I can improve it now
A big thanks for your nice answer rugolinifr !
Okay, I'm still having my issue finally... My browser is not sending the cookie...
My auth request returning bearer cookie (valid, tested with postman)
My cookie received from auth request
My GET request without that auth cookie
I'm missing something but I don't find it...
I've set credentials, Access-Control-Allow-Credentials, samesite is 'none' for sending it everywhere. Is there something else to do ? Or maybe I'm doing a stupid little thing that is wrong ?
I can't answer in comment because there's code...
So, It's managed by the react admin base of api-platform (https://api-platform.com/docs/admin/), but my config is like this :
const fetchHeaders = {
credentials: 'include',
};
const fetchHydra = (url, options = {}) =>
baseFetchHydra(url, {
...options,
headers: new Headers(fetchHeaders),
});
const apiDocumentationParser = (entrypoint) =>
parseHydraDocumentation(entrypoint, { headers: new Headers(fetchHeaders) }).then(
({ api }) => ({ api }),
(result) => {
...
},
);
const dataProvider = baseHydraDataProvider(entrypoint, fetchHydra, apiDocumentationParser, true);
So, all get, post etc request for datas are based on this conf
But my first call for authentication is done like that :
login: ({ username, password }) => {
const request = new Request(`${entrypoint}/authentication_token`, {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
return fetch(request).then((response) => {
if (response.status < 200 || response.status >= 300) {
localStorage.removeItem('isAuthenticated');
throw new Error(response.statusText);
}
localStorage.setItem('isAuthenticated', 'true');
});
},
ok, I've found solution :
add credentials to the auth request, if header is not added, cookie won't be stored by browser.
And second point :
const fetchHydra = (url, options = {}) =>
baseFetchHydra(url, {
...options,
credentials: 'include',
});
credentials: 'include' is not in headers option... Nice !
Faced the same problem.Tried out many solutions but didn't work.At last found out it was the cors configuration of node backend that was causing the problem. Configured cors like the following way to solve the problem.
const corsConfig = {
origin: true,
credentials: true,
};
app.use(cors(corsConfig));
app.options('*', cors(corsConfig));
I can retrieve a key after logging in through my Django REST API, but then I am wondering how I should store that key. I'm not really using Django, but I imagine I have to store the cookie myself then or something. I'm using Axios for VueJS to interact with the API. I am using django rest auth to get the token.
Keep it easy to Local Storage or SessionStorage after get token from Backend-Response and then pass it in every Request in Axios Header-config.
DRF auth Token get it from header with this format:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
Add this section code to your Axios configs to get token from LocalStorage.
Axios.interceptors.request.use(
(config) => {
let token = window.localStorage.getItem("token");
if (token) {
config.headers["Authorization"] = `Token ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
i need to understand something.
I've a rest server on server A (django-rest-framework). An app on server B (angularjs) requests the rest server.
I want to add authentication. each time i request http://serverA/api-auth/login/, it returns 403 because i don't pass the csrf token.
So, in my app.js, i've added :
.run(function($http, $cookies) {
$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
});
now, fine, i can send the csrf token.
My question is, how can i populate the cookie ? Do i have to do a get() to obtain the token before posting ? Because currently my cookie is empty :(
Thank you
You cannot use SessionAuthentication method if you don't share the same domain. In your case the OAuth2Authentication is the way to go.
Assuming your angularjs code using jquery ajax to post, you can put the csrf token into the meta tag
<!--<meta name="csrf-token" content="{{csrf_token}}">-->
Then setup your jquery ajax method to include the csrf token.
jQuery(document).ajaxSend(function(event, xhr, settings) {
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
//var token = $('meta[name="csrf-token"]').attr('content');
var csrftoken = $.cookie('csrftoken');
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}..............
});