feat(auth): add @ketopath/auth package wrapping Better Auth

- New workspace packages/auth exporting:
  - auth: Better Auth server instance using Prisma adapter on @ketopath/db
  - makeAuthClient: React client factory parameterised by baseURL
  - readAuthEnv: Zod-validated env reader (BETTER_AUTH_SECRET min 32 chars,
    BETTER_AUTH_URL, optional GOOGLE_CLIENT_ID/SECRET)
- emailAndPassword enabled with min 8 chars, requireEmailVerification: false
  for MVP (re-enable in V1, see ADR 0001)
- Google provider conditionally registered when both env vars are present —
  keeps the package usable before OAuth configuration is delivered
- 7-day sessions, 24h rolling refresh, cookie prefix 'ketopath'

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
lucianoandClaude Opus 4.7 committed 2026-04-29 12:48:51 +02:00
1 parent 79d4943c00
commit 31eea207ba
9 files changed
+110 -1

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
root: true,
extends: ['@ketopath/eslint-config'],
};
+25
View File
@@ -0,0 +1,25 @@
{
"name": "@ketopath/auth",
"version": "0.0.0",
"private": true,
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./client": "./src/client.ts"
},
"scripts": {
"typecheck": "tsc --noEmit",
"lint": "eslint src --max-warnings=0"
},
"dependencies": {
"@ketopath/db": "workspace:*",
"better-auth": "^1.0.21",
"zod": "^3.23.8"
},
"devDependencies": {
"@ketopath/eslint-config": "workspace:*",
"@ketopath/tsconfig": "workspace:*"
}
}
+5
View File
@@ -0,0 +1,5 @@
import { createAuthClient } from 'better-auth/react';
export function makeAuthClient(baseURL: string): ReturnType<typeof createAuthClient> {
return createAuthClient({ baseURL });
}
+19
View File
@@ -0,0 +1,19 @@
import { z } from 'zod';
// Le env vars sono lette dal processo che importa @ketopath/auth (apps/web,
// apps/api, ecc.). Vengono validate al momento della creazione dell'istanza,
// così errori di configurazione emergono al boot e non a runtime.
const authEnvSchema = z.object({
BETTER_AUTH_SECRET: z
.string()
.min(32, 'BETTER_AUTH_SECRET deve essere lungo almeno 32 caratteri'),
BETTER_AUTH_URL: z.string().url(),
GOOGLE_CLIENT_ID: z.string().optional(),
GOOGLE_CLIENT_SECRET: z.string().optional(),
});
export type AuthEnv = z.infer<typeof authEnvSchema>;
export function readAuthEnv(source: NodeJS.ProcessEnv = process.env): AuthEnv {
return authEnvSchema.parse(source);
}
+2
View File
@@ -0,0 +1,2 @@
export { auth, type Auth } from './server.js';
export { readAuthEnv, type AuthEnv } from './env.js';
+40
View File
@@ -0,0 +1,40 @@
import { prisma } from '@ketopath/db';
import { betterAuth } from 'better-auth';
import { prismaAdapter } from 'better-auth/adapters/prisma';
import { readAuthEnv } from './env.js';
const env = readAuthEnv();
const googleProvider =
env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET
? {
google: {
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
},
}
: undefined;
export const auth = betterAuth({
database: prismaAdapter(prisma, { provider: 'postgresql' }),
secret: env.BETTER_AUTH_SECRET,
baseURL: env.BETTER_AUTH_URL,
emailAndPassword: {
enabled: true,
// MVP: nessuna verifica email per non bloccare l'onboarding (vedi
// docs/decisions/0001-auth-provider.md). Verrà attivata in V1.
requireEmailVerification: false,
minPasswordLength: 8,
},
socialProviders: googleProvider,
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 giorni
updateAge: 60 * 60 * 24, // refresh expiresAt ogni 24h se la session è attiva
},
advanced: {
cookiePrefix: 'ketopath',
},
});
export type Auth = typeof auth;
+12
View File
@@ -0,0 +1,12 @@
{
"extends": "@ketopath/tsconfig/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"composite": true,
"lib": ["DOM", "ES2022"],
"types": ["node"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
BIN
View File
Binary file not shown.
+2 -1
View File
@@ -3,6 +3,7 @@
"references": [ "references": [
{ "path": "./packages/shared" }, { "path": "./packages/shared" },
{ "path": "./packages/ui" }, { "path": "./packages/ui" },
{ "path": "./packages/db" } { "path": "./packages/db" },
{ "path": "./packages/auth" }
] ]
} }