Skip to content

Commit e714cd3

Browse files
authored
Merge pull request #1542 from rocket-admin/backend_ai_table_settings_fix
Enhance JWT verification in AuthMiddlewares and update test assertions for unauthorized access
2 parents ba9de5e + c0964f9 commit e714cd3

File tree

6 files changed

+91
-67
lines changed

6 files changed

+91
-67
lines changed

backend/src/authorization/auth-with-api.middleware.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,20 @@ export class AuthWithApiMiddleware implements NestMiddleware {
6363
const jwtSecret = process.env.JWT_SECRET;
6464
const data = jwt.verify(tokenFromCookie, jwtSecret) as jwt.JwtPayload;
6565
const userId = data.id;
66+
6667
if (!userId) {
6768
throw new UnauthorizedException('JWT verification failed');
6869
}
70+
71+
const userExists = await this.userRepository.findOne({ where: { id: userId } });
72+
if (!userExists) {
73+
throw new UnauthorizedException('JWT verification failed');
74+
}
75+
76+
if (userExists.suspended) {
77+
throw new UnauthorizedException(Messages.ACCOUNT_SUSPENDED);
78+
}
79+
6980
const addedScope: Array<JwtScopesEnum> = data.scope;
7081
if (addedScope && addedScope.length > 0) {
7182
if (addedScope.includes(JwtScopesEnum.TWO_FA_ENABLE)) {
@@ -104,13 +115,14 @@ export class AuthWithApiMiddleware implements NestMiddleware {
104115
.where('api_key.hash = :hash', { hash: apiKeyHash })
105116
.getOne();
106117

118+
if (!foundUserByApiKey) {
119+
throw new NotFoundException(Messages.NO_AUTH_KEYS_FOUND);
120+
}
121+
107122
if (foundUserByApiKey.suspended) {
108123
throw new UnauthorizedException(Messages.API_KEY_SUSPENDED);
109124
}
110125

111-
if (!foundUserByApiKey) {
112-
throw new NotFoundException(Messages.NO_AUTH_KEYS_FOUND);
113-
}
114126
req.decoded = {
115127
sub: foundUserByApiKey.id,
116128
email: foundUserByApiKey.email,
Lines changed: 71 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {
2-
BadRequestException,
3-
HttpException,
4-
Injectable,
5-
InternalServerErrorException,
6-
NestMiddleware,
7-
UnauthorizedException,
2+
BadRequestException,
3+
HttpException,
4+
Injectable,
5+
InternalServerErrorException,
6+
NestMiddleware,
7+
UnauthorizedException,
88
} from '@nestjs/common';
99
import { InjectRepository } from '@nestjs/typeorm';
1010
import { Response } from 'express';
@@ -21,61 +21,73 @@ import { IRequestWithCognitoInfo } from './cognito-decoded.interface.js';
2121

2222
@Injectable()
2323
export class AuthMiddleware implements NestMiddleware {
24-
public constructor(
25-
@InjectRepository(UserEntity)readonly _userRepository: Repository<UserEntity>,
26-
@InjectRepository(LogOutEntity)
27-
private readonly logOutRepository: Repository<LogOutEntity>,
28-
) {}
29-
async use(req: IRequestWithCognitoInfo, _res: Response, next: (err?: any, res?: any) => void): Promise<void> {
30-
let token: string;
31-
try {
32-
token = req.cookies[Constants.JWT_COOKIE_KEY_NAME];
33-
} catch (_e) {
34-
if (process.env.NODE_ENV !== 'test') {
35-
throw new UnauthorizedException('JWT verification failed');
36-
}
37-
}
24+
public constructor(
25+
@InjectRepository(UserEntity)
26+
private readonly userRepository: Repository<UserEntity>,
27+
@InjectRepository(LogOutEntity)
28+
private readonly logOutRepository: Repository<LogOutEntity>,
29+
) {}
30+
async use(req: IRequestWithCognitoInfo, _res: Response, next: (err?: any, res?: any) => void): Promise<void> {
31+
let token: string;
32+
try {
33+
token = req.cookies[Constants.JWT_COOKIE_KEY_NAME];
34+
} catch (_e) {
35+
if (process.env.NODE_ENV !== 'test') {
36+
throw new UnauthorizedException('JWT verification failed');
37+
}
38+
}
3839

39-
if (!token) {
40-
throw new UnauthorizedException('Token is missing');
41-
}
40+
if (!token) {
41+
throw new UnauthorizedException('Token is missing');
42+
}
4243

43-
const isLoggedOut = !!(await this.logOutRepository.findOne({ where: { jwtToken: token } }));
44-
if (isLoggedOut) {
45-
throw new UnauthorizedException('Token is invalid');
46-
}
44+
const isLoggedOut = !!(await this.logOutRepository.findOne({ where: { jwtToken: token } }));
45+
if (isLoggedOut) {
46+
throw new UnauthorizedException('Token is invalid');
47+
}
4748

48-
try {
49-
const jwtSecret = process.env.JWT_SECRET;
50-
const data = jwt.verify(token, jwtSecret) as jwt.JwtPayload;
51-
const userId = data.id;
52-
if (!userId) {
53-
throw new UnauthorizedException('JWT verification failed');
54-
}
55-
const addedScope: Array<JwtScopesEnum> = data.scope;
56-
if (addedScope && addedScope.length > 0) {
57-
if (addedScope.includes(JwtScopesEnum.TWO_FA_ENABLE)) {
58-
throw new BadRequestException(Messages.TWO_FA_REQUIRED);
59-
}
60-
}
49+
try {
50+
const jwtSecret = process.env.JWT_SECRET;
51+
const data = jwt.verify(token, jwtSecret) as jwt.JwtPayload;
52+
const userId = data.id;
6153

62-
const payload = {
63-
sub: userId,
64-
email: data.email,
65-
exp: data.exp,
66-
iat: data.iat,
67-
};
68-
if (!payload || isObjectEmpty(payload)) {
69-
throw new UnauthorizedException('JWT verification failed');
70-
}
71-
req.decoded = payload;
72-
next();
73-
} catch (e) {
74-
Sentry.captureException(e);
75-
if (e instanceof HttpException || e instanceof UnauthorizedException) {
76-
throw e;
77-
}
78-
throw new InternalServerErrorException(Messages.AUTHORIZATION_REJECTED);
79-
}
80-
}
54+
if (!userId) {
55+
throw new UnauthorizedException('JWT verification failed');
56+
}
57+
58+
const userExists = await this.userRepository.findOne({ where: { id: userId } });
59+
if (!userExists) {
60+
throw new UnauthorizedException('JWT verification failed');
61+
}
62+
63+
if (userExists.suspended) {
64+
throw new UnauthorizedException(Messages.ACCOUNT_SUSPENDED);
65+
}
66+
67+
const addedScope: Array<JwtScopesEnum> = data.scope;
68+
if (addedScope && addedScope.length > 0) {
69+
if (addedScope.includes(JwtScopesEnum.TWO_FA_ENABLE)) {
70+
throw new BadRequestException(Messages.TWO_FA_REQUIRED);
71+
}
72+
}
73+
74+
const payload = {
75+
sub: userId,
76+
email: data.email,
77+
exp: data.exp,
78+
iat: data.iat,
79+
};
80+
if (!payload || isObjectEmpty(payload)) {
81+
throw new UnauthorizedException('JWT verification failed');
82+
}
83+
req.decoded = payload;
84+
next();
85+
} catch (e) {
86+
Sentry.captureException(e);
87+
if (e instanceof HttpException || e instanceof UnauthorizedException) {
88+
throw e;
89+
}
90+
throw new InternalServerErrorException(Messages.AUTHORIZATION_REJECTED);
91+
}
92+
}
8193
}

backend/test/ava-tests/non-saas-tests/non-saas-company-info-e2e.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,5 +658,5 @@ test.serial(`${currentTest} should delete company`, async (t) => {
658658
.set('Cookie', adminUserToken)
659659
.set('Accept', 'application/json');
660660

661-
t.is(foundCompanyInfoAfterDelete.status, 403);
661+
t.is(foundCompanyInfoAfterDelete.status, 401);
662662
});

backend/test/ava-tests/non-saas-tests/non-saas-user-e2e.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ test.serial(`${currentTest} should return user deletion result`, async (t) => {
9595
.set('Cookie', token)
9696
.set('Content-Type', 'application/json')
9797
.set('Accept', 'application/json');
98-
t.is(getUserResult.status, 404);
98+
t.is(getUserResult.status, 401);
9999
t.pass();
100100
});
101101

backend/test/ava-tests/saas-tests/company-info-e2e.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ test.serial(`${currentTest} should delete company`, async (t) => {
726726
.set('Cookie', adminUserToken)
727727
.set('Accept', 'application/json');
728728

729-
t.is(foundCompanyInfoAfterDelete.status, 403);
729+
t.is(foundCompanyInfoAfterDelete.status, 401);
730730
});
731731

732732
currentTest = `PUT company/2fa/:companyId`;
@@ -905,7 +905,7 @@ test.serial(
905905
.set('Accept', 'application/json');
906906

907907
const findAllConnectionsResponseRO = JSON.parse(findAllConnectionsResponse.text);
908-
t.is(findAllConnectionsResponse.status, 403);
908+
t.is(findAllConnectionsResponse.status, 401);
909909
t.is(findAllConnectionsResponseRO.message, Messages.ACCOUNT_SUSPENDED);
910910
},
911911
);

backend/test/ava-tests/saas-tests/user-e2e.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ test.serial(`${currentTest} should return user deletion result`, async (t) => {
105105
.set('Cookie', token)
106106
.set('Content-Type', 'application/json')
107107
.set('Accept', 'application/json');
108-
t.is(getUserResult.status, 404);
108+
t.is(getUserResult.status, 401);
109109
t.pass();
110110
});
111111

0 commit comments

Comments
 (0)