Skip to content

Commit

Permalink
add auth checking
Browse files Browse the repository at this point in the history
Signed-off-by: splatter <splatterxl@outlook.ie>
  • Loading branch information
splatterxl committed Oct 27, 2024
1 parent a17d373 commit 34d0081
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 8 deletions.
43 changes: 42 additions & 1 deletion src/common/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { compare, hash } from "bcrypt";
import { randomBytes } from "crypto";
import { FastifyReply, FastifyRequest } from "fastify";
import { SALT } from "./constants";
import { psqlClient } from "./database";
import { ErrorCode, RESTError } from "./error";
Expand All @@ -26,8 +27,9 @@ export async function generateToken(userID: bigint, addToDatabase: boolean): Pro
const token = randomBytes(60).toString("base64").replace("+", "");
const hashedToken = await hash(token, SALT);
const seed = Math.floor(Math.random() * (999999 - 100000 + 1)) + 100000;

if (addToDatabase)
await psqlClient.query("INSERT INTO tokens VALUES ($1, $2, $3)", [
await psqlClient.query("INSERT INTO tokens (id, seed, token) VALUES ($1, $2, $3)", [
userID,
seed,
hashedToken
Expand All @@ -36,6 +38,45 @@ export async function generateToken(userID: bigint, addToDatabase: boolean): Pro
userID,
seed,
token,
// FIXME: why is this a string if the whole thing is provided? -- splatter
full: `${userID}.${seed}.${token}`
};
}

export const authHook = async (req: FastifyRequest, reply: FastifyReply) => {
const auth = req.headers.authorization;

if (!auth) return;

const [userID, seed, token] = auth.split(".");

const tokenQuery = await psqlClient.query("SELECT * FROM tokens WHERE id=$1 AND seed=$2", [
userID,
seed
]);
if (!tokenQuery.rowCount) {
throw new RESTError(ErrorCode.AuthError, "Invalid token");
}

const potentialToken = tokenQuery.rows[0];

const authed = await compare(token, potentialToken.token);

if (!authed) {
throw new RESTError(ErrorCode.AuthError, "Invalid token");
}

const user = await psqlClient.query("SELECT * FROM users WHERE id=$1", [userID]);

if (!user.rowCount) {
throw new RESTError(ErrorCode.AuthError, "Invalid token");
}

req.user = user.rows[0];
};

declare module "fastify" {
interface FastifyRequest {
user: User;
}
}
2 changes: 2 additions & 0 deletions src/common/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export class RESTError extends APIError {
return 500;
}
}

static Authed = new RESTError(ErrorCode.AuthError, "Must be authenticated");
}

export class SocketError extends APIError {
Expand Down
6 changes: 6 additions & 0 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export type User = {
role: number;
};

export const sanitiseUser = (user: User): User => {
const { password, ...sanitised } = user;

return sanitised;
};

export type Token = {
userID: bigint;
seed: number;
Expand Down
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import fastify from "fastify";

import { bootstrap } from "fastify-decorators";
import { resolve } from "path";
import { authHook } from "./common/auth.js";
import { __DEV__ } from "./common/constants.js";
import { ERROR_HANDLER } from "./common/error.js";
import { Yapper } from "./common/Yapper.js";
import { __DEV__ } from "./common/constants.js";

export const server = fastify({
loggerInstance: new Yapper()
Expand All @@ -18,6 +19,10 @@ server.register(bootstrap, {

server.setErrorHandler(ERROR_HANDLER);

server.decorateRequest("user", null);
server.addHook("preHandler", authHook);

// what is this for? - splatter
if (__DEV__) {
server.log.trace("Trace message");
server.log.debug("Debug message");
Expand Down
12 changes: 6 additions & 6 deletions src/rest/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ export default class AuthController {
if (!(await validateCaptcha(body.turnstileKey)))
throw new RESTError(ErrorCode.ValidationError, "Invalid captcha");

const existing = await psqlClient.query(
"SELECT id FROM users WHERE username=$1 OR email=$2",
[body.username, body.email]
);

// Check for existing info
if (
await psqlClient.query("SELECT id FROM users WHERE username=$1 OR email=$2", [
body.username,
body.email
])
)
if (existing.rowCount)
throw new RESTError(ErrorCode.ConflictError, "Username or email already exists");

// Moderate username
Expand Down
16 changes: 16 additions & 0 deletions src/rest/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Controller, GET } from "fastify-decorators";
import { RESTError } from "../common/error.js";
import { sanitiseUser } from "../common/types.js";

@Controller({ route: "/users" })
export default class UserController {
@GET({
url: "/me"
})
async helloHandler(req, res) {
// TODO: add a decorator?
if (!req.user) throw RESTError.Authed;

return sanitiseUser(req.user);
}
}

0 comments on commit 34d0081

Please sign in to comment.