Files
library/api/src/app/plugins/auth.ts
T
2026-02-04 16:48:08 -08:00

90 lines
2.2 KiB
TypeScript

import { FastifyPluginAsync, FastifyRequest } from "fastify";
import fastifyPlugin from "fastify-plugin";
import fastifyJwt from "@fastify/jwt";
import fastifyCookie from "@fastify/cookie";
import fastifyOauth2 from "@fastify/oauth2";
declare module "fastify" {
interface FastifyInstance {
authenticate: (request: FastifyRequest) => Promise<void>;
oauth2Discord: any;
}
}
declare module "@fastify/jwt" {
interface FastifyJWT {
user: {
id: string;
username: string;
email?: string;
avatar?: string;
isAdmin: boolean;
};
}
}
const getJwtSecret = (): string => {
const secret = process.env.JWT_SECRET;
if (!secret) {
throw new Error("JWT_SECRET environment variable is required");
}
return secret;
};
const authPlugin: FastifyPluginAsync = async (app) => {
const jwtSecret = getJwtSecret();
// Register cookie plugin with signing secret
app.register(fastifyCookie, {
secret: jwtSecret,
});
// Register JWT plugin
app.register(fastifyJwt, {
secret: jwtSecret,
sign: {
algorithm: "HS256",
},
verify: {
algorithms: ["HS256"],
},
cookie: {
cookieName: "auth-token",
signed: true,
},
formatUser: (payload: { sub: string; email?: string; username: string; isAdmin: boolean }) => {
return {
id: payload.sub,
email: payload.email,
username: payload.username,
isAdmin: payload.isAdmin,
};
},
});
// Register Discord OAuth2
app.register(fastifyOauth2, {
name: "oauth2Discord",
scope: ["identify", "email"],
credentials: {
client: {
id: process.env.DISCORD_CLIENT_ID || "",
secret: process.env.DISCORD_CLIENT_SECRET || "",
},
auth: fastifyOauth2.DISCORD_CONFIGURATION,
},
startRedirectPath: "/api/auth/login",
callbackUri: `${process.env.BASE_URL || "http://localhost:3000"}/api/auth/callback`,
});
// Authentication decorator
app.decorate("authenticate", async (request: FastifyRequest) => {
try {
await request.jwtVerify();
} catch (err) {
throw app.httpErrors.unauthorized("Invalid token");
}
});
};
export default fastifyPlugin(authPlugin);