Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/config/http.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import express from 'express';
import IExpressConfig from '@src/core/domains/express/interfaces/IExpressConfig';
import parseBooleanFromString from '@src/core/util/parseBooleanFromString';
import bodyParser from 'body-parser';
import express from 'express';

const config: IExpressConfig = {
enabled: parseBooleanFromString(process.env.ENABLE_EXPRESS, 'true'),
Expand Down
1 change: 1 addition & 0 deletions src/core/domains/auth/actions/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export default async (req: Request, res: Response): Promise<void> => {
// Handle other errors
if (error instanceof Error) {
responseError(req, res, error);
return;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/core/domains/auth/actions/getUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default async (req: IAuthorizedRequest, res: Response) => {
// If there is an error, send the error response
if (error instanceof Error) {
responseError(req, res, error);
return;
}
}
};
1 change: 1 addition & 0 deletions src/core/domains/auth/actions/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default async (req: Request, res: Response): Promise<void> => {
// Handle other errors
if (error instanceof Error) {
responseError(req, res, error)
return;
}
}
}
1 change: 1 addition & 0 deletions src/core/domains/auth/actions/revoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default async (req: IAuthorizedRequest, res: Response) => {
// Handle any errors
if (error instanceof Error) {
responseError(req, res, error);
return;
}
}
};
1 change: 1 addition & 0 deletions src/core/domains/auth/actions/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default async (req: BaseRequest, res: Response) => {
// If there is an error, send the error response
if(error instanceof Error) {
responseError(req, res, error)
return;
}
}
}
1 change: 1 addition & 0 deletions src/core/domains/auth/actions/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default (req: IAuthorizedRequest, res: Response) => {
// Handle any errors
if (error instanceof Error) {
responseError(req, res, error);
return;
}
}
};
16 changes: 0 additions & 16 deletions src/core/domains/auth/interfaces/IAuthService.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
/* eslint-disable no-unused-vars */
import User from "@src/app/models/auth/User";
import IApiTokenModel from "@src/core/domains/auth/interfaces/IApitokenModel";
import IApiTokenRepository from "@src/core/domains/auth/interfaces/IApiTokenRepository";
import IUserModel from "@src/core/domains/auth/interfaces/IUserModel";
import IUserRepository from "@src/core/domains/auth/interfaces/IUserRepository";
import { ISecurityMiddleware } from "@src/core/domains/auth/middleware/securityMiddleware";
import { IRoute } from "@src/core/domains/express/interfaces/IRoute";
import IService from "@src/core/interfaces/IService";

Expand Down Expand Up @@ -104,18 +102,4 @@ export interface IAuthService extends IService {
* @memberof IAuthService
*/
getAuthRoutes(): IRoute[] | null;

/**
* Returns the authenticated user.
*
* @returns {User | null}
*/
user(): User | null;

/**
* Returns the security middleware
*
* @returns {ISecurityMiddleware}
*/
securityMiddleware(): ISecurityMiddleware;
}
5 changes: 5 additions & 0 deletions src/core/domains/auth/interfaces/IRequestIdentifiable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Request } from 'express';

export default interface IRequestIdentifiable extends Request {
id?: string;
}
7 changes: 0 additions & 7 deletions src/core/domains/auth/interfaces/ISecurityRequest.ts

This file was deleted.

46 changes: 0 additions & 46 deletions src/core/domains/auth/middleware/securityMiddleware.ts

This file was deleted.

8 changes: 4 additions & 4 deletions src/core/domains/auth/routes/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import user from "@src/core/domains/auth/actions/user";
import authConsts from "@src/core/domains/auth/consts/authConsts";
import { IAuthConfig } from "@src/core/domains/auth/interfaces/IAuthConfig";
import { IRoute } from "@src/core/domains/express/interfaces/IRoute";
import { authorize } from "@src/core/domains/express/middleware/authorize";
import { authorizeMiddleware } from "@src/core/domains/express/middleware/authorizeMiddleware";
import Route from "@src/core/domains/express/routing/Route";
import RouteGroup from "@src/core/domains/express/routing/RouteGroup";

