Hi and thanks in advance,
I've successfully setup JWT authentication using django-rest-framework-simplejwt and React but I'm still very confused about the advantages and specifically database hits.
I'm using simplejwt with ROTATE_REFRESH_TOKENS': True 'BLACKLIST_AFTER_ROTATION': True, when my access_token expire I ask for a new one through /api/token/refresh and it blacklist old tokens, I'm using axios interceptors to perform that automatically.
But in my understanding the benefits of JWt is that they are stateless, meaning I don't have to hit the user database table everytime I want to make an a request that needs authentication permission. The problem is even with a simple view like this :
class IsConnecteddAPI(APIView):
permission_classes = [permissions.IsAuthenticated]
def get(self, request, *args, **kwargs):
data = "You seem to be connected"
return Response(data, status=status.HTTP_200_OK)
using django-silk I see that it still performs 1 query to my user table when I call it with a valid access token, is that normal ? If so why do we say that JWT are stateless ? I'm really confused.
That's my axios code if needed :
import axios from "axios";
const baseURL = "http://localhost:5000";
const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 5000,
headers: {
Authorization: localStorage.getItem("accesstoken")
? "JWT " + localStorage.getItem("accesstoken")
: null,
"Content-Type": "application/json",
accept: "application/json",
},
});
const axioAnonymousInstance = axios.create({
baseURL: baseURL,
timeout: 5000,
headers: {
"Content-Type": "application/json",
accept: "application/json",
},
});
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async function (error) {
const originalRequest = error.config;
if (typeof error.response === "undefined") {
alert(
"A server/network error occurred. " +
"Looks like CORS might be the problem. " +
"Sorry about this - we will get it fixed shortly."
);
return Promise.reject(error);
}
if (
error.response.status === 401 &&
originalRequest.url === baseURL + "token/refresh/"
) {
window.location.href = "/login/";
return Promise.reject(error);
}
if (
error.response.data.code === "token_not_valid" &&
error.response.status === 401 &&
error.response.statusText === "Unauthorized"
) {
const refreshToken = localStorage.getItem("refreshtoken");
if (refreshToken) {
const tokenParts = JSON.parse(atob(refreshToken.split(".")[1]));
// exp date in token is expressed in seconds, while now() returns milliseconds:
const now = Math.ceil(Date.now() / 1000);
console.log(tokenParts.exp);
if (tokenParts.exp > now) {
return axioAnonymousInstance
.post("/api/token/refresh/", { refresh: refreshToken })
.then((response) => {
localStorage.setItem("accesstoken", response.data.access);
localStorage.setItem("refreshtoken", response.data.refresh);
axiosInstance.defaults.headers["Authorization"] =
"JWT " + response.data.access;
originalRequest.headers["Authorization"] =
"JWT " + response.data.access;
return axiosInstance(originalRequest);
})
.catch((err) => {
// redirect ro /login here if wanted
console.log("axios Safe Instance error");
console.log(err);
// window.location.href = "/login/";
});
} else {
console.log("Refresh token is expired", tokenParts.exp, now);
window.location.href = "/login/";
}
} else {
console.log("Refresh token not available.");
window.location.href = "/login/";
}
}
// specific error handling done elsewhere
return Promise.reject(error);
}
);
export { axiosInstance, axioAnonymousInstance };
( I know I shouldn't use localStorage but whatever )
and I would typically just call this function to make the simple request to the view written above :
const IsConnected = () => {
axiosInstance
.get("/api/is_connected/")
.then((response) => {
if (response.status === 200) {
console.log(response.data);
console.log("Is connected : CONNECTED ");
} else {
console.log("IS connected : not connected");
}
})
.catch((error) => {
console.log("Is connected : NOT CONNECTED");
console.log(error);
});
};