I am authenticating user with http only cookies which contains jwt tokens.
Heres all my settigns require to use cookies.
settings.py
CORS_EXPOSE_HEADERS = ["Content-Type", "X-CSRFToken"]
CORS_ALLOW_CREDENTIALS = True
# CORS_ALLOW_HEADERS = ["Set-Cookie",]
SESSION_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_HTTPONLY = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = "None"
CSRF_COOKIE_SECURE = True
CSRF_TRUSTED_ORIGINS = ["http://localhost:3000", "http://127.0.0.1:3000"]
'ACCESS_TOKEN_LIFETIME': timedelta(days=2),
'REFRESH_TOKEN_LIFETIME': timedelta(days=10),
# HTTPOnly Cookies
"AUTH_COOKIE": "access_token",
"AUTH_COOKIE_REFRESH": "refresh_token",
"AUTH_COOKIE_SAMESITE": "None",
"AUTH_COOKIE_SECURE": True,
"AUTH_COOKIE_HTTP_ONLY": True,
"AUTH_COOKIE_PATH": "/",
"AUTH_COOKIE_DOMAIN": None,
axios.js
import axios from "axios";
import { BASE_URL_BACK_SERVER } from "./_variables";
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.xsrfHeaderName = "X-CSRFToken";
axios.defaults.withCredentials = true;
export const axiosInstanceBack = axios.create({
baseURL: BASE_URL_BACK_SERVER,
timeout: 5000,
headers: {
"Content-Type": "application/json",
},
});
Authenticating user working fine but the problem is when logging out user.
when it makes request for LogoutView it return 200 response.
But after redirecting in frontend again fetches current user which should be available if only user is logged in. When checked to network tab i found that request header for current user's details contain same cookies. Why is that happening? am i missing something here?
LogoutView
class LogoutView(APIView):
def post(self, request):
response = Response()
response.delete_cookie(settings.SIMPLE_JWT['AUTH_COOKIE'])
response.delete_cookie(settings.SIMPLE_JWT['AUTH_COOKIE_REFRESH'])
response.data = {
"status": "success",
"msg": "User logout successfully"
}
return response
logout Request
const logoutUser = () => {
axiosInstanceBack
.post("user/logout/", {}, { headers: { "X-CSRFToken": csrfToken } })
.then((response) => {
mutateCsrf();
setShouldFetch(false);
mutate("user/current/", null, { revalidate: false });
router.push("/login");
})
.catch((error) => console.log(error));
};
Related
So I have a working LOCAL Twitter clone called Hater but cant deploy front end b/c I cant access secured Cookies(https://github.com/mustafabin/hater)
I used Django's built-in Session-based auth I have middleware all set up
LOGIN VIEW
#method_decorator(csrf_protect, name="dispatch")
class LoginView(APIView):
permission_classes = (permissions.AllowAny,)
def post(self, request, format=None):
data = self.request.data
username = data['username']
password = data['password']
try:
user = auth.authenticate(username=username, password=password)
if user is not None:
auth.login(request, user)
return Response({'success': 'User authenticated'})
else:
return Response({'error': 'Error Authenticating'})
except:
return Response({'error': 'Something went wrong when logging in'})
SIGN UP
#method_decorator(csrf_protect, name="dispatch")
class SignupView(APIView):
permission_classes = (permissions.AllowAny,)
def post(self, request, format=None):
data = self.request.data
username = data['username']
password = data['password']
re_password = data['re_password']
tag = data['tag']
try:
if password == re_password:
if User.objects.filter(username=username).exists():
return Response({"error": "Username already exists"})
else:
if len(password) < 6:
return Response({"error": "Password must be at least 6 characters"})
else:
user = User.objects.create_user(
username=username, password=password)
user = User.objects.get(id=user.id)
user_profile = User_profile.objects.create(
user=user, name=username, tag=tag)
return Response({'success': "User created successfully"})
else:
return Response({'error': "Passwords do not match"})
except:
return Response({"error": "Something went wrong signing up"})
I'm aware some of these settings are redundant but ur man got desperate
CORS_ORIGIN_ALLOW_ALL = True
CSRF_COOKIE_HTTPONLY = False
SESSION_COOKIE_HTTPONLY = False
CORS_ALLOW_CREDENTIALS = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SAMESITE = 'None'
CSRF_TRUSTED_ORIGINS = ['http://localhost:3000', 'http://localhost:8000',
'https://hater.netlify.app', 'https://haterip.netlify.app']
CORS_EXPOSE_HEADERS = ["Set-Cookie"]
django_heroku.settings(locals())
and this is React.js code that handles login and the terinary that displays the login form when user isnt logged in user.tag is a global state that is null if no current user is logged in
let handleLogin = (e) => {
e.preventDefault();
let headerInfo = {
Accept: "application/json",
"Content-Type": "application/json",
};
let loginOptions = {
method: "POST",
headers: headerInfo,
credentials: "include",
body: JSON.stringify(form),
};
let options = {
method: "GET",
headers: headerInfo,
credentials: "include",
};
fetch(`https://haterbackend.herokuapp.com/user/login`, loginOptions)
.then((res) => res.json())
.then((data) => {
if (data["error"]) {
return alert(data["error"]);
} else {
fetch(`https://haterbackend.herokuapp.com/user/grabProfile`, options)
.then((res) => res.json())
.then((data) => {
store.dispatch({ type: "set", payload: data.profile });
})
.then(() => navigate("/home"))
.catch((err) => console.log(err));
}
})
.catch((err) => console.log(err));
};
{!user.tag ? (
<form onSubmit={handleLogin} className="landingForm">
<CSRFToken></CSRFToken>
<input
onChange={handleChange}
className="landingLoginInput"
placeholder="Username"
type="text"
name="username"
/>
<input
onChange={handleChange}
className="landingLoginInput"
placeholder="Password"
type="password"
name="password"
autoComplete="current-password"
/>
<Button id="login" type="submit">
Login
</Button>
</form>
) : (
<div className="landing-signout">
<Link className="landing-home-link" to="/home">
<Button>Home 🏡</Button>
</Link>
<Link className="landing-signout-link" onClick={signOut} to="/">
<Button>Sign out 🚪</Button>
</Link>
</div>
)}
The CSRFToken componet is just a hidden input field
import React, { useState, useEffect } from "react";
export default function CSRFToken() {
const [csrftoken, setcsrftoken] = useState("");
const getCookie = (name) => {
let cookieValue = null;
if (document.cookie && document.cookie !== "") {
let cookies = document.cookie.split(";");
for (let i = 0; i < cookies.length; i++) {
let cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === name + "=") {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
};
useEffect(() => {
fetch(`https://haterbackend.herokuapp.com/user/csrf_cookie`, {
credentials: "include",
})
.then((res) => {
setcsrftoken(getCookie("csrftoken"));
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<input type="hidden" name="csrfmiddlewaretoken" value={csrftoken || ""} />
);
}
I implemented session login with react front end as outlined here:
https://www.stackhawk.com/blog/django-csrf-protection-guide/
https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax official doc
the method above only worked locally on HTTP but wouldnt work over HTTPS because the deployed site wouldnt set the cookie because it wasnt secured
error screenshot
But client side scripts cant grab secure cookies and the getCookie function from the Django docs only parses thru a set cookie header so it wouldnt work if the cookie is undefined or empty ( client side script code attempts to read the cookie, the browser returns an empty string as the result source linked below)
https://owasp.org/www-community/HttpOnly
TLDR: Project works locally HTTP but when deployed cookies cant be set over HTTPS but client-side scripts cant read secure cookies so i cant register or log users in because that requires the csrftoken cookie
Super late reply but its not possible to securely use session based auth when my backend service was deployed on a different domain.
front end was on netifly
back end was on heroku
a better alternative that I used was django knox tokens it behaves the same way as JWT authentication but has more functionality and tokens can be invalidated.
TLDR; because the services were not under the same domain its not possible under a https connection
the newly registered user get email for activation . He clicks on the links and move to an activation page .then he clicks on verify button which take uid and token from the link and post it to auth/users/activation/ and then gets the response stale token for the given user no matter how fast he click on the link on verify link . result is same. I am using djoser for activation and all user related stuff.and redux in the frontend for api calls and also the React as frontend
here is my settings.py:
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=30),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'AUTH_HEADER_TYPES': ('JWT','Bearer'),
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule' ,
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
}
DJOSER = {
'LOGIN_FIELD': 'email',
'USERNAME_CHANGED_EMAIL_CONFIRMATION': True,
'PASSWORD_CHANGED_EMAIL_CONFIRMATION': True,
'SEND_CONFIRMATION_EMAIL': True,
'SET_USERNAME_RETYPE': True,
'SET_PASSWORD_RETYPE': True,
'PASSWORD_CHANGED_EMAIL_CONFIRMATION':True,
'PASSWORD_RESET_CONFIRM_URL': 'password/reset/confirm/{uid}/{token}',
'SEND_ACTIVATION_EMAIL': True,
'ACTIVATION_URL': 'activate/{uid}/{token}',
'SOCIAL_AUTH_TOKEN_STRATEGY': 'djoser.social.token.jwt.TokenStrategy',
'SOCIAL_AUTH_ALLOWED_REDIRECT_URIS': ['https://negoziohub.herokuapp.com/google', 'http://127.0.0.1:8000/facebook'],
'SERIALIZERS': {
'user_create': 'base.serializers.UserSerializer',
'user': 'base.serializers.UserSerializer',
'user_delete': 'djoser.serializers.UserDeleteSerializer',
}
}
here is userAction.js:
export const activate = (uid, token) => async (dispatch) => {
try {
dispatch({
type: USER_ACTIVATE_REQUEST
})
const config = {
headers: {
'Content-type': 'application/json',
}
}
const body = JSON.stringify({ uid, token });
const { data } = await axios.post(`/auth/users/activation/`, body,
config
)
dispatch({
type: USER_ACTIVATE_SUCCESS,
payload: data
})
// dispatch(login())
localStorage.setItem('userInfo', JSON.stringify(data))
} catch (error) {
dispatch({
type: USER_ACTIVATE_FAIL,
payload: error.response && error.response.data.detail
? error.response.data.detail
: error.message,
})
}
}
Any help would be appreciated. Thanks❤❤❤
I know it's too late to answer your question but I will do it just for other folks out there who might face the same issue in the future.
I had the same problem and I couldn't find anything on the internet, which drove me to dive deep in the source code of Djoser. The issue in my case was that I changed the default behavior to change the state of the user and set user.is_active = True. This was the issue as Djoser sends the stale token error if user.is_active != False.
Hope this helps.
I have my backend in Django and front in Vue.
A user performes login in Vue and via a POST request the creds are sent to a Django JWT login endpoint. This endpoint returns a token which is set in localStorage.
Then I want to check in Vue that the user is logged in. For that another endpoint in Django exists. However, it always returns "AnonymUser". I cannot get how to set this check.
Django:
My settings.py
JWT_AUTH = {
'JWT_ALLOW_REFRESH': True,
'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=1),
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
My urls.py
path('check-auth', views.check_if_logged_in, name="check-auth"), # check auth
path('auth/obtain_token', obtain_jwt_token), # obtain token
path('auth/refresh_token', refresh_jwt_token),
My views.py
# Login Check
#csrf_exempt
def check_if_logged_in(request):
authentication_class = (JSONWebTokenAuthentication,)
permission_classes = (IsAuthenticated,)
print(request.user) # returns AnonymUser
check = None
if request.user.is_authenticated:
check = True
else:
check = False
print(check) # returns False
return HttpResponse(f"<html><body>{check}</body></html>")
Vue
obtainToken function
obtainToken(){
var that = this;
fetch(this.endpoints.obtainJWT, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: that.django.username,
password: that.django.password
})
}).then(response => response.json()
).then(function(response) {
console.log('auth', response); # get token
that.updateToken(response.token); # update localStorage
that.checkAuthData(); #check auth
});
},
checkAuth function
checkAuthData: function() {
var that = this;
fetch('http://localhost:8000/check-auth', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
token: this.jwt # send token
})
}).then(response => response.json()
).then(function(response) {
console.log('check', response);
});
},
You should include token not in the body, but in the header instead:
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.jwt
},
Also, please make sure that in your Django settings in REST_FRAMEWORK DEFAULT_AUTHENTICATION_CLASSES contains JWT authentication:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
...
'rest_framework_simplejwt.authentication.JWTAuthentication',
]
}
I'm building a basic app with login, register, etc. now i try finish the login using Django like backend and fronted with Angular, but i can't end my login because this error, when i try login with the correct credentials and redirect to new page or url show this error.
TypeError at /doctor
'list' object is not callable"in network panel"
service .ts
constructor(private http: Http, private httpClient: HttpClient) { }
private headers = new Headers({ 'Content-Type': 'application/json' });
getDoctores(): Promise<Doctor[]> {
return this.http.get(this.baseurl + '/doctor?format=json', { headers: this.headers })
.toPromise()
.then(response => response.json() as Doctor[])
}
component .ts
constructor(private dataService: dataService, public dialog: MatDialog,
private router: Router) {
this.getDoctores();
this.selectedDoctor = {
id: -1, nombreDoc: '', apellidoDoc: '', rutDoc: '', direccionDoc: ''
, telefonoDoc: '', release_date: ''
}
}
getDoctores(): void {
this.dataService
.getDoctores()
.then(doctores => this.doctores = doctores);
}
url.py
path('auth/login/', obtain_jwt_token),
path('auth/refresh-token/', refresh_jwt_token),
url(r'^doctor$', views.DoctorList.as_view()),
url(r'^doctor/(?P<pk>[0-9]+)$', views.DoctorDetail.as_view()),
view.py
class DoctorList(generics.ListCreateAPIView):
queryset = Doctor.objects.all()
serializer_class = DoctorSerializer
class DoctorDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Doctor.objects.all()
serializer_class = DoctorSerializer
I've read the duplicates and nothing seems to be working. I can do the put request directly from the form in the url but I can't seem to get the axios request working.
I tried:
CSRF with Django, React+Redux using Axios
https://gist.github.com/paltman/490049a64fa4115a2cea
my view.py:
class FrequencyList(generics.ListCreateAPIView):
queryset = Frequency.objects.all()
serializer_class = FrequencySerializer
class FrequencyDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Frequency.objects.all()
serializer_class = FrequencySerializer
My axios request:
axios({
method: 'put',
url: '/f/'+id,
data: {
item: item,
},
}).then(function (response) {
this.setState({needReceipt: true});
})
.catch(function (error) {
console.log(error);
});
In my settings.py:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
}
in my webpack.config.dev.js:
const axios = require('axios');
axios.defaults.xsrfHeaderName = "X-CSRFToken";
axios.defaults.xsrfCookieName = "csrftoken";
try this
axios.put('/f/'+id, { item: item })
.then(function(response){
this.setState({needReceipt: true});
});
Reference