feat(auth): basic token validation for login + minor improvements on utils

This commit is contained in:
2024-10-27 01:40:10 +02:00
parent 9fc39b0a0c
commit 946c2f3c56
10 changed files with 667 additions and 24 deletions

View File

@@ -10,6 +10,7 @@ if (config.enableCors) {
app.use(cors());
}
app.use(express.json());
// Global middleware
app.use((req, _res, next) => {
console.log(`LOG: new ${req.method} request for ${req.url}`);

View File

@@ -0,0 +1 @@
export const AUTH_KEY_SIZE = 30;

View File

@@ -1,5 +1,33 @@
import { Router } from "express";
import { userService } from "../users/users.routes";
import { AuthService } from "./auth.service";
import { ResponseError } from "../../utils/response/response-error.model";
export const authRoutes = Router();
authRoutes.get("/signIn", async (req, res) => {});
const authService = new AuthService(userService);
authRoutes.post("/login", async (req, res) => {
try {
if (!req.body) {
res.status(400);
res.send(new ResponseError(400));
}
const token = await authService.signIn(
req.body.username,
req.body.password,
);
if (token) {
res.status(200);
res.send(token);
} else {
res.status(401);
res.send(new ResponseError(401));
}
} catch (e) {
res.status(500);
res.send(e);
}
});
export { authService };

View File

@@ -1,18 +1,27 @@
import { sign, verify } from "jsonwebtoken";
import UserService from "../users/users.service";
const secret_key = "asdf";
import UsersService from "../users/users.service";
import { compare } from "bcrypt";
import { generateRandomString } from "./auth.utils";
import { AUTH_KEY_SIZE } from "./auth.constants";
export class AuthService {
constructor(private usersService: UserService) {}
private secret_key: string;
constructor(private usersService: UsersService) {
this.secret_key = generateRandomString(AUTH_KEY_SIZE);
}
async signIn(username: string, password: string) {
console.log("__LOGIN", { username, password });
const user = await this.usersService.getUserByUsername(username);
if (!user) {
return null;
}
const isSamePasswd = await compare(`${password}`, `${user?.password}`);
if (!isSamePasswd) return null;
const payload = {
sub: user.id,
username: user.username,
@@ -20,7 +29,7 @@ export class AuthService {
picture: user.picture,
};
const token = sign(payload, secret_key, { expiresIn: "1h" });
const token = sign(payload, this.secret_key, { expiresIn: "1h" });
return token;
}
@@ -28,8 +37,9 @@ export class AuthService {
const token = jwt.split(".")[1];
if (!token) return false;
try {
const payload = verify(token, secret_key);
return payload.username;
const payload = verify(token, this.secret_key);
if (payload instanceof Object) return payload.username;
else return payload;
} catch (e) {
return false;
}

View File

@@ -0,0 +1,6 @@
function generateRandomString(size: number) {
const value = Math.random() * Math.pow(10, size);
return btoa(value.toString());
}
export { generateRandomString };

View File

@@ -1,12 +1,12 @@
import { Router } from "express";
import UserService from "./users.service";
import UsersService from "./users.service";
import { sanitize_user } from "./users.utils";
import { ResponseSuccess } from "../../utils/response/response-success.model";
import { ResponseError } from "../../utils/response/response-error.model";
export const userRoutes = Router();
const userService = new UserService();
const userService = new UsersService();
//TODO: block access to NON-admins or simply comment
userRoutes.get("/", async (_, res) => {
@@ -35,3 +35,5 @@ userRoutes.get("/:username", async (req, res) => {
res.send(e);
}
});
export { userService };

View File

@@ -1,7 +1,7 @@
import { db_query } from "../../db";
import { User } from "./users.types";
class UserService {
class UsersService {
constructor() {}
async getAllUsers(): Promise<User[]> {
@@ -18,4 +18,4 @@ class UserService {
}
}
export default UserService;
export default UsersService;

View File

@@ -1,14 +1,33 @@
type BasicError = {
code: string | number;
number?: number;
detail?: string;
status?: number;
suggestion?: string;
};
const code_error_mapping: Record<number, BasicError> = {
400: {
code: "BAD REQUEST",
status: 400,
},
401: {
code: "Unauthorized",
status: 401,
},
};
export class ResponseError extends Error {
public error: BasicError | number;
constructor(
public error: {
code: string | number;
number?: number;
detail?: string;
status?: number;
suggestion?: string;
},
error: BasicError | number,
public timestamp: number = Date.now(),
) {
super();
if (error instanceof Object) {
this.error = error;
} else {
this.error = code_error_mapping[error];
}
}
}