refactor(routes): routes to modules + feat(auth): auth middleware basic structure done
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import express from "express";
|
||||
import config from "./config";
|
||||
import cors from "cors";
|
||||
import { routes } from "./routes";
|
||||
import { routes } from "./modules";
|
||||
import { authorizationMiddleware } from "./modules/auth/auth.middleware";
|
||||
|
||||
const app = express();
|
||||
const port = config.port;
|
||||
@@ -12,6 +13,7 @@ if (config.enableCors) {
|
||||
|
||||
app.use(express.json());
|
||||
// Global middleware
|
||||
app.use(authorizationMiddleware as any);
|
||||
app.use((req, _res, next) => {
|
||||
console.log(`LOG: new ${req.method} request for ${req.url}`);
|
||||
next();
|
||||
@@ -23,7 +25,7 @@ app.use("/example", (req, _res, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
app.use("/api/v1", routes); // / serves as the base for the imported routes
|
||||
app.use(config.baseRoute, routes); // / serves as the base for the imported routes
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server running at http://localhost:${port}`);
|
||||
|
||||
@@ -5,6 +5,7 @@ configDotenv();
|
||||
const config = {
|
||||
enableCors: false,
|
||||
port: process.env.PORT || 3000,
|
||||
baseRoute: "/api/v1",
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
63
back-express/src/modules/auth/auth.middleware.ts
Normal file
63
back-express/src/modules/auth/auth.middleware.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import { authService } from "./auth.routes";
|
||||
import { Role } from "../users/users.types";
|
||||
import { ResponseError } from "../../utils/response/response-error.model";
|
||||
import config from "../../config";
|
||||
|
||||
type Permissions = {
|
||||
roles: Array<Role>;
|
||||
resource: string;
|
||||
actions: Array<"get" | "put" | "post" | "delete" | "config">;
|
||||
};
|
||||
|
||||
async function authorization(req: Request, res: Response, next: NextFunction) {
|
||||
const resource = req.url.replace(config.baseRoute, "");
|
||||
const isPublic = !!authorizationTable
|
||||
.find((permission) => permission.resource.startsWith(resource))
|
||||
?.roles.includes(Role.Public);
|
||||
|
||||
if (isPublic) {
|
||||
return next();
|
||||
}
|
||||
const authHeader = req.get("authorization");
|
||||
const token = authHeader && authHeader.split(" ")[1];
|
||||
|
||||
if (token == null) {
|
||||
res.status(401);
|
||||
return res.send(new ResponseError(401));
|
||||
}
|
||||
|
||||
const user = authService.verifyToken(token);
|
||||
if (user && user instanceof Object) {
|
||||
// FIXME: this check is wrong, needs to take into account the resource
|
||||
const authorized = authorizationTable.find((auth) =>
|
||||
user.roles.some((role) => auth.roles.includes(role)),
|
||||
);
|
||||
if (authorized) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
res.status(401);
|
||||
return res.send(new ResponseError(401));
|
||||
}
|
||||
|
||||
// TODO: resource should be a regex
|
||||
const authorizationTable: Permissions[] = [
|
||||
{
|
||||
roles: [Role.Public],
|
||||
resource: "/auth/login",
|
||||
actions: ["post"],
|
||||
},
|
||||
{
|
||||
roles: [Role.Admin],
|
||||
resource: "/users",
|
||||
actions: ["get", "put", "post", "delete"],
|
||||
},
|
||||
{
|
||||
roles: [Role.User],
|
||||
resource: "/users",
|
||||
actions: ["get"],
|
||||
},
|
||||
];
|
||||
|
||||
export { authorization as authorizationMiddleware };
|
||||
@@ -19,7 +19,7 @@ authRoutes.post("/login", async (req, res) => {
|
||||
);
|
||||
if (token) {
|
||||
res.status(200);
|
||||
res.send(token);
|
||||
res.send({ access_token: token });
|
||||
} else {
|
||||
res.status(401);
|
||||
res.send(new ResponseError(401));
|
||||
@@ -3,6 +3,7 @@ import UsersService from "../users/users.service";
|
||||
import { compare } from "bcrypt";
|
||||
import { generateRandomString } from "./auth.utils";
|
||||
import { AUTH_KEY_SIZE } from "./auth.constants";
|
||||
import { Role } from "../users/users.types";
|
||||
|
||||
export class AuthService {
|
||||
private secret_key: string;
|
||||
@@ -11,7 +12,6 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async signIn(username: string, password: string) {
|
||||
console.log("__LOGIN", { username, password });
|
||||
const user = await this.usersService.getUserByUsername(username);
|
||||
|
||||
if (!user) {
|
||||
@@ -29,17 +29,21 @@ export class AuthService {
|
||||
picture: user.picture,
|
||||
};
|
||||
|
||||
const token = sign(payload, this.secret_key, { expiresIn: "1h" });
|
||||
const token = sign(payload, this.secret_key, { expiresIn: 60 * 60 });
|
||||
return token;
|
||||
}
|
||||
|
||||
async verifyToken(jwt: string) {
|
||||
verifyToken(jwt: string) {
|
||||
const token = jwt.split(".")[1];
|
||||
if (!token) return false;
|
||||
try {
|
||||
const payload = verify(token, this.secret_key);
|
||||
if (payload instanceof Object) return payload.username;
|
||||
else return payload;
|
||||
const payload = verify(jwt, this.secret_key);
|
||||
if (payload instanceof Object)
|
||||
return {
|
||||
username: payload.username as string,
|
||||
roles: payload.roles as Role[],
|
||||
};
|
||||
else return false;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
@@ -4,9 +4,13 @@ import { defaultRoute } from "./default.route";
|
||||
import { exampleRoutes } from "./example/example.routes";
|
||||
import { userRoutes } from "./users/users.routes";
|
||||
import { authRoutes } from "./auth/auth.routes";
|
||||
import { authorizationMiddleware } from "./auth/auth.middleware";
|
||||
|
||||
export const routes = express.Router();
|
||||
|
||||
/*
|
||||
* Routes
|
||||
* */
|
||||
routes.use("/", defaultRoute);
|
||||
routes.use("/example", exampleRoutes);
|
||||
routes.use("/users", userRoutes);
|
||||
@@ -6,15 +6,25 @@ class UsersService {
|
||||
|
||||
async getAllUsers(): Promise<User[]> {
|
||||
const data = await db_query("select * from user");
|
||||
return data as User[];
|
||||
const users: User[] = data.map((user: any) => ({
|
||||
...user,
|
||||
roles: user.roles.split(";"),
|
||||
}));
|
||||
return users;
|
||||
}
|
||||
|
||||
async getUserByUsername(username: string): Promise<User | null> {
|
||||
const data = await db_query(
|
||||
`select * from user as user WHERE LOWER(username) = LOWER('${username}');`,
|
||||
);
|
||||
console.log("Data:", data);
|
||||
return data.length ? (data[0] as User) : null;
|
||||
if (data.length) {
|
||||
const user = {
|
||||
...(data[0] as User),
|
||||
roles: (data[0] as any).roles.split(";"),
|
||||
};
|
||||
return user;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user