mirror of
https://github.com/kennethnym/aris.git
synced 2026-02-02 05:01:17 +00:00
feat(backend): add BetterAuth email/password authentication
- Add PostgreSQL connection (src/db.ts) - Configure BetterAuth with email/password (src/auth/index.ts) - Add session middleware for route protection (src/auth/session-middleware.ts) - Add registerAuthHandlers for mounting auth routes (src/auth/http.ts) - Rename index.ts to server.ts - Add .env.example with required environment variables Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
8
apps/aris-backend/.env.example
Normal file
8
apps/aris-backend/.env.example
Normal file
@@ -0,0 +1,8 @@
|
||||
# PostgreSQL connection string
|
||||
DATABASE_URL=postgresql://user:password@localhost:5432/aris
|
||||
|
||||
# BetterAuth secret (min 32 chars, generate with: openssl rand -base64 32)
|
||||
BETTER_AUTH_SECRET=
|
||||
|
||||
# Base URL of the backend
|
||||
BETTER_AUTH_URL=http://localhost:3000
|
||||
@@ -2,10 +2,10 @@
|
||||
"name": "@aris/backend",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"main": "src/server.ts",
|
||||
"scripts": {
|
||||
"dev": "bun run --watch src/index.ts",
|
||||
"start": "bun run src/index.ts",
|
||||
"dev": "bun run --watch src/server.ts",
|
||||
"start": "bun run src/server.ts",
|
||||
"test": "bun test src/"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -14,6 +14,9 @@
|
||||
"@aris/source-weatherkit": "workspace:*",
|
||||
"better-auth": "^1",
|
||||
"hono": "^4",
|
||||
"postgres": "^3"
|
||||
"pg": "^8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/pg": "^8"
|
||||
}
|
||||
}
|
||||
|
||||
7
apps/aris-backend/src/auth/http.ts
Normal file
7
apps/aris-backend/src/auth/http.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { Hono } from "hono"
|
||||
|
||||
import { auth } from "./index.ts"
|
||||
|
||||
export function registerAuthHandlers(app: Hono): void {
|
||||
app.on(["POST", "GET"], "/api/auth/*", (c) => auth.handler(c.req.raw))
|
||||
}
|
||||
10
apps/aris-backend/src/auth/index.ts
Normal file
10
apps/aris-backend/src/auth/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { betterAuth } from "better-auth"
|
||||
|
||||
import { pool } from "../db.ts"
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: pool,
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
})
|
||||
54
apps/aris-backend/src/auth/session-middleware.ts
Normal file
54
apps/aris-backend/src/auth/session-middleware.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import type { Context, Next } from "hono"
|
||||
|
||||
import { auth } from "./index.ts"
|
||||
|
||||
type SessionUser = typeof auth.$Infer.Session.user
|
||||
type Session = typeof auth.$Infer.Session.session
|
||||
|
||||
export interface SessionVariables {
|
||||
user: SessionUser | null
|
||||
session: Session | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware that attaches session and user to the context.
|
||||
* Does not reject unauthenticated requests - use requireSession for that.
|
||||
*/
|
||||
export async function sessionMiddleware(c: Context, next: Next): Promise<void> {
|
||||
const session = await auth.api.getSession({ headers: c.req.raw.headers })
|
||||
|
||||
if (session) {
|
||||
c.set("user", session.user)
|
||||
c.set("session", session.session)
|
||||
} else {
|
||||
c.set("user", null)
|
||||
c.set("session", null)
|
||||
}
|
||||
|
||||
await next()
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware that requires a valid session. Returns 401 if not authenticated.
|
||||
*/
|
||||
export async function requireSession(c: Context, next: Next): Promise<Response | void> {
|
||||
const session = await auth.api.getSession({ headers: c.req.raw.headers })
|
||||
|
||||
if (!session) {
|
||||
return c.json({ error: "Unauthorized" }, 401)
|
||||
}
|
||||
|
||||
c.set("user", session.user)
|
||||
c.set("session", session.session)
|
||||
await next()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session from headers. Useful for WebSocket upgrade validation.
|
||||
*/
|
||||
export async function getSessionFromHeaders(
|
||||
headers: Headers,
|
||||
): Promise<{ user: SessionUser; session: Session } | null> {
|
||||
const session = await auth.api.getSession({ headers })
|
||||
return session
|
||||
}
|
||||
5
apps/aris-backend/src/db.ts
Normal file
5
apps/aris-backend/src/db.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Pool } from "pg"
|
||||
|
||||
export const pool = new Pool({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
})
|
||||
@@ -1,9 +1,13 @@
|
||||
import { Hono } from "hono"
|
||||
|
||||
import { registerAuthHandlers } from "./auth/http.ts"
|
||||
|
||||
const app = new Hono()
|
||||
|
||||
app.get("/health", (c) => c.json({ status: "ok" }))
|
||||
|
||||
registerAuthHandlers(app)
|
||||
|
||||
export default {
|
||||
port: 3000,
|
||||
fetch: app.fetch,
|
||||
@@ -1,7 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": ["bun-types"]
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user