Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions apps/app/middleware/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@
* Middleware de navigation — protège les pages authentifiées.
* Usage : definePageMeta({ middleware: 'auth' }) dans la page.
* Redirige vers /connexion si la session est absente.
*
* La session est vérifiée auprès du serveur Better Auth (GET /api/auth/get-session),
* fiable en SSR (cookies transférés) comme en navigation client. L'atom nanostores
* de useAuth() n'est PAS utilisable ici : l'objet est toujours truthy et la session
* n'est hydratée qu'après le premier fetch côté client — l'ancien check laissait
* passer les visiteurs non connectés (écran d'erreur 401 sur /profil au lieu d'une
* redirection).
*/
export default defineNuxtRouteMiddleware(async () => {
const { session } = useAuth()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const s = (session as any)?.value ?? session
if (!s) return navigateTo('/connexion')
// En SSR, $fetch interne ne transmet pas les cookies du navigateur tout seul.
const headers = import.meta.server ? useRequestHeaders(['cookie']) : undefined
try {
const session = await $fetch('/api/auth/get-session', { headers })
if (!session) return navigateTo('/connexion')
} catch {
return navigateTo('/connexion')
}
})
15 changes: 11 additions & 4 deletions apps/app/server/utils/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* - `requireUserId` (THI-126) : lit `event.context.userId` posé par le middleware
* d'auth (THI-134) et lève une 401 si absent. Découplé de l'instance Better Auth.
* - `getAuthSession` (THI-132) : récupère la session Better Auth complète depuis
* l'événement (headers Web API via `toWebRequest`). Retourne null si non authentifié.
* les headers de l'événement. Retourne null si non authentifié.
*/
import { createError, type H3Event } from 'h3'
import { auth } from './auth'
Expand All @@ -26,8 +26,15 @@ export function requireUserId(event: H3Event): string {
return userId
}

/** Récupère la session Better Auth complète (ou null si non authentifié). */
/**
* Récupère la session Better Auth complète (ou null si non authentifié).
*
* ⚠️ On passe `event.headers` directement — surtout pas `toWebRequest(event)` :
* convertir l'événement en Request touche au flux du corps, et le `readBody(event)`
* qui suit dans les handlers PUT/POST attend alors un corps déjà verrouillé →
* requête qui pend indéfiniment (toutes les écritures /api/profile* étaient HS
* en build de prod ; les GET, sans corps, passaient).
*/
export async function getAuthSession(event: H3Event) {
const req = toWebRequest(event)
return auth.api.getSession({ headers: req.headers })
return auth.api.getSession({ headers: event.headers })
}
Loading