[INIT] Initial Project Structure
This commit is contained in:
commit
0fc5f05b6a
105 changed files with 10448 additions and 0 deletions
48
lib/modules/auth/auth.controller.ts
Normal file
48
lib/modules/auth/auth.controller.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { Request, Response, Express } from "express";
|
||||
import { VixpressController } from "@dunemask/vix";
|
||||
import Cairo from "@lib/Cairo";
|
||||
import type PostgresService from "@lib/database/PostgresService";
|
||||
import { CAuthContract, AuthContract } from "@lib/contracts/auth.contracts";
|
||||
import { ContractRouteContext } from "@dunemask/vix/express";
|
||||
import { AuthErrors, ProjectErrors } from "@lib/vix/ClientErrors";
|
||||
import { getUserToken, hashCompare } from "./auth.service";
|
||||
import { CDatabaseContract } from "@lib/contracts/database.contracts";
|
||||
import { ProjectContract } from "@lib/contracts/project.contracts";
|
||||
import { UserRequest } from "@lib/types/ApiRequests";
|
||||
import { ResourcePolicy } from "@dunemask/vix/util";
|
||||
|
||||
type LoginCRC = ContractRouteContext<{
|
||||
RequestBodyContract: typeof AuthContract.Login;
|
||||
RequestParamsContract: typeof ProjectContract.ProjectParams;
|
||||
}>;
|
||||
|
||||
export default class AuthController extends VixpressController {
|
||||
declare pg: PostgresService;
|
||||
constructor(app: Express) {
|
||||
super(app);
|
||||
this.pg = this.app.get(Cairo.PostgresService);
|
||||
}
|
||||
|
||||
verify = (_req: Request, res: Response) => res.sendStatus(200);
|
||||
|
||||
async login(crc: LoginCRC): Promise<CAuthContract["LoginCredentials"]> {
|
||||
const { identity, password } = crc.reqBody;
|
||||
const { project } = crc.reqParams;
|
||||
const user = await this.pg.users.byIdentity(project, identity);
|
||||
if (!user?.rolePolicy?.policies) throw AuthErrors.UnauthorizedRequest;
|
||||
const authorized = await hashCompare(password, user.hash);
|
||||
if (!authorized) throw AuthErrors.UnauthorizedRequest;
|
||||
const projectKeyPairs = user.project.keyPairs;
|
||||
if (projectKeyPairs.length !== 1) throw ProjectErrors.BadRequestProjectIncomplete;
|
||||
const token = await getUserToken(user.id, user.project.keyPairs[0].encryptedPrivateKey);
|
||||
const policies = user.rolePolicy.policies;
|
||||
const userData: CDatabaseContract["User"] = { username: user.username, rolePolicyId: user.rolePolicyId };
|
||||
return { token, user: userData, policies };
|
||||
}
|
||||
|
||||
async credentials(crc: ContractRouteContext): Promise<CAuthContract["Credentials"]> {
|
||||
const { user, policies } = crc.req as UserRequest;
|
||||
const userData: CDatabaseContract["User"] = { username: user.username, rolePolicyId: user.rolePolicyId };
|
||||
return { user: userData, policies: ResourcePolicy.asStrings(policies) };
|
||||
}
|
||||
}
|
26
lib/modules/auth/auth.router.ts
Normal file
26
lib/modules/auth/auth.router.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { VixpressRoute } from "@dunemask/vix";
|
||||
import { contract } from "@dunemask/vix/express";
|
||||
import RouteGuard from "@lib/vix/RouteGuard";
|
||||
import { Router } from "express";
|
||||
import AuthController from "./auth.controller";
|
||||
import { AuthContract } from "@lib/contracts/auth.contracts";
|
||||
import { ProjectContract } from "@lib/contracts/project.contracts";
|
||||
|
||||
export class AuthRoute extends VixpressRoute {
|
||||
async configureRoutes(router: Router) {
|
||||
const jsonOpts = { limit: "20mb" };
|
||||
const cBase = { json: jsonOpts, reqParams: ProjectContract.ProjectParams };
|
||||
// Controllers
|
||||
const authController = this.useController(AuthController);
|
||||
|
||||
// Configuration
|
||||
const loginCreds = { ...cBase, reqBody: AuthContract.Login, resBody: AuthContract.LoginCredentials };
|
||||
const credRes = { ...cBase, resBody: AuthContract.Credentials };
|
||||
// Middleware
|
||||
|
||||
// Routes
|
||||
router.get("/verify", RouteGuard.User, authController.verify);
|
||||
router.post("/login", contract(authController.login, loginCreds));
|
||||
router.get("/credentials", RouteGuard.User, contract(authController.credentials, credRes));
|
||||
}
|
||||
}
|
36
lib/modules/auth/auth.service.ts
Normal file
36
lib/modules/auth/auth.service.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import bcrypt from "bcrypt";
|
||||
import { signToken, verifyToken } from "@lib/svc/token.service";
|
||||
import config from "@lib/config";
|
||||
import { decrypt } from "@lib/svc/crypt.service";
|
||||
const { HashRounds } = config.SigningOptions;
|
||||
|
||||
export async function getUserToken(id: string, encryptedPrivateKey: string) {
|
||||
const privateKey = await decrypt(encryptedPrivateKey, config.SigningOptions.Keys.KeyPair);
|
||||
const tokenPayload = {
|
||||
iss: config.SigningOptions.Issuer,
|
||||
sub: [config.SigningOptions.Subjects.User],
|
||||
aud: [config.SigningOptions.Issuer],
|
||||
id,
|
||||
};
|
||||
return signToken(tokenPayload, privateKey);
|
||||
}
|
||||
|
||||
export async function userTokenLogin(token: string, encryptedPublicKey: string): Promise<boolean> {
|
||||
const publicKey = await decrypt(encryptedPublicKey, config.SigningOptions.Keys.KeyPair);
|
||||
return !!verifyToken(token, publicKey);
|
||||
}
|
||||
|
||||
export async function getUserTokenId(token: string, encryptedPublicKey: string): Promise<string | undefined> {
|
||||
const publicKey = await decrypt(encryptedPublicKey, config.SigningOptions.Keys.KeyPair);
|
||||
const tokenData = verifyToken(token, publicKey);
|
||||
if (!tokenData) return undefined;
|
||||
return tokenData.id;
|
||||
}
|
||||
|
||||
export async function hashText(password: string) {
|
||||
return bcrypt.hash(password, HashRounds);
|
||||
}
|
||||
|
||||
export async function hashCompare(password: string, hash: string) {
|
||||
return bcrypt.compare(password, hash);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue