Skip to content

Commit e2da98f

Browse files
authored
fix(auth): handle auth exceptions and fix internal server error 500 (#31)
1 parent 88e0e39 commit e2da98f

8 files changed

Lines changed: 48 additions & 19 deletions

File tree

src/modules/auth/controller/auth.controller.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export class AuthController {
6969
@UseGuards(BearerAuthGuard)
7070
@PostLogoutSwagger()
7171
async logout(@Res({ passthrough: true }) res: FastifyReply, @Req() req: FastifyRequest) {
72-
const session = req.cookies['refresh'];
72+
const session = req.cookies?.['refresh'];
7373
const response = await this.facade.signOut(session);
7474

7575
res.clearCookie('refresh', { path: '/' });
@@ -83,7 +83,7 @@ export class AuthController {
8383
@HttpCode(200)
8484
async refresh(@Res({ passthrough: true }) res: FastifyReply, @Req() req: FastifyRequest) {
8585
const meta = getDeviceMeta(req);
86-
const session = req.cookies['refresh'];
86+
const session = req.cookies?.['refresh'];
8787
const { tokens, ...response } = await this.facade.refresh(session, meta);
8888

8989
res.setCookie('refresh', tokens.refresh, {

src/modules/auth/services/auth.service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,9 @@ export class AuthService {
151151
};
152152

153153
public signIn = async (dto: SignInDto, meta: DeviceMetadata) => {
154-
const { user, security } = await this.findUserCommand.execute({ email: dto.email });
154+
const entities = await this.findUserCommand.execute({ email: dto.email });
155155

156-
if (!user || !security) {
156+
if (!entities?.user || !entities?.security) {
157157
throw new BaseException(
158158
{
159159
code: 'INVALID_CREDENTIALS',
@@ -163,6 +163,7 @@ export class AuthService {
163163
);
164164
}
165165

166+
const { security, user } = entities;
166167
const isPasswordValid = await argon.verify(security.passwordHash, dto.password);
167168

168169
if (!isPasswordValid) {

src/modules/auth/services/token.service.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
22
import { JwtService } from '@nestjs/jwt';
33
import { ConfigService } from '@nestjs/config';
44
import type { JwtPayload } from '@shared/types';
5+
import type { User } from '@core/modules/user';
56

67
@Injectable()
78
export class TokenService {
@@ -10,16 +11,16 @@ export class TokenService {
1011
private readonly cfg: ConfigService,
1112
) {}
1213

13-
async generateTokens(user: any, sessionId: string) {
14+
async generateTokens(user: User, sessionId: string) {
1415
const domain = this.cfg.get('DOMAIN');
16+
const audConstraint = this.cfg.getOrThrow('JWT_AUDIENCE');
1517

1618
const payload = {
1719
jti: sessionId,
1820
sub: user.id,
1921
email: user.email,
2022
iss: btoa(domain),
21-
aud: btoa(this.cfg.getOrThrow('JWT_AUDIENCE')),
22-
role: user.role,
23+
aud: btoa(audConstraint),
2324
};
2425

2526
const [access, refresh] = await Promise.all([
@@ -38,10 +39,10 @@ export class TokenService {
3839

3940
async validateToken(token: string, type: 'access' | 'refresh'): Promise<JwtPayload> {
4041
try {
41-
const secret =
42-
type === 'access'
43-
? this.cfg.get('JWT_ACCESS_SECRET')
44-
: this.cfg.get('JWT_REFRESH_SECRET');
42+
const accessSecret = this.cfg.get('JWT_ACCESS_SECRET');
43+
const refreshSecret = this.cfg.get('JWT_REFRESH_SECRET');
44+
45+
const secret = type === 'access' ? accessSecret : refreshSecret;
4546

4647
return this.jwtService.verifyAsync(token, { secret });
4748
} catch (e) {

src/modules/auth/strategies/bearer.strategy.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ import { Strategy, ExtractJwt } from 'passport-jwt';
66

77
@Injectable()
88
export class BearerStrategy extends PassportStrategy(Strategy, 'bearer') {
9-
constructor(configService: ConfigService) {
9+
constructor(cfg: ConfigService) {
10+
const audConstraint = cfg.getOrThrow('JWT_AUDIENCE');
11+
const audience = btoa(audConstraint);
12+
1013
super({
1114
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
12-
secretOrKey: configService.get<string>('JWT_ACCESS_SECRET'),
13-
issuer: configService.get<string>('JWT_ISSUER'),
14-
audience: configService.get<string>('JWT_AUDIENCE'),
15+
secretOrKey: cfg.get<string>('JWT_ACCESS_SECRET'),
16+
issuer: cfg.get<string>('JWT_ISSUER'),
17+
audience,
1518
});
1619
}
1720

src/modules/auth/strategies/cookie.strategy.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export class CookieStrategy extends PassportStrategy(Strategy, 'cookie') {
1212
super({
1313
jwtFromRequest: ExtractJwt.fromExtractors([
1414
(request: FastifyRequest) => {
15-
return request?.cookies?.['refresh'];
15+
const token = request?.cookies?.['refresh'];
16+
return token;
1617
},
1718
]),
1819
secretOrKey: configService.get<string>('JWT_REFRESH_SECRET'),
@@ -21,6 +22,7 @@ export class CookieStrategy extends PassportStrategy(Strategy, 'cookie') {
2122
}
2223

2324
validate(_req: FastifyRequest, payload: JwtPayload) {
25+
console.log(_req, payload);
2426
if (!payload || !payload.jti) {
2527
throw new BaseException(
2628
{

src/modules/user/entities/user.entity.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export const users = baseSchema.table('users', {
1010
firstName: varchar('first_name', { length: 50 }).notNull(),
1111
lastName: varchar('last_name', { length: 50 }).notNull(),
1212
middleName: varchar('middle_name', { length: 50 }),
13-
1413
email: varchar('email', { length: 255 }).notNull().unique(),
1514
bio: text('bio'),
1615
avatarUrl: varchar('avatar_url', { length: 512 }),

src/modules/user/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { UserModule } from './user.module';
22
export { UserRepository } from './repository/user.repository';
33
export { CreateUserCommand, FindOneUserCommand, UpdatePassUserCommand } from './commands';
4+
export { User } from './entities/user.domain';

src/shared/guards/cookie.guard.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
1-
import { Injectable } from '@nestjs/common';
1+
import { HttpStatus, Injectable } from '@nestjs/common';
22
import { AuthGuard } from '@nestjs/passport';
3+
import { BaseException } from '@shared/error';
4+
import type { JwtPayload } from '@shared/types';
35

46
@Injectable()
5-
export class CookieAuthGuard extends AuthGuard('cookie') {}
7+
export class CookieAuthGuard extends AuthGuard('cookie') {
8+
handleRequest<TUser = JwtPayload>(err: unknown, user: TUser, info: any): TUser {
9+
if (err || !user) {
10+
throw new BaseException(
11+
{
12+
code: 'INVALID_REFRESH_TOKEN',
13+
message: 'Refresh токен невалиден или отсутствует',
14+
details: [
15+
{
16+
target: 'auth',
17+
reason: info?.message || 'Token verification failed',
18+
},
19+
],
20+
},
21+
HttpStatus.UNAUTHORIZED,
22+
);
23+
}
24+
25+
return user;
26+
}
27+
}

0 commit comments

Comments
 (0)