diff --git a/apps/api/package.json b/apps/api/package.json index 42da91d..6fd7208 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -13,9 +13,11 @@ "@glossa/db": "workspace:*", "@glossa/shared": "workspace:*", "better-auth": "^1.6.2", + "cors": "^2.8.6", "express": "^5.2.1" }, "devDependencies": { + "@types/cors": "^2.8.19", "@types/express": "^5.0.6", "@types/supertest": "^7.2.0", "supertest": "^7.2.2", diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts index 79a45fd..276f0e2 100644 --- a/apps/api/src/app.ts +++ b/apps/api/src/app.ts @@ -4,10 +4,12 @@ import { toNodeHandler } from "better-auth/node"; import { auth } from "./lib/auth.js"; import { apiRouter } from "./routes/apiRouter.js"; import { errorHandler } from "./middleware/errorHandler.js"; +import cors from "cors"; export function createApp() { const app: Express = express(); + app.use(cors({ origin: "http://localhost:5173", credentials: true })); app.all("/api/auth/*splat", toNodeHandler(auth)); app.use(express.json()); app.use("/api/v1", apiRouter); diff --git a/apps/api/src/lib/auth.ts b/apps/api/src/lib/auth.ts index c594f92..d3bc934 100644 --- a/apps/api/src/lib/auth.ts +++ b/apps/api/src/lib/auth.ts @@ -1,9 +1,11 @@ import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { db } from "@glossa/db"; +import * as schema from "@glossa/db/schema"; export const auth = betterAuth({ - database: drizzleAdapter(db, { provider: "pg" }), + database: drizzleAdapter(db, { provider: "pg", schema }), + trustedOrigins: ["http://localhost:5173"], socialProviders: { google: { clientId: process.env["GOOGLE_CLIENT_ID"] as string, diff --git a/apps/api/src/middleware/authMiddleware.ts b/apps/api/src/middleware/authMiddleware.ts new file mode 100644 index 0000000..da18d01 --- /dev/null +++ b/apps/api/src/middleware/authMiddleware.ts @@ -0,0 +1,20 @@ +import type { Request, Response, NextFunction } from "express"; +import { fromNodeHeaders } from "better-auth/node"; +import { auth } from "../lib/auth.js"; + +export const requireAuth = async ( + req: Request, + res: Response, + next: NextFunction, +) => { + const session = await auth.api.getSession({ + headers: fromNodeHeaders(req.headers), + }); + + if (!session) { + res.status(401).json({ success: false, error: "Unauthorized" }); + return; + } + + next(); +}; diff --git a/apps/api/src/routes/gameRouter.ts b/apps/api/src/routes/gameRouter.ts index 664a640..f65bfb6 100644 --- a/apps/api/src/routes/gameRouter.ts +++ b/apps/api/src/routes/gameRouter.ts @@ -1,8 +1,10 @@ import express from "express"; import type { Router } from "express"; import { createGame, submitAnswer } from "../controllers/gameController.js"; +import { requireAuth } from "../middleware/authMiddleware.js"; export const gameRouter: Router = express.Router(); +gameRouter.use(requireAuth); gameRouter.post("/start", createGame); gameRouter.post("/answer", submitAnswer); diff --git a/apps/web/src/lib/auth-client.ts b/apps/web/src/lib/auth-client.ts new file mode 100644 index 0000000..3addfad --- /dev/null +++ b/apps/web/src/lib/auth-client.ts @@ -0,0 +1,7 @@ +import { createAuthClient } from "better-auth/react"; + +export const authClient = createAuthClient({ + baseURL: "http://localhost:3000", +}); + +export const { signIn, signOut, useSession } = authClient; diff --git a/apps/web/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts index 17e2c55..ce1cdf1 100644 --- a/apps/web/src/routeTree.gen.ts +++ b/apps/web/src/routeTree.gen.ts @@ -10,6 +10,7 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as PlayRouteImport } from './routes/play' +import { Route as LoginRouteImport } from './routes/login' import { Route as AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' @@ -18,6 +19,11 @@ const PlayRoute = PlayRouteImport.update({ path: '/play', getParentRoute: () => rootRouteImport, } as any) +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', + getParentRoute: () => rootRouteImport, +} as any) const AboutRoute = AboutRouteImport.update({ id: '/about', path: '/about', @@ -32,30 +38,34 @@ const IndexRoute = IndexRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/about': typeof AboutRoute + '/login': typeof LoginRoute '/play': typeof PlayRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/about': typeof AboutRoute + '/login': typeof LoginRoute '/play': typeof PlayRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/about': typeof AboutRoute + '/login': typeof LoginRoute '/play': typeof PlayRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/about' | '/play' + fullPaths: '/' | '/about' | '/login' | '/play' fileRoutesByTo: FileRoutesByTo - to: '/' | '/about' | '/play' - id: '__root__' | '/' | '/about' | '/play' + to: '/' | '/about' | '/login' | '/play' + id: '__root__' | '/' | '/about' | '/login' | '/play' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute AboutRoute: typeof AboutRoute + LoginRoute: typeof LoginRoute PlayRoute: typeof PlayRoute } @@ -68,6 +78,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PlayRouteImport parentRoute: typeof rootRouteImport } + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport + parentRoute: typeof rootRouteImport + } '/about': { id: '/about' path: '/about' @@ -88,6 +105,7 @@ declare module '@tanstack/react-router' { const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, AboutRoute: AboutRoute, + LoginRoute: LoginRoute, PlayRoute: PlayRoute, } export const routeTree = rootRouteImport diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index a05fa98..1448282 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -1,20 +1,51 @@ -import { createRootRoute, Link, Outlet } from "@tanstack/react-router"; +import { + createRootRoute, + Link, + Outlet, + useNavigate, +} from "@tanstack/react-router"; import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; +import { useSession, signOut } from "../lib/auth-client"; -const RootLayout = () => ( - <> -