Expand All @@ -31,7 +31,7 @@ export const routes = (config: IAuthConfig): IRoute[] => {
method: 'patch',
path: '/auth/user',
action: update,
middlewares: [authorize()],
middlewares: [authorizeMiddleware()],
validator: config.validators.updateUser,
validateBeforeAction: true
}),
Expand All @@ -40,14 +40,14 @@ export const routes = (config: IAuthConfig): IRoute[] => {
method: 'get',
path: '/auth/user',
action: user,
middlewares: [authorize()]
middlewares: [authorizeMiddleware()]
}),
Route({
name: authConsts.routes.authRevoke,
method: 'post',
path: '/auth/revoke',
action: revoke,
middlewares: [authorize()]
middlewares: [authorizeMiddleware()]
})
])
}
Expand Down
39 changes: 39 additions & 0 deletions src/core/domains/auth/services/AuthRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import UnauthorizedError from "@src/core/domains/auth/exceptions/UnauthorizedError";
import CurrentRequest from "@src/core/domains/express/services/CurrentRequest";
import { BaseRequest } from "@src/core/domains/express/types/BaseRequest.t";
import { App } from "@src/core/services/App";

class AuthRequest {

/**
* Attempts to authorize a request with a Bearer token.
*
* If successful, attaches the user and apiToken to the request. Sets the user in the App.
*
* @param req The request to authorize
* @returns The authorized request
* @throws UnauthorizedError if the token is invalid
*/
public static async attemptAuthorizeRequest(req: BaseRequest): Promise<BaseRequest> {
const authorization = (req.headers.authorization ?? '').replace('Bearer ', '');

const apiToken = await App.container('auth').attemptAuthenticateToken(authorization)

const user = await apiToken?.user()

if(!user || !apiToken) {
throw new UnauthorizedError();
}

req.user = user;
req.apiToken = apiToken

CurrentRequest.set(req, 'user', user);
CurrentRequest.set(req, 'userId', user?.getId())

return req;
}

}

export default AuthRequest
68 changes: 28 additions & 40 deletions src/core/domains/auth/services/AuthService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import User from '@src/app/models/auth/User';
import Service from '@src/core/base/Service';
import InvalidJWTSecret from '@src/core/domains/auth/exceptions/InvalidJWTSecret';
import UnauthorizedError from '@src/core/domains/auth/exceptions/UnauthorizedError';
Expand All @@ -8,16 +7,14 @@ import IApiTokenModel from '@src/core/domains/auth/interfaces/IApitokenModel';
import IApiTokenRepository from '@src/core/domains/auth/interfaces/IApiTokenRepository';
import { IAuthConfig } from '@src/core/domains/auth/interfaces/IAuthConfig';
import { IAuthService } from '@src/core/domains/auth/interfaces/IAuthService';
import { IJSonWebToken } from '@src/core/domains/auth/interfaces/IJSonWebToken';
import IUserModel from '@src/core/domains/auth/interfaces/IUserModel';
import IUserRepository from '@src/core/domains/auth/interfaces/IUserRepository';
import { securityMiddleware } from '@src/core/domains/auth/middleware/securityMiddleware';
import authRoutes from '@src/core/domains/auth/routes/auth';
import comparePassword from '@src/core/domains/auth/utils/comparePassword';
import createJwt from '@src/core/domains/auth/utils/createJwt';
import decodeJwt from '@src/core/domains/auth/utils/decodeJwt';
import { IRoute } from '@src/core/domains/express/interfaces/IRoute';
import { App } from '@src/core/services/App';
import { JsonWebTokenError } from 'jsonwebtoken';

export default class AuthService extends Service<IAuthConfig> implements IAuthService {

Expand All @@ -30,7 +27,7 @@ export default class AuthService extends Service<IAuthConfig> implements IAuthSe
* Repository for accessing user data
*/
public userRepository: IUserRepository;

/**
* Repository for accessing api tokens
*/
Expand All @@ -51,7 +48,7 @@ export default class AuthService extends Service<IAuthConfig> implements IAuthSe
* Validate jwt secret
*/
private validateJwtSecret() {
if(!this.config.jwtSecret || this.config.jwtSecret === '') {
if (!this.config.jwtSecret || this.config.jwtSecret === '') {
throw new InvalidJWTSecret();
}
}
Expand All @@ -66,7 +63,7 @@ export default class AuthService extends Service<IAuthConfig> implements IAuthSe
await apiToken.save();
return apiToken
}

/**
* Creates a JWT from a user model
* @param user
Expand All @@ -83,7 +80,7 @@ export default class AuthService extends Service<IAuthConfig> implements IAuthSe
* @returns
*/
jwt(apiToken: IApiTokenModel): string {
if(!apiToken?.data?.userId) {
if (!apiToken?.data?.userId) {
throw new Error('Invalid token');
}
const payload = JWTTokenFactory.create(apiToken.data?.userId?.toString(), apiToken.data?.token);
Expand All @@ -96,7 +93,7 @@ export default class AuthService extends Service<IAuthConfig> implements IAuthSe
* @returns
*/
async revokeToken(apiToken: IApiTokenModel): Promise<void> {
if(apiToken?.data?.revokedAt) {
if (apiToken?.data?.revokedAt) {
return;
}

Expand All @@ -110,21 +107,30 @@ export default class AuthService extends Service<IAuthConfig> implements IAuthSe
* @returns
*/
async attemptAuthenticateToken(token: string): Promise<IApiTokenModel | null> {
const decoded = decodeJwt(this.config.jwtSecret, token) as IJSonWebToken;
try {
const decoded = decodeJwt(this.config.jwtSecret, token);

const apiToken = await this.apiTokenRepository.findOneActiveToken(decoded.token)
const apiToken = await this.apiTokenRepository.findOneActiveToken(decoded.token)

if(!apiToken) {
throw new UnauthorizedError()
}
if (!apiToken) {
throw new UnauthorizedError()
}

const user = await this.userRepository.findById(decoded.uid)
const user = await this.userRepository.findById(decoded.uid)

if(!user) {
throw new UnauthorizedError()
if (!user) {
throw new UnauthorizedError()
}

return apiToken
}
catch (err) {
if(err instanceof JsonWebTokenError) {
throw new UnauthorizedError()
}
}

return apiToken
return null
}

/**
Expand All @@ -136,11 +142,11 @@ export default class AuthService extends Service<IAuthConfig> implements IAuthSe
async attemptCredentials(email: string, password: string): Promise<string> {
const user = await this.userRepository.findOneByEmail(email) as IUserModel;

if(!user?.data?.id) {
if (!user?.data?.id) {
throw new UnauthorizedError()
}

if(user?.data?.hashedPassword && !comparePassword(password, user.data?.hashedPassword)) {
if (user?.data?.hashedPassword && !comparePassword(password, user.data?.hashedPassword)) {
throw new UnauthorizedError()
}

Expand All @@ -153,35 +159,17 @@ export default class AuthService extends Service<IAuthConfig> implements IAuthSe
* @returns an array of IRoute objects, or null if auth routes are disabled
*/
getAuthRoutes(): IRoute[] | null {
if(!this.config.enableAuthRoutes) {
if (!this.config.enableAuthRoutes) {
return null
}

const routes = authRoutes(this.config);

if(!this.config.enableAuthRoutesAllowCreate) {
if (!this.config.enableAuthRoutesAllowCreate) {
return routes.filter((route) => route.name !== 'authCreate');
}

return routes;
}

/**
* Returns the currently authenticated user from the request context.
* @returns The user model if the user is authenticated, or null if not.
*/
user(): User | null {
return App.getValue<User>('user') ?? null;
}

/**
* Returns the security middleware for the AuthService.
*
* @returns The middleware that will run security checks defined in the route.
* @memberof AuthService
*/
securityMiddleware() {
return securityMiddleware;
}

}
Loading