Created
January 18, 2024 08:36
-
-
Save tabdon/d568ae78f4751c082deca08e9af5862a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Django-ninja APIs (Python) | |
from ninja import Router, Schema | |
from django.http import JsonResponse | |
from django.contrib.auth import authenticate, login | |
from django.views.decorators.csrf import ensure_csrf_cookie | |
from django.contrib.auth.models import User | |
from ninja.errors import HttpError | |
router = Router() | |
_TGS = ['User API'] | |
class UserIn(Schema): | |
email: str | |
password: str | |
class UserOut(Schema): | |
id: int | |
email: str | |
class LoginIn(Schema): | |
email: str | |
password: str | |
@router.get("/me", tags=_TGS, response=UserOut) | |
def get_me(request): | |
if request.user: | |
return request.user | |
else: | |
raise HttpError(401, "Need to login.") | |
@router.post("/login", tags=_TGS, response=UserOut, auth=None) | |
def login_user(request, payload: LoginIn): | |
user = User(**payload.dict()) | |
user = authenticate(request, email=user.email, password=user.password) | |
if user is not None: | |
login(request, user) | |
print("login good") | |
return user | |
else: | |
print("login bad") | |
return JsonResponse({"error": "email or password is incorrect"}) | |
@router.get('/set-cookie', tags=_TGS, auth=None) | |
@ensure_csrf_cookie | |
def login_set_cookie(request): | |
""" | |
`login_view` requires that a csrf cookie be set. | |
`getCsrfToken` in `auth.js` uses this cookie to | |
make a request to `login_view` | |
""" | |
return JsonResponse({"details": "CSRF cookie set"}) | |
# React Component for Login | |
import { useState } from "react"; | |
import { useNavigate } from "react-router-dom"; | |
import { getCSRFToken } from "../../utils/csrf"; | |
const Login = () => { | |
const navigate = useNavigate(); | |
const [email, setEmail] = useState(""); | |
const [password, setPassword] = useState(""); | |
const [error, setError] = useState(""); | |
const handleLogin = async () => { | |
try { | |
const response = await fetch("/api/user/login", { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json", | |
'X-CSRFToken': getCSRFToken(), | |
}, | |
body: JSON.stringify({ email, password }), | |
}); | |
if (response.ok) { | |
const data = await response.json(); | |
setError(''); | |
navigate('/app/dash/'); | |
console.log(data); | |
} else { | |
setError("Failed to login. Please try again."); | |
} | |
} catch (err) { | |
setError("Failed to fetch. Please try again."); | |
console.error(err); | |
} | |
}; | |
return ( | |
<div className="flex justify-center items-center h-screen bg-gray-100"> | |
<div className="bg-white p-8 rounded-lg shadow-md w-96"> | |
{error && <div className="text-red-500 mb-4">{error}</div>} | |
<div className="mb-4"> | |
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="username"> | |
Username | |
</label> | |
<input | |
id="email" | |
type="text" | |
placeholder="Username" | |
value={email} | |
onChange={(e) => setEmail(e.target.value)} | |
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" | |
/> | |
</div> | |
<div className="mb-4"> | |
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="password"> | |
Password | |
</label> | |
<input | |
id="password" | |
type="password" | |
placeholder="Password" | |
value={password} | |
onChange={(e) => setPassword(e.target.value)} | |
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" | |
/> | |
</div> | |
<div className="flex items-center justify-between"> | |
<button | |
onClick={handleLogin} | |
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" | |
> | |
Login | |
</button> | |
</div> | |
</div> | |
</div> | |
); | |
}; | |
export default Login; | |
# CSRF.js utility | |
export const getCSRFToken = () => { | |
const cookies = document.cookie.split("; "); | |
for (let i = 0; i < cookies.length; i++) { | |
const cookie = cookies[i].split("="); | |
if (cookie[0] === "csrftoken") { | |
return cookie[1]; | |
} | |
} | |
return ""; | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment