Sarting auth implementation in front

This commit is contained in:
2025-04-04 15:40:25 +02:00
parent b89bb484b7
commit d5e443a7c4
10 changed files with 102 additions and 40 deletions

View File

@@ -4,7 +4,7 @@ import uuid
from beanie import PydanticObjectId from beanie import PydanticObjectId
from fastapi import Depends from fastapi import Depends
from fastapi_users import UUIDIDMixin, BaseUserManager, FastAPIUsers, schemas from fastapi_users import UUIDIDMixin, BaseUserManager, FastAPIUsers, schemas
from fastapi_users.authentication import AuthenticationBackend, BearerTransport from fastapi_users.authentication import AuthenticationBackend, BearerTransport, CookieTransport
from fastapi_users.authentication.strategy import AccessTokenDatabase, DatabaseStrategy from fastapi_users.authentication.strategy import AccessTokenDatabase, DatabaseStrategy
from fastapi_users_db_beanie.access_token import BeanieBaseAccessTokenDocument, BeanieAccessTokenDatabase from fastapi_users_db_beanie.access_token import BeanieBaseAccessTokenDocument, BeanieAccessTokenDatabase
from httpx_oauth.clients.google import GoogleOAuth2 from httpx_oauth.clients.google import GoogleOAuth2
@@ -19,7 +19,8 @@ discord_oauth_client = DiscordOAuth2(os.getenv("DISCORD_CLIENT_ID"), os.getenv("
TOKEN_LIFETIME = 3600 TOKEN_LIFETIME = 3600
bearer_transport = BearerTransport(tokenUrl="auth/login") # bearer_transport = BearerTransport(tokenUrl="auth/login")
cookie_transport = CookieTransport(cookie_name="rpkapiusersauth")
class AccessToken(BeanieBaseAccessTokenDocument): class AccessToken(BeanieBaseAccessTokenDocument):
@@ -44,10 +45,7 @@ async def get_user_manager(user_db=Depends(get_user_db)):
yield UserManager(user_db) yield UserManager(user_db)
auth_backend = AuthenticationBackend( auth_backend = AuthenticationBackend(name="db", transport=cookie_transport, get_strategy=get_database_strategy,
name="db",
transport=bearer_transport,
get_strategy=get_database_strategy,
) )
fastapi_users = FastAPIUsers[User, PydanticObjectId](get_user_manager, [auth_backend]) fastapi_users = FastAPIUsers[User, PydanticObjectId](get_user_manager, [auth_backend])

View File

@@ -2,7 +2,7 @@ services:
api: api:
build: build:
context: ./api context: ./api
#image: roleplay-contracts-api-dev image: roleplay-contracts-api-dev
env_file: "./.env" env_file: "./.env"
restart: always restart: always
ports: ports:
@@ -16,21 +16,21 @@ services:
- "traefik.http.routers.back.rule=PathPrefix(`/api/v1/`)" - "traefik.http.routers.back.rule=PathPrefix(`/api/v1/`)"
- "traefik.http.services.back.loadbalancer.server.port=8000" - "traefik.http.services.back.loadbalancer.server.port=8000"
# gui: gui:
# build: build:
# context: ./gui context: ./gui
# image: roleplay-contracts-gui-dev image: roleplay-contracts-gui-dev
# restart: always restart: always
# ports: ports:
# - "4200:4200" - "4200:4200"
# volumes: volumes:
# - ./gui/rpk-gui/src:/app/src - ./gui/rpk-gui/src:/app/src
# - ./gui/rpk-gui/public:/app/public - ./gui/rpk-gui/public:/app/public
# labels: labels:
# - "traefik.enable=true" - "traefik.enable=true"
# - "traefik.http.routers.front.entrypoints=web" - "traefik.http.routers.front.entrypoints=web"
# - "traefik.http.routers.front.rule=PathPrefix(`/`)" - "traefik.http.routers.front.rule=PathPrefix(`/`)"
# - "traefik.http.services.front.loadbalancer.server.port=4200" - "traefik.http.services.front.loadbalancer.server.port=4200"
proxy: proxy:
image: traefik:latest image: traefik:latest

13
gui/Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM node:lts-alpine
WORKDIR /app
RUN npm install -g @angular/cli http-server
COPY rpk-gui/package*.json ./
RUN npm install
COPY rpk-gui/ .
RUN npm run build
EXPOSE 4200
CMD [ "npm", "run", "ng", "serve", "--", "--host", "0.0.0.0", "--disable-host-check" ]

View File

@@ -17,8 +17,8 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.21.0", "@eslint/js": "^9.21.0",
"@types/react": "^19.0.10", "@types/react": "^19.1.0",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.1.1",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.21.0", "eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-hooks": "^5.1.0",
@@ -2066,20 +2066,18 @@
"peer": true "peer": true
}, },
"node_modules/@types/react": { "node_modules/@types/react": {
"version": "19.0.12", "version": "19.1.0",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.12.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz",
"integrity": "sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==", "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==",
"license": "MIT",
"dependencies": { "dependencies": {
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/@types/react-dom": { "node_modules/@types/react-dom": {
"version": "19.0.4", "version": "19.1.1",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.1.tgz",
"integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==", "integrity": "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==",
"dev": true, "dev": true,
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@types/react": "^19.0.0" "@types/react": "^19.0.0"
} }

View File

@@ -15,13 +15,12 @@
"better-auth": "^1.2.5", "better-auth": "^1.2.5",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-router": "^7.4.1", "react-router": "^7.4.1"
"react-router-auth"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.21.0", "@eslint/js": "^9.21.0",
"@types/react": "^19.0.10", "@types/react": "^19.1.0",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.1.1",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.21.0", "eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-hooks": "^5.1.0",

View File

@@ -1,12 +1,16 @@
//https://www.robinwieruch.de/react-router-private-routes/
import { useState } from 'react' import { useState } from 'react'
import reactLogo from './assets/react.svg' import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg' import viteLogo from '/vite.svg'
import './App.css' import './App.css'
import {createBrowserRouter, Link, RouterProvider} from "react-router"; import {createBrowserRouter, Link, Route, RouterProvider, Routes} from "react-router";
import { EntityList } from "./page/entities/list.tsx"; import { EntityList } from "./pages/entities/List.tsx";
import {ProtectedRoute} from "./pages/auth/ProtectedRoute.tsx";
function App() { function App() {
const [user, setUser] = useState<AuthUser | null>(null)
const [count, setCount] = useState(0) const [count, setCount] = useState(0)
const router = createBrowserRouter([ const router = createBrowserRouter([
@@ -23,8 +27,13 @@ function App() {
return ( return (
<> <>
<RouterProvider router={router}> <RouterProvider router={router}>
</RouterProvider> </RouterProvider>
<Routes>
<Route index element={ <h1>INDEX</h1> } />
<Route element={ <ProtectedRoute user={user} /> }>
<Route path="toto" element={ <h1>PROTECTED ROUTE</h1>} />
</Route>
</Routes>
<div> <div>
<a href="https://vite.dev" target="_blank"> <a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" /> <img src={viteLogo} className="logo" alt="Vite logo" />

View File

@@ -0,0 +1,14 @@
export const Login = () => {
async function handleLogin(e: any) {
e.preventDefault()
}
return (
<form onSubmit={ handleLogin }>
<input type={ "text" } name={ "email" } />
<input type={ "password" } name={ "password" } />
<input type={ "submit" } />
</form>
)
}

View File

@@ -0,0 +1,16 @@
import {Navigate, Outlet} from "react-router";
type AuthUser = { id: string; name: string };
type ProtectedRouteProps = {
user: AuthUser | null;
redirectPath?: string;
};
export const ProtectedRoute = ({ user, redirectPath = "/" }: ProtectedRouteProps) => {
if (!user) {
return <Navigate to={redirectPath} replace />;
}
return <Outlet />;
};

View File

@@ -0,0 +1,15 @@
export const Register = () => {
async function handleRegister(e: any) {
e.preventDefault()
}
return (
<form onSubmit={ handleRegister }>
<input type={ "text" } name={ "email" } />
<input type={ "password" } name={ "password" }/>
<input type={ "password" } name={ "confirm_password" }/>
<input type={ "submit" } />
</form>
)
}

View File

@@ -1,6 +1,6 @@
export const EntityList = (props: any) => { export const EntityList = () => {
return ( return (
<h1>List des entity: Yoyoyo</h1> <h1>List des entity: Yoyoyo</h1>
) )