{"_id":"@btcv/auth-provider","name":"@btcv/auth-provider","dist-tags":{"latest":"0.2.0"},"versions":{"0.2.0":{"name":"@btcv/auth-provider","version":"0.2.0","description":"Plug-and-play OIDC/WebAuthn auth module for Next.js apps backed by me.btcv.fr","license":"MIT","author":{"name":"Maxime Roque","email":"mroqueda@gmail.com"},"homepage":"https://github.com/La-Batcave/auth-provider#readme","repository":{"type":"git","url":"git+https://github.com/La-Batcave/auth-provider.git"},"bugs":{"url":"https://github.com/La-Batcave/auth-provider/issues"},"keywords":["auth","authentication","oidc","oauth2","webauthn","passkey","next","nextjs","react","btcv"],"type":"module","sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./next":{"types":"./dist/next/index.d.ts","import":"./dist/next/index.js","require":"./dist/next/index.cjs"},"./server":{"types":"./dist/server/index.d.ts","import":"./dist/server/index.js","require":"./dist/server/index.cjs"},"./styles.css":{"style":"./dist/styles.css","default":"./dist/styles.css"}},"publishConfig":{"access":"public"},"scripts":{"prebuild":"node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"","build":"tsup && npx @tailwindcss/cli -i src/styles.css -o dist/styles.css --minify","dev":"tsup --watch","test":"vitest","test:run":"vitest run","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run build"},"peerDependencies":{"next":">=14.0.0","react":">=18.0.0","react-dom":">=18.0.0"},"peerDependenciesMeta":{"next":{"optional":true}},"devDependencies":{"@types/node":"^25.5.0","@types/react":"^18.0.0","@types/react-dom":"^18.0.0","next":"^15.0.0","react":"^19.0.0","react-dom":"^19.0.0","@tailwindcss/cli":"^4.0.0","tailwindcss":"^4.0.0","tsup":"^8.0.0","typescript":"^5.5.0","vitest":"^2.0.0"},"dependencies":{"@btcv/ui":"^4.1.2","@simplewebauthn/browser":"^13.0.0","lucide-react":"^1.7.0"},"_id":"@btcv/auth-provider@0.2.0","gitHead":"303d5ef1dfff3589fda9154771f69d81cc97b084","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-Sd77w8hKdFJnT9a/qJ5qXblfKcwouq24MWrHZj51lqhCJpAbFm0qn/NV2uC2elTX+2yEe49ltDRyIHtdT6KFGQ==","shasum":"b4a18db18df8768d224c580a79cb90ae1da88c72","tarball":"https://registry.npmjs.org/@btcv/auth-provider/-/auth-provider-0.2.0.tgz","fileCount":17,"unpackedSize":790269,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDwxppa5D83Tvnu5Sqh7MDoI3wN53yIsRYb9jZQTcRKpAIgVqTenD4Kfjk7eIzhmlYl5CNwDhcQQaas8h1KKwFMrX0="}]},"_npmUser":{"name":"mroqueda","email":"mroqueda@gmail.com"},"directories":{},"maintainers":[{"name":"mroqueda","email":"mroqueda@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/auth-provider_0.2.0_1779448493309_0.9958840603209944"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-22T11:14:53.201Z","0.2.0":"2026-05-22T11:14:53.469Z","modified":"2026-05-22T11:14:53.661Z"},"maintainers":[{"name":"mroqueda","email":"mroqueda@gmail.com"}],"description":"Plug-and-play OIDC/WebAuthn auth module for Next.js apps backed by me.btcv.fr","homepage":"https://github.com/La-Batcave/auth-provider#readme","keywords":["auth","authentication","oidc","oauth2","webauthn","passkey","next","nextjs","react","btcv"],"repository":{"type":"git","url":"git+https://github.com/La-Batcave/auth-provider.git"},"author":{"name":"Maxime Roque","email":"mroqueda@gmail.com"},"bugs":{"url":"https://github.com/La-Batcave/auth-provider/issues"},"license":"MIT","readme":"# @btcv/auth-provider\n\nModule d'authentification plug-and-play pour applications **Next.js (App Router)**, façade au-dessus du serveur OIDC `me.btcv.fr`. Le module ne gère **pas** les flows de login/register : ils ont lieu sur le site d'auth externe. Il fournit :\n\n- un **middleware** Next.js qui protège les routes et injecte les headers user\n- un **catch-all route handler** (`/api/auth/[...auth]`) qui gère login → callback PKCE → session → refresh → logout → proxy authentifié vers `${authUrl}/api/*`\n- un **`<AuthProvider>`** + hooks (`useSession`, `useAccount`, `usePasskeys`, `useAuthGuard`)\n- un **`<AccountDialog>`** prêt à l'emploi (profil, sécurité/passkeys, sessions, providers)\n- des helpers serveur (`getSession`, `withAuth`) pour RSC et route handlers\n\n---\n\n## Installation\n\n```bash\nnpm install @btcv/auth-provider\n```\n\nPeers requis dans l'app consommatrice :\n\n- `next >= 14`\n- `react >= 18` / `react-dom >= 18`\n- `@simplewebauthn/browser` (uniquement si tu utilises les passkeys côté client)\n\n### Styles (Tailwind v4)\n\nLe `<AccountDialog>` utilise des classes Tailwind utilitaires (`md:h-140`, `w-[225px]`, etc.).\nTailwind v4 ne scanne pas `node_modules` par défaut — sans import explicite, le dialog\nrend comme une mini-boîte sans dimensions.\n\nDans le CSS global de ton app :\n\n```css\n@import \"tailwindcss\";\n@import \"@btcv/ui/styles.css\";\n@import \"@btcv/auth-provider/styles.css\";  /* classes du AccountDialog */\n```\n\n`styles.css` est un fichier pré-compilé qui contient uniquement les classes utilitaires\nutilisées par les composants du package. Les tokens (couleurs, radius, fonts) viennent\nde `@btcv/ui/styles.css` que tu importes déjà.\n\n### AccountDialog depuis un DropdownMenuItem (Radix)\n\nSi tu ouvres le dialog depuis un `<DropdownMenuItem>`, un `setTimeout(0)` est\nnécessaire pour laisser le dropdown restaurer `pointer-events` sur `<body>` avant\nque le dialog pose son propre lock — sinon la page reste bloquée après fermeture :\n\n```tsx\nconst [accountOpen, setAccountOpen] = useState(false);\n\n<DropdownMenuItem\n  onSelect={() => setTimeout(() => setAccountOpen(true), 0)}\n>\n  Mon compte\n</DropdownMenuItem>\n\n<AccountDialog open={accountOpen} onClose={() => setAccountOpen(false)} />\n```\n\nSans le `setTimeout`, le dropdown et le dialog se disputent la gestion du\n`pointer-events: none` sur le body (conflit Radix Dropdown ↔ Dialog).\n\n---\n\n## Configuration minimale\n\n### 1. Variables d'environnement\n\n```bash\nAUTH_URL=https://me.btcv.fr\nAUTH_CLIENT_ID=your-client-id\nAUTH_CLIENT_SECRET=your-client-secret   # optionnel (clients confidentiels)\n```\n\n### 2. Route handler catch-all\n\n`app/api/auth/[...auth]/route.ts` :\n\n```ts\nimport { createAuthHandlers } from \"@btcv/auth-provider/next\";\n\nexport const { GET, POST, PATCH, DELETE } = createAuthHandlers({\n  authUrl: process.env.AUTH_URL!,\n  clientId: process.env.AUTH_CLIENT_ID!,\n  clientSecret: process.env.AUTH_CLIENT_SECRET,\n  afterSignInUrl: \"/dashboard\",\n  afterSignOutUrl: \"/\",\n});\n```\n\nRoutes exposées automatiquement :\n\n| Route                       | Rôle                                                       |\n| --------------------------- | ---------------------------------------------------------- |\n| `GET  /api/auth/login`      | Démarre le flow OAuth (PKCE) — `?redirect_uri=` supporté   |\n| `GET  /api/auth/callback`   | Callback OAuth, échange le code, set les cookies           |\n| `GET  /api/auth/session`    | Retourne `{ user }` à partir des cookies                   |\n| `POST /api/auth/refresh`    | Refresh silencieux du token                                |\n| `POST /api/auth/logout`     | Détruit la session locale + remote                         |\n| `*    /api/auth/proxy/...`  | Proxy authentifié vers `${authUrl}/api/...` (CSRF requis)  |\n\n### 3. Middleware\n\n`middleware.ts` à la racine :\n\n```ts\nimport { createAuthMiddleware } from \"@btcv/auth-provider/next\";\n\nexport default createAuthMiddleware({\n  authUrl: process.env.AUTH_URL!,\n  publicRoutes: [\"/\", \"/about\", \"/pricing/*\"],\n});\n\nexport const config = {\n  matcher: [\"/((?!_next|favicon.ico|.*\\\\..*).*)\"],\n};\n```\n\nComportement :\n\n- Les routes `/api/auth/*` passent toujours.\n- Si session valide → injecte `x-user-id`, `x-user-email`, `x-user-name`, `x-user-image` dans la requête (et **strip** ces headers s'ils viennent du client, anti-forgery).\n- Si access token expiré + refresh dispo → tente un refresh silencieux et forward les `Set-Cookie`.\n- Sinon → redirect vers `/api/auth/login?redirect_uri=<pathname>`.\n- `publicRoutes` accepte les wildcards en suffixe (`/blog/*`).\n\n### 4. AuthProvider + AccountDialog (client)\n\n```tsx\n\"use client\";\nimport { AuthProvider, AccountDialog } from \"@btcv/auth-provider\";\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n  return (\n    <AuthProvider authUrl={process.env.NEXT_PUBLIC_AUTH_URL!}>\n      {children}\n    </AuthProvider>\n  );\n}\n```\n\n> Note : `<AuthProvider>` n'a besoin que de `authUrl`. Toutes les requêtes API passent par `basePath` (défaut `/api/auth`, donc le proxy du route handler) — il n'y a **pas** de `apiUrl` séparé.\n\n```tsx\nconst [open, setOpen] = useState(false);\n<AccountDialog open={open} onClose={() => setOpen(false)} defaultTab=\"profile\" />\n```\n\n---\n\n## Hooks client\n\n```ts\nimport { useSession, useAccount, usePasskeys, useAuthGuard } from \"@btcv/auth-provider\";\n\nconst { user, isLoading, isAuthenticated } = useSession();\nconst { updateProfile, revokeSession } = useAccount();\nconst { passkeys, addPasskey, removePasskey } = usePasskeys();\nuseAuthGuard(); // redirige automatiquement si non auth (utile en CSR pur)\n```\n\n## Helpers serveur (RSC / route handlers)\n\n```ts\nimport { getSession, withAuth } from \"@btcv/auth-provider/server\";\n\n// dans un Server Component\nconst session = await getSession(); // SessionData | null\n\n// wrapper pour les route handlers\nexport const GET = withAuth(async (req, { user }) => {\n  return Response.json({ hello: user.email });\n});\n```\n\n---\n\n## CSRF\n\nToute requête mutating (`POST/PATCH/DELETE`) à travers `/api/auth/proxy/*` doit inclure le header `X-CSRF-Token` égal à la valeur du cookie CSRF posé par `/api/auth/session`. Les hooks fournis (`useAccount`, `usePasskeys`, etc.) gèrent ça automatiquement — c'est seulement à savoir si tu appelles le proxy à la main.\n\n## Référence backend\n\nLe contrat REST complet (endpoints existants vs. à créer côté `me.btcv.fr`, headers, payloads) vit dans **[`api-specs.md`](./api-specs.md)**. Toute modif de `src/core/api.ts`, `src/core/tokens.ts` ou des hooks doit y rester alignée.\n\n## Dev local du package\n\n```bash\nnpm run dev          # tsup watch\nnpm run build        # bundle dist/\nnpm run typecheck\nnpm test             # vitest watch\nnpm run test:run     # vitest single run\n```\n","readmeFilename":"README.md","_rev":"1-728698e2a22aa1a716a037b67eaa2405"}