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