feat(auth): working plugin

This commit is contained in:
Maieul BOYER 2025-08-27 22:25:44 +02:00 committed by Maix0
parent c545499c73
commit ddde700494
5 changed files with 140 additions and 31 deletions

View file

@ -9,7 +9,9 @@
"author": "",
"license": "ISC",
"dependencies": {
"@fastify/cookie": "^11.0.2",
"@fastify/jwt": "^9.1.0",
"@sinclair/typebox": "^0.34.40",
"@types/bcrypt": "^6.0.0",
"bcrypt": "^6.0.0",
"better-sqlite3": "^11.10.0",
@ -17,7 +19,6 @@
"fastify-plugin": "^5.0.1",
"joi": "^18.0.0",
"otp": "^1.1.2",
"rfc4648": "^1.5.4",
"uuidv7": "^1.0.2"
},
"devDependencies": {

View file

@ -1,16 +0,0 @@
//! Anything in this file shouldn't be used...
//!
//! This file is here because it is easier to share code in here.
import type { Database } from "@shared/database";
import type { UserId } from "../database/mixin/user";
import OTP, * as OtpModules from "otp";
let secret = "JKZSGXRCBP3UHOFVDVLYQ3W43IZH3D76";
let otp = new OTP({ secret, name: "test" });
console.log(`${otp.totpURL}`);
console.log(new URL(otp.totpURL));
setInterval(() => {
console.log(`${otp.totp(Date.now())}`);
}, 999);

View file

@ -1,10 +1,26 @@
import fastifyJwt from "@fastify/jwt";
import { FastifyPluginAsync } from "fastify";
import fp from 'fastify-plugin'
import { user } from "@shared/database"
import OTP from "otp";
import fastifyJwt from "@fastify/jwt";
import fp from 'fastify-plugin'
import { FastifyPluginAsync, FastifyRequest } from "fastify";
import { Static, Type } from "@sinclair/typebox"
import { useDatabase, user } from "@shared/database"
import cookie from "@fastify/cookie";
const kRouteAuthDone = Symbol('shared-route-auth-done');
declare module 'fastify' {
export interface FastifyInstance {
signJwt: (kind: "auth" | "otp", who: string) => string;
}
export interface FastifyRequest {
authUser?: user.UserId;
}
export interface FastifyContextConfig {
requireAuth?: boolean,
}
}
export const Otp = OTP;
export const jwtPlugin = fp<FastifyPluginAsync>(async (fastify, _opts) => {
let env = process.env.JWT_SECRET;
@ -14,14 +30,38 @@ export const jwtPlugin = fp<FastifyPluginAsync>(async (fastify, _opts) => {
secret: env,
decode: { complete: false },
});
void fastify.decorate("signJwt", (kind, who) => fastify.jwt.sign({ kind, who, createdAt: Date.now() }))
});
export type JwtClaims = {
id: user.UserId,
};
export const JwtType = Type.Object({
kind: Type.Union([
Type.Const("otp", { description: "the token is only valid for otp call" }),
Type.Const("auth", { description: "the token is valid for authentication" })
]),
who: Type.String({ description: "the login of the user" }),
createdAt: Type.Integer({ description: "Unix timestamp of when the token as been created at" })
});
export * as _inner from "./_inner.js";
export type JwtType = Static<typeof JwtType>;
export const otpPlugin = fp<FastifyPluginAsync>(async (fastify, _opts) => {
fastify.decorate('otp', {}, ["db"]);
export const authPlugin = fp<FastifyPluginAsync>(async (fastify, _opts) => {
await fastify.register(useDatabase as any, {});
await fastify.register(jwtPlugin as any, {});
await fastify.register(cookie);
fastify.addHook('onRoute', (routeOpts) => {
if (routeOpts.config?.requireAuth) {
routeOpts.preValidation = [function(req, res) {
if (req.cookies.token === undefined)
return res.clearCookie("token").send({ kind: "notLoggedIn", msg_key: "" })
let tok = this.jwt.verify<JwtType>(req.cookies.token);
if (tok.kind != "auth")
return res.clearCookie("token").send({ kind: "notLoggedIn", msg_key: "" })
let user = this.db.getUserFromName(tok.who);
if (user === null)
return res.clearCookie("token").send({ kind: "notLoggedIn", msg_key: "" })
req.authUser = user.id;
}, ...(routeOpts.preValidation as any || []),];
}
})
})