Refresh token
This commit is contained in:
parent
eef6e6fa97
commit
4e39aacedf
@ -12,6 +12,7 @@ import { IJWTPayload } from "../infraestructure";
|
||||
export interface IAuthService {
|
||||
generateAccessToken(payload: IJWTPayload): string;
|
||||
generateRefreshToken(payload: IJWTPayload): string;
|
||||
verifyRefreshToken(token: string): IJWTPayload;
|
||||
|
||||
registerUser(params: {
|
||||
username: Username;
|
||||
|
||||
@ -39,6 +39,10 @@ export class AuthService implements IAuthService {
|
||||
return JwtHelper.generateToken(payload, REFRESH_EXPIRATION);
|
||||
}
|
||||
|
||||
verifyRefreshToken(token: string): IJWTPayload {
|
||||
return JwtHelper.verifyToken(token);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Registra un nuevo usuario en la base de datos bajo transacción.
|
||||
@ -147,16 +151,16 @@ export class AuthService implements IAuthService {
|
||||
|
||||
// 🔹 Generar Access Token y Refresh Token
|
||||
const accessToken = this.generateAccessToken({
|
||||
userId: user.id.toString(),
|
||||
user_id: user.id.toString(),
|
||||
email: email.toString(),
|
||||
tabId: tabId.toString(),
|
||||
tab_id: tabId.toString(),
|
||||
roles: ["USER"],
|
||||
});
|
||||
|
||||
const refreshToken = this.generateRefreshToken({
|
||||
userId: user.id.toString(),
|
||||
user_id: user.id.toString(),
|
||||
email: email.toString(),
|
||||
tabId: tabId.toString(),
|
||||
tab_id: tabId.toString(),
|
||||
roles: ["USER"],
|
||||
});
|
||||
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { Result, ValueObject } from "@common/domain";
|
||||
import { z } from "zod";
|
||||
|
||||
export const NULLED_EMAIL_ADDRESS = null;
|
||||
|
||||
export class EmailAddress extends ValueObject<string | null> {
|
||||
static create(email: string | null): Result<EmailAddress, Error> {
|
||||
const normalizedEmail = email?.trim() === "" ? null : email?.toLowerCase() || null;
|
||||
const normalizedEmail =
|
||||
email?.trim() === "" ? NULLED_EMAIL_ADDRESS : email?.toLowerCase() || NULLED_EMAIL_ADDRESS;
|
||||
|
||||
const result = EmailAddress.validate(normalizedEmail);
|
||||
|
||||
@ -16,4 +19,8 @@ export class EmailAddress extends ValueObject<string | null> {
|
||||
const schema = z.string().email({ message: "Invalid email format" }).or(z.null());
|
||||
return schema.safeParse(email);
|
||||
}
|
||||
|
||||
isDefined(): boolean {
|
||||
return !this.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
const SECRET_KEY = process.env.JWT_SECRET || "supersecretkey";
|
||||
const ACCESS_EXPIRATION = process.env.JWT_ACCESS_EXPIRATION || "1h";
|
||||
const REFRESH_EXPIRATION = process.env.JWT_REFRESH_EXPIRATION || "7d";
|
||||
|
||||
export class JwtHelper {
|
||||
static generateToken(payload: object, expiresIn = "1h"): string {
|
||||
|
||||
@ -9,9 +9,9 @@ import { Strategy as LocalStrategy } from "passport-local";
|
||||
const SECRET_KEY = process.env.JWT_SECRET || "supersecretkey";
|
||||
|
||||
export interface IJWTPayload {
|
||||
userId: string;
|
||||
user_id: string;
|
||||
email: string;
|
||||
tabId: string;
|
||||
tab_id: string;
|
||||
roles: string[];
|
||||
}
|
||||
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
export * from "./login";
|
||||
export * from "./logout";
|
||||
export * from "./refreshToken";
|
||||
export * from "./register";
|
||||
|
||||
@ -26,6 +26,10 @@ class LoginController extends ExpressController {
|
||||
return this.clientError("Invalid input data", resultValidation.error);
|
||||
}
|
||||
|
||||
if (emailVO.data.isEmpty()) {
|
||||
return this.clientError("Invalid input data");
|
||||
}
|
||||
|
||||
const loginResultOrError = await this._authService.loginUser({
|
||||
email: emailVO.data,
|
||||
plainPassword: plainPasswordVO.data,
|
||||
|
||||
@ -23,6 +23,10 @@ class LogoutController extends ExpressController {
|
||||
return this.clientError("Invalid input data", resultValidation.error);
|
||||
}
|
||||
|
||||
if (emailVO.data.isEmpty()) {
|
||||
return this.clientError("Invalid input data");
|
||||
}
|
||||
|
||||
await this._authService.logoutUser({
|
||||
email: emailVO.data,
|
||||
tabId: tabIdVO.data,
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export * from "./refresh-token.controller";
|
||||
@ -0,0 +1,40 @@
|
||||
import { ExpressController } from "@common/presentation";
|
||||
import { createAuthService, IAuthService } from "@contexts/auth/application";
|
||||
import { IRefreshTokenPresenter, RefreshTokenPresenter } from "./refresh-token.presenter";
|
||||
|
||||
class RefreshTokenController extends ExpressController {
|
||||
private readonly _authService!: IAuthService;
|
||||
private readonly _presenter!: IRefreshTokenPresenter;
|
||||
|
||||
public constructor(authService: IAuthService, presenter: IRefreshTokenPresenter) {
|
||||
super();
|
||||
this._authService = authService;
|
||||
this._presenter = presenter;
|
||||
}
|
||||
|
||||
async executeImpl() {
|
||||
const tabId = String(this.req.headers["x-tab-id"]);
|
||||
const refreshToken = String(this.req.body.refresh_token);
|
||||
|
||||
const result = this._authService.verifyRefreshToken(refreshToken);
|
||||
if (!result || !result.email || !result.user_id || !result.tab_id || !result.roles) {
|
||||
return this.clientError("Invalid input data");
|
||||
}
|
||||
|
||||
const { user_id, tab_id, email, roles } = result;
|
||||
|
||||
const newRefreshToken = this._authService.generateRefreshToken({
|
||||
user_id,
|
||||
tab_id,
|
||||
email,
|
||||
roles,
|
||||
});
|
||||
|
||||
return this.created(this._presenter.map({ refreshToken: newRefreshToken }));
|
||||
}
|
||||
}
|
||||
|
||||
export const createRefreshTokenController = () => {
|
||||
const authService = createAuthService();
|
||||
return new RefreshTokenController(authService, RefreshTokenPresenter);
|
||||
};
|
||||
@ -0,0 +1,15 @@
|
||||
import { IRefreshTokenResponseDTO } from "../../dto";
|
||||
|
||||
export interface IRefreshTokenPresenter {
|
||||
map: (data: { refreshToken: string }) => IRefreshTokenResponseDTO;
|
||||
}
|
||||
|
||||
export const RefreshTokenPresenter: IRefreshTokenPresenter = {
|
||||
map: (data: { refreshToken: string }): IRefreshTokenResponseDTO => {
|
||||
const { refreshToken } = data;
|
||||
|
||||
return {
|
||||
refresh_token: refreshToken,
|
||||
};
|
||||
},
|
||||
};
|
||||
@ -22,6 +22,10 @@ class RegisterController extends ExpressController {
|
||||
return this.clientError("Invalid input data");
|
||||
}
|
||||
|
||||
if (emailVO.data.isEmpty()) {
|
||||
return this.clientError("Invalid input data");
|
||||
}
|
||||
|
||||
const userOrError = await this._authService.registerUser({
|
||||
username: usernameVO.data,
|
||||
email: emailVO.data,
|
||||
|
||||
@ -15,7 +15,10 @@ export interface ILoginUserResponseDTO {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
};
|
||||
//tab_id: string;
|
||||
}
|
||||
|
||||
export interface ILogoutResponseDTO {}
|
||||
|
||||
export interface IRefreshTokenResponseDTO {
|
||||
refresh_token: string;
|
||||
}
|
||||
|
||||
@ -11,6 +11,6 @@ export const LoginUserSchema = z.object({
|
||||
password: z.string().min(6, "Password must be at least 6 characters long"),
|
||||
});
|
||||
|
||||
export const SelectCompanySchema = z.object({
|
||||
companyId: z.string().min(1, "Company ID is required"),
|
||||
export const RefreshTokenSchema = z.object({
|
||||
refresh_token: z.string().min(1, "Refresh token is required"),
|
||||
});
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
export * from "../../../routes/auth.routes";
|
||||
export * from "./controllers";
|
||||
export * from "./dto";
|
||||
export * from "./middleware";
|
||||
|
||||
5
apps/server/src/contexts/companies/domain/index.ts
Normal file
5
apps/server/src/contexts/companies/domain/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export * from "./aggregates";
|
||||
export * from "./entities";
|
||||
export * from "./events";
|
||||
export * from "./repositories";
|
||||
export * from "./value-objects";
|
||||
@ -0,0 +1 @@
|
||||
export interface ICompanyRepository {}
|
||||
@ -0,0 +1 @@
|
||||
export * from "./company-repository.interface";
|
||||
@ -0,0 +1,3 @@
|
||||
export * from "./mappers";
|
||||
export * from "./passport";
|
||||
export * from "./sequelize";
|
||||
@ -1,10 +1,17 @@
|
||||
import { validateRequestDTO } from "@common/presentation";
|
||||
import { createAuthProvider } from "@contexts/auth/infraestructure";
|
||||
import { validateTabContextHeader } from "@contexts/auth/presentation";
|
||||
import { createLoginController } from "@contexts/auth/presentation/controllers";
|
||||
import {
|
||||
createLoginController,
|
||||
createRefreshTokenController,
|
||||
} from "@contexts/auth/presentation/controllers";
|
||||
import { createLogoutController } from "@contexts/auth/presentation/controllers/logout/logout.controller";
|
||||
import { createRegisterController } from "@contexts/auth/presentation/controllers/register/register.controller";
|
||||
import { LoginUserSchema, RegisterUserSchema } from "@contexts/auth/presentation/dto";
|
||||
import {
|
||||
LoginUserSchema,
|
||||
RefreshTokenSchema,
|
||||
RegisterUserSchema,
|
||||
} from "@contexts/auth/presentation/dto";
|
||||
import { NextFunction, Request, Response, Router } from "express";
|
||||
|
||||
export const authRouter = (appRouter: Router) => {
|
||||
@ -73,5 +80,15 @@ export const authRouter = (appRouter: Router) => {
|
||||
}
|
||||
);
|
||||
|
||||
authRoutes.post(
|
||||
"/refresh",
|
||||
validateRequestDTO(RefreshTokenSchema),
|
||||
//validateTabContextHeader,
|
||||
//authProvider.authenticateJWT(),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
createRefreshTokenController().execute(req, res, next);
|
||||
}
|
||||
);
|
||||
|
||||
appRouter.use("/auth", authRoutes);
|
||||
};
|
||||
|
||||
49
apps/server/src/routes/company.routes.ts
Normal file
49
apps/server/src/routes/company.routes.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { validateRequestDTO } from "@common/presentation";
|
||||
import { createAuthProvider } from "@contexts/company/infraestructure";
|
||||
import { validateTabContextHeader } from "@contexts/company/presentation";
|
||||
import { createLoginController } from "@contexts/company/presentation/controllers";
|
||||
import { createLogoutController } from "@contexts/company/presentation/controllers/logout/logout.controller";
|
||||
import { createRegisterController } from "@contexts/company/presentation/controllers/register/register.controller";
|
||||
import { LoginUserSchema, RegisterUserSchema } from "@contexts/company/presentation/dto";
|
||||
import { NextFunction, Request, Response, Router } from "express";
|
||||
|
||||
export const companyRouter = (appRouter: Router) => {
|
||||
const companyRoutes: Router = Router({ mergeParams: true });
|
||||
const authProvider = createAuthProvider();
|
||||
|
||||
companyRoutes.get(
|
||||
"/",
|
||||
/*validateRequestDTO(ListCompaniesSchema),*/
|
||||
validateTabContextHeader,
|
||||
authProvider.companyenticateJWT(),
|
||||
|
||||
getDealerMiddleware,
|
||||
handleRequest(listQuotesController)
|
||||
);
|
||||
companyRoutes.get("/:quoteId", checkUser, getDealerMiddleware, handleRequest(getQuoteController));
|
||||
companyRoutes.post("/", checkUser, getDealerMiddleware, handleRequest(createQuoteController));
|
||||
|
||||
companyRoutes.post("/register", validateRequestDTO(RegisterUserSchema), (req, res, next) => {
|
||||
createRegisterController().execute(req, res, next);
|
||||
});
|
||||
|
||||
companyRoutes.post(
|
||||
"/login",
|
||||
validateRequestDTO(LoginUserSchema),
|
||||
validateTabContextHeader,
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
createLoginController().execute(req, res, next);
|
||||
}
|
||||
);
|
||||
|
||||
companyRoutes.post(
|
||||
"/logout",
|
||||
validateTabContextHeader,
|
||||
authProvider.companyenticateJWT(),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
createLogoutController().execute(req, res, next);
|
||||
}
|
||||
);
|
||||
|
||||
appRouter.use("/company", companyRoutes);
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user