feat: inelegant and buggy version of auth

This commit is contained in:
julius 2025-02-16 17:22:36 +01:00
parent fbe17479f7
commit 15c9a64de2
Signed by: julius
GPG Key ID: C80A63E6A5FD7092
5 changed files with 121 additions and 19 deletions

View File

@ -1,6 +1,6 @@
from datetime import timedelta, timezone, datetime from datetime import timedelta, timezone, datetime
from typing import Annotated from typing import Annotated
from fastapi import Depends, HTTPException, status from fastapi import Depends, HTTPException, Response, status
from pydantic import BaseModel from pydantic import BaseModel
import jwt import jwt
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
@ -102,7 +102,7 @@ async def get_current_active_user(
async def login_for_access_token( async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()], form_data: Annotated[OAuth2PasswordRequestForm, Depends()], response: Response
) -> Token: ) -> Token:
user = authenticate_user(form_data.username, form_data.password) user = authenticate_user(form_data.username, form_data.password)
if not user: if not user:
@ -115,6 +115,9 @@ async def login_for_access_token(
access_token = create_access_token( access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires data={"sub": user.username}, expires_delta=access_token_expires
) )
response.set_cookie(
"Authorization", value=f"Bearer {access_token}", httponly=True, samesite="none"
)
return Token(access_token=access_token, token_type="bearer") return Token(access_token=access_token, token_type="bearer")

View File

@ -1,5 +1,6 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { baseUrl } from "./api"; import { apiAuth, baseUrl, token } from "./api";
import useAuthContext from "./AuthContext";
//const debounce = <T extends (...args: any[]) => void>( //const debounce = <T extends (...args: any[]) => void>(
// func: T, // func: T,
@ -61,18 +62,14 @@ export default function Analysis() {
// Function to generate and fetch the graph image // Function to generate and fetch the graph image
async function loadImage() { async function loadImage() {
setLoading(true); setLoading(true);
await fetch(`${baseUrl}api/analysis/image`, { await apiAuth("analysis/image", params, "POST")
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(params)
})
.then((resp) => resp.json())
.then((data) => { .then((data) => {
setImage(data.image); setImage(data.image);
setLoading(false); setLoading(false);
}); }).catch((e) => {
const { checkAuth } = useAuthContext();
checkAuth();
})
} }
useEffect(() => { useEffect(() => {
@ -92,6 +89,9 @@ export default function Analysis() {
} }
} }
const { user } = useAuthContext()!
console.log(`logged in as ${user.username}`);
return ( return (
<div className="stack column dropdown"> <div className="stack column dropdown">
<button onClick={() => setShowControlPanel(!showControlPanel)}> <button onClick={() => setShowControlPanel(!showControlPanel)}>

View File

@ -12,16 +12,17 @@ body {
height: 100%; height: 100%;
} }
footer {
font-size: x-small;
}
#root { #root {
max-width: 1280px; max-width: 1280px;
margin: 0 auto; margin: 0 auto;
padding: 8px; padding: 8px;
} }
footer {
font-size: x-small;
}
.grey { .grey {
color: #444; color: #444;
} }
@ -37,6 +38,11 @@ footer {
z-index: -1; z-index: -1;
} }
input {
padding: 0.2em 16px;
margin-bottom: 0.5em;
}
h1, h1,
h2, h2,
h3 { h3 {
@ -120,7 +126,8 @@ h3 {
margin: auto; margin: auto;
} }
button { button,
.button {
font-weight: bold; font-weight: bold;
font-size: large; font-size: large;
color: aliceblue; color: aliceblue;

View File

@ -1,9 +1,10 @@
import Analysis from "./Analysis"; import Analysis from "./Analysis";
import "./App.css"; import "./App.css";
import { AuthProvider } from "./AuthContext";
import Footer from "./Footer"; import Footer from "./Footer";
import Header from "./Header"; import Header from "./Header";
import Rankings from "./Rankings"; import Rankings from "./Rankings";
import { BrowserRouter, Routes, Route } from "react-router-dom"; import { BrowserRouter, Routes, Route } from "react-router";
function App() { function App() {
//const [data, setData] = useState({ nodes: [], links: [] } as SociogramData); //const [data, setData] = useState({ nodes: [], links: [] } as SociogramData);
@ -17,7 +18,11 @@ function App() {
<Header /> <Header />
<Routes> <Routes>
<Route index element={<Rankings />} /> <Route index element={<Rankings />} />
<Route path="/analysis" element={<Analysis />} /> <Route path="/analysis" element={
<AuthProvider>
<Analysis />
</AuthProvider>
} />
</Routes> </Routes>
<Footer /> <Footer />
</BrowserRouter> </BrowserRouter>

View File

@ -1,4 +1,10 @@
import { useContext } from "react";
import useAuthContext from "./AuthContext";
import { createCookie } from "react-router";
export const baseUrl = import.meta.env.VITE_BASE_URL as string; export const baseUrl = import.meta.env.VITE_BASE_URL as string;
export const token = () => localStorage.getItem("access_token") as string;
export default async function api(path: string, data: any): Promise<any> { export default async function api(path: string, data: any): Promise<any> {
const request = new Request(`${baseUrl}${path}/`, { const request = new Request(`${baseUrl}${path}/`, {
method: "POST", method: "POST",
@ -15,3 +21,84 @@ export default async function api(path: string, data: any): Promise<any> {
} }
return response; return response;
} }
export async function apiAuth(path: string, data: any, method: string = "GET"): Promise<any> {
const req = new Request(`${baseUrl}api/${path}`, {
method: method, headers: {
"Authorization": `Bearer ${token()} `,
'Content-Type': 'application/json'
},
body: JSON.stringify(data),
});
let resp: Response;
try {
resp = await fetch(req);
} catch (e) {
throw new Error(`request failed: ${e}`);
}
if (!resp.ok) {
if (resp.status === 401) {
logout()
throw new Error('Unauthorized');
}
}
return resp.json()
}
export type User = {
username: string;
fullName: string;
}
export async function currentUser(): Promise<User> {
if (!token()) throw new Error("you have no access token")
const req = new Request(`${baseUrl}api/users/me/`, {
method: "GET", headers: {
"Authorization": `Bearer ${token()} `,
'Content-Type': 'application/json'
}
});
let resp: Response;
try {
resp = await fetch(req);
} catch (e) {
throw new Error(`request failed: ${e}`);
}
if (!resp.ok) {
if (resp.status === 401) {
logout()
throw new Error('Unauthorized');
}
}
return resp.json() as Promise<User>;
}
export type LoginRequest = {
username: string;
password: string;
};
export type Token = {
access_token: string;
token_type: string;
};
export const login = (req: LoginRequest) => {
fetch(`${baseUrl}api/token`, {
method: "POST", headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}, body: new URLSearchParams(req).toString()
}).then(resp => resp.json() as unknown as Token).then(token => token ? localStorage.setItem("access_token", token.access_token) : console.log("token not acquired")).catch((e) => console.log("catch error " + e + " in login"));
}
export const cookielogin = (req: LoginRequest) => {
fetch(`${baseUrl}api/token`, {
method: "POST", headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}, body: new URLSearchParams(req).toString()
}).then(resp => { createCookie(resp.headers.getSetCookie()) }).catch((e) => console.log("catch error " + e + " in login"));
}
export const logout = () => localStorage.removeItem("access_token");