@@ -0,0 +1,22 @@
|
||||
import { Injectable, Logger, ExecutionContext } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
@Injectable()
|
||||
export class JwtGuard extends AuthGuard('jwt') {
|
||||
private readonly logger = new Logger('JwtGuard');
|
||||
|
||||
canActivate(context: ExecutionContext) {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
this.logger.debug(`JwtGuard invoked. cookies=${JSON.stringify(req.cookies)} authHeader=${req.headers?.authorization}`);
|
||||
return super.canActivate(context);
|
||||
}
|
||||
|
||||
handleRequest(err: any, user: any, info: any) {
|
||||
if (err || !user) {
|
||||
this.logger.debug(`JwtGuard handleRequest failed. err=${err} user=${!!user} info=${JSON.stringify(info)}`);
|
||||
} else {
|
||||
this.logger.debug(`JwtGuard handleRequest success user=${JSON.stringify(user)}`);
|
||||
}
|
||||
return super.handleRequest(err, user, info);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Injectable, Logger, ExecutionContext } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
@Injectable()
|
||||
export class RefreshGuard extends AuthGuard('refresh') {
|
||||
private readonly logger = new Logger('RefreshGuard');
|
||||
|
||||
canActivate(context: ExecutionContext) {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
this.logger.debug(`RefreshGuard invoked. cookies=${JSON.stringify(req.cookies)} body=${JSON.stringify(req.body)}`);
|
||||
return super.canActivate(context);
|
||||
}
|
||||
|
||||
handleRequest(err: any, user: any, info: any) {
|
||||
if (err || !user) {
|
||||
this.logger.debug(`RefreshGuard handleRequest failed. err=${err} user=${!!user} info=${JSON.stringify(info)}`);
|
||||
} else {
|
||||
this.logger.debug(`RefreshGuard handleRequest success userId=${user.id} authInfo=${JSON.stringify(info)}`);
|
||||
}
|
||||
return super.handleRequest(err, user, info);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Strategy as JwtStrategyBase, ExtractJwt } from 'passport-jwt';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
function cookieExtractor(req: any): string | null {
|
||||
if (!req) return null;
|
||||
if (req.cookies && req.cookies.raylab_jwt) return req.cookies.raylab_jwt as string;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(JwtStrategyBase, 'jwt') {
|
||||
private readonly logger = new Logger('JwtStrategy');
|
||||
|
||||
constructor(private config: ConfigService) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromExtractors([cookieExtractor, ExtractJwt.fromAuthHeaderAsBearerToken()]),
|
||||
secretOrKey: config.get<string>('RAYLAB_JWT_SECRET') || process.env.RAYLAB_JWT_SECRET || 'raylab-secret',
|
||||
algorithms: ['HS256'],
|
||||
});
|
||||
this.logger.debug('JwtStrategy initialized');
|
||||
}
|
||||
|
||||
async validate(payload: any) {
|
||||
this.logger.debug(`JwtStrategy.validate payload=${JSON.stringify(payload)}`);
|
||||
return payload; // attached to req.user
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Strategy } from 'passport-strategy';
|
||||
import { InMemoryRefreshStore } from '../../../modules/auth/refresh/inmemory-refresh.store';
|
||||
import { IUser } from '../../../modules/identity/domain/repositories/user.interface';
|
||||
import { Inject } from '@nestjs/common';
|
||||
|
||||
// Minimal Passport Strategy for refresh tokens (non-JWT random tokens)
|
||||
class RefreshTokenStrategy extends Strategy {
|
||||
name = 'refresh';
|
||||
authenticate(req: any) {
|
||||
// This will be overridden in PassportStrategy wrapper
|
||||
this.error(new Error('Not implemented'));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class RefreshStrategy extends PassportStrategy(RefreshTokenStrategy, 'refresh') {
|
||||
private readonly logger = new Logger('RefreshStrategy');
|
||||
|
||||
constructor(private refreshStore: InMemoryRefreshStore, @Inject(IUser) private userRepository: IUser) {
|
||||
super();
|
||||
this.logger.debug('RefreshStrategy initialized');
|
||||
}
|
||||
|
||||
async authenticate(req: any, options?: any) {
|
||||
const cookies = req.cookies || null;
|
||||
const token = (cookies && cookies.raylab_refresh) || (req.body && req.body.refreshToken);
|
||||
this.logger.debug(`RefreshStrategy.authenticate cookies=${JSON.stringify(cookies)} tokenExtracted=${!!token}`);
|
||||
|
||||
if (!token) {
|
||||
this.logger.debug('RefreshStrategy: no refresh token provided');
|
||||
return this.fail('Missing refresh token', 401);
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await this.refreshStore.get(token);
|
||||
if (!data) {
|
||||
this.logger.debug('RefreshStrategy: refresh token not found/expired');
|
||||
return this.fail('Invalid refresh token', 401);
|
||||
}
|
||||
|
||||
const user = await this.userRepository.getById(data.userId);
|
||||
if (!user) {
|
||||
this.logger.debug('RefreshStrategy: user not found for refresh token');
|
||||
return this.fail('User not found', 401);
|
||||
}
|
||||
|
||||
// success - attach user + token info
|
||||
const info = { refreshToken: token, userId: data.userId };
|
||||
this.logger.debug(`RefreshStrategy: validated refresh token for userId=${data.userId}`);
|
||||
return this.success(user, info);
|
||||
} catch (e) {
|
||||
this.logger.debug(`RefreshStrategy error: ${(e as Error).message}`);
|
||||
return this.error(e as Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user