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:
1 parent
79d4943c00
commit
31eea207ba
9 files changed
+110
-1
No files matched your search
@@ -0,0 +1,5 @@
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@ketopath/eslint-config'],
|
||||
};
|
||||
@@ -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:*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { createAuthClient } from 'better-auth/react';
|
||||
|
||||
export function makeAuthClient(baseURL: string): ReturnType<typeof createAuthClient> {
|
||||
return createAuthClient({ baseURL });
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { auth, type Auth } from './server.js';
|
||||
export { readAuthEnv, type AuthEnv } from './env.js';
|
||||
@@ -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;
|
||||
@@ -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"]
|
||||
}
|
||||
Generated
BIN
Binary file not shown.
+2
-1
@@ -3,6 +3,7 @@
|
||||
"references": [
|
||||
{ "path": "./packages/shared" },
|
||||
{ "path": "./packages/ui" },
|
||||
{ "path": "./packages/db" }
|
||||
{ "path": "./packages/db" },
|
||||
{ "path": "./packages/auth" }
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user