feat(frontend/auth): fix cookie not working due to path being /app

Cookies being set to path=/app meant that the API didn't have those
cookies.
Also fixed the Schema injection for auth'ed routes
This commit is contained in:
Maieul BOYER 2025-11-10 18:43:34 +01:00 committed by Maix0
parent e8b0b7e310
commit aba4c4498c
5 changed files with 151 additions and 131 deletions

View file

@ -9,12 +9,14 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"devDependencies": { "devDependencies": {
"@types/js-cookie": "^3.0.6",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"vite": "^7.1.10", "vite": "^7.1.10",
"vite-tsconfig-paths": "^5.1.4" "vite-tsconfig-paths": "^5.1.4"
}, },
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.1.16", "@tailwindcss/vite": "^4.1.16",
"js-cookie": "^3.0.5",
"openapi-fetch": "^0.15.0", "openapi-fetch": "^0.15.0",
"tailwindcss": "^4.1.16" "tailwindcss": "^4.1.16"
} }

View file

@ -11,6 +11,9 @@ importers:
'@tailwindcss/vite': '@tailwindcss/vite':
specifier: ^4.1.16 specifier: ^4.1.16
version: 4.1.16(vite@7.1.12(jiti@2.6.1)(lightningcss@1.30.2)) version: 4.1.16(vite@7.1.12(jiti@2.6.1)(lightningcss@1.30.2))
js-cookie:
specifier: ^3.0.5
version: 3.0.5
openapi-fetch: openapi-fetch:
specifier: ^0.15.0 specifier: ^0.15.0
version: 0.15.0 version: 0.15.0
@ -18,6 +21,9 @@ importers:
specifier: ^4.1.16 specifier: ^4.1.16
version: 4.1.16 version: 4.1.16
devDependencies: devDependencies:
'@types/js-cookie':
specifier: ^3.0.6
version: 3.0.6
typescript: typescript:
specifier: ~5.9.3 specifier: ~5.9.3
version: 5.9.3 version: 5.9.3
@ -405,6 +411,9 @@ packages:
'@types/estree@1.0.8': '@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/js-cookie@3.0.6':
resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==}
debug@4.4.3: debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
@ -451,6 +460,10 @@ packages:
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
hasBin: true hasBin: true
js-cookie@3.0.5:
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
engines: {node: '>=14'}
lightningcss-android-arm64@1.30.2: lightningcss-android-arm64@1.30.2:
resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
@ -867,6 +880,8 @@ snapshots:
'@types/estree@1.0.8': {} '@types/estree@1.0.8': {}
'@types/js-cookie@3.0.6': {}
debug@4.4.3: debug@4.4.3:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
@ -920,6 +935,8 @@ snapshots:
jiti@2.6.1: {} jiti@2.6.1: {}
js-cookie@3.0.5: {}
lightningcss-android-arm64@1.30.2: lightningcss-android-arm64@1.30.2:
optional: true optional: true

View file

@ -3,6 +3,7 @@ import { showError, showInfo, showSuccess } from "@app/toast";
import authHtml from './login.html?raw'; import authHtml from './login.html?raw';
import client from '@app/api' import client from '@app/api'
import { updateUser } from "@app/auth"; import { updateUser } from "@app/auth";
import Cookie from 'js-cookie';
type Providers = { type Providers = {
@ -34,7 +35,7 @@ function handleLogin(_url: string, _args: RouteHandlerParams): RouteHandlerRetur
const res = await client.login({ loginRequest: { name: formData.login, password: formData.password } }); const res = await client.login({ loginRequest: { name: formData.login, password: formData.password } });
switch (res.kind) { switch (res.kind) {
case 'success': { case 'success': {
document.cookie = `token=${res.payload.token}`; Cookie.set('token', res.payload.token, { path: '/', sameSite: 'lax' });
let user = await updateUser(); let user = await updateUser();
if (user === null) if (user === null)
return showError('Failed to get user: no user ?'); return showError('Failed to get user: no user ?');
@ -61,7 +62,7 @@ function handleLogin(_url: string, _args: RouteHandlerParams): RouteHandlerRetur
const res = await client.guestLogin(); const res = await client.guestLogin();
switch (res.kind) { switch (res.kind) {
case 'success': { case 'success': {
document.cookie = `token=${res.payload.token}`; Cookie.set('token', res.payload.token, { path: '/', sameSite: 'lax' });
let user = await updateUser(); let user = await updateUser();
if (user === null) if (user === null)
return showError('Failed to get user: no user ?'); return showError('Failed to get user: no user ?');

View file

@ -41,7 +41,7 @@ http {
} }
location / { location / {
proxy_ssl_verify off; proxy_ssl_verify off;
return 301 'https://$http_host/app/$request_uri'; proxy_pass http://localhost:5173/;
} }
} }
} }

View file

@ -90,10 +90,10 @@ export const authPlugin = fp<{ onlySchema?: boolean }>(async (fastify, { onlySch
let schema: TSchema = authSchema; let schema: TSchema = authSchema;
if ('401' in (routeOpts.schema.response as { [k: string]: TSchema })) { if ('401' in (routeOpts.schema.response as { [k: string]: TSchema })) {
const schema_orig = (routeOpts.schema.response as { [k: string]: TSchema })['401']; const schema_orig = (routeOpts.schema.response as { [k: string]: TSchema })['401'];
if (schema_orig[Typebox.Kind] === 'Union') { if (Type.IsUnion(schema_orig)) {
schema = Typebox.Union([...((schema_orig as Typebox.TUnion).anyOf), authSchema]); schema = Typebox.Union([...((schema_orig as Typebox.TUnion).anyOf), authSchema]);
} }
else if (schema_orig[Typebox.Kind] === 'Object') { else if (Type.IsObject(schema_orig)) {
schema = Typebox.Union([schema_orig, authSchema]); schema = Typebox.Union([schema_orig, authSchema]);
} }
} }
@ -103,26 +103,26 @@ export const authPlugin = fp<{ onlySchema?: boolean }>(async (fastify, { onlySch
try { try {
if (isNullish(req.cookies.token)) { if (isNullish(req.cookies.token)) {
return res return res
.clearCookie('token') .clearCookie('token', { path: '/' })
.makeResponse(401, 'notLoggedIn', 'auth.noCookie'); .makeResponse(401, 'notLoggedIn', 'auth.noCookie');
} }
const tok = this.jwt.verify<JwtType>(req.cookies.token); const tok = this.jwt.verify<JwtType>(req.cookies.token);
if (tok.kind != 'auth') { if (tok.kind != 'auth') {
return res return res
.clearCookie('token') .clearCookie('token', { path: '/' })
.makeResponse(401, 'notLoggedIn', 'auth.invalidKind'); .makeResponse(401, 'notLoggedIn', 'auth.invalidKind');
} }
const user = this.db.getUser(tok.who); const user = this.db.getUser(tok.who);
if (isNullish(user)) { if (isNullish(user)) {
return res return res
.clearCookie('token') .clearCookie('token', { path: '/' })
.makeResponse(401, 'notLoggedIn', 'auth.noUser'); .makeResponse(401, 'notLoggedIn', 'auth.noUser');
} }
req.authUser = { id: user.id, name: tok.who }; req.authUser = { id: user.id, name: tok.who };
} }
catch { catch {
return res return res
.clearCookie('token') .clearCookie('token', { path: '/' })
.makeResponse(401, 'notLoggedIn', 'auth.invalid'); .makeResponse(401, 'notLoggedIn', 'auth.invalid');
} }
}; };