This commit is contained in:
2025-12-09 23:40:44 +03:30
parent 07f3006aef
commit 33b250287c
14 changed files with 386 additions and 1 deletions
+2 -1
View File
@@ -65,7 +65,8 @@ export class AuthGuard implements CanActivate {
return type === 'Bearer' ? token : undefined;
}
private extractSlugFromHeader(request: Request): string | undefined {
const slug = request.headers['X-Slug'] as string;
// Fastify normalizes headers to lowercase, so check both cases
const slug = (request.headers['x-slug'] || request.headers['X-Slug']) as string;
return slug;
}
}
@@ -0,0 +1,94 @@
import { CanActivate, ExecutionContext, Injectable, Inject, Logger } from '@nestjs/common';
import { Request } from 'express';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { ITokenPayload } from '../interfaces/IToken-payload';
export interface UserOptionalAuthRequest extends Request {
userId?: string;
restId?: string;
slug?: string;
}
@Injectable()
export class OptionalAuthGuard implements CanActivate {
private readonly logger = new Logger(OptionalAuthGuard.name);
constructor(
@Inject(JwtService)
private readonly jwtService: JwtService,
@Inject(ConfigService)
private readonly configService: ConfigService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<UserOptionalAuthRequest>();
const token = this.extractTokenFromHeader(request);
const slug = this.extractSlugFromHeader(request);
// If no token is provided, allow the request without authentication
if (!slug) {
console.log('No slug provided');
this.logger.warn('No slug provided');
return false;
}
if (!token) {
request['slug'] = slug;
return true;
}
// Try to verify the token if it exists
try {
const secret = this.configService.getOrThrow<string>('JWT_SECRET');
const payload = await this.jwtService.verifyAsync<ITokenPayload>(token, {
secret,
});
// Validate payload structure
if (!payload.userId || !payload.restId) {
this.logger.warn('Invalid token payload structure', payload);
// Allow request but don't set user info
request['slug'] = slug;
return true;
}
// Validate slug if provided
if (slug && payload.slug !== slug) {
this.logger.warn('Token slug does not match header slug', {
tokenSlug: payload.slug,
headerSlug: slug,
});
// Allow request but don't set user info
request['slug'] = slug;
return true;
}
// Token is valid, set user info
request['userId'] = payload.userId;
request['restId'] = payload.restId;
request['slug'] = slug;
return true;
} catch (err) {
// Token is invalid or expired, but allow the request anyway
this.logger.debug('Token verification failed in OptionalAuthGuard', {
error: err instanceof Error ? err.message : 'Unknown error',
});
// Set restId from slug
request['slug'] = slug;
return true;
}
}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
private extractSlugFromHeader(request: Request): string | undefined {
// Fastify normalizes headers to lowercase, so check both cases
const slug = (request.headers['x-slug'] || request.headers['X-Slug']) as string;
return slug;
}
}