JWT kimlik doğrulama, basit görünen ama yanlış gitmenin düzinelerce yolu olan konulardan biridir. Birçok NestJS servisinde uyguladıktan — ve PromptForge gibi AI üreticilerinin ne ürettiğini gördükten — sonra, üretimde güvenilir biçimde çalışan kalıbı paylaşıyorum.
Temel Mimari
Üretime hazır bir JWT kurulumu, tek değil iki token gerektirir:
- Access token: Kısa ömürlü (15 dakika ila 1 saat). Her API isteğiyle gönderilir. Durumsuz — doğrulamak için veritabanı araması gerekmez.
- Refresh token: Uzun ömürlü (7–30 gün). Yalnızca yeni bir access token almak için kullanılır. Güvenli şekilde saklanmalıdır (httpOnly cookie veya güvenli depolama).
Çoğu eğitim access token'da durur. Öğrenmek için bu yeterlidir; ancak üretimde kullanıcılar her saat oturumdan çıkar ve sessizce yeniden kimlik doğrulama yolu yoktur.
NestJS'te Passport ve JWT Kurulumu
// auth.module.ts
@Module({
imports: [
JwtModule.registerAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
secret: config.get('JWT_SECRET'),
signOptions: { expiresIn: '1h' },
}),
}),
PassportModule,
UsersModule,
],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
JWT Strategy
// jwt.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(config: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: config.get('JWT_SECRET'),
});
}
async validate(payload: { sub: string; email: string; type: string }) {
if (payload.type !== 'access') throw new UnauthorizedException();
return { id: payload.sub, email: payload.email };
}
}
type claim kontrolüne dikkat edin. Bu, refresh token'ın access token olarak kullanılmasını engeller — token'ları birbirinden ayırt etmezseniz ortaya çıkan ince ama gerçek bir güvenlik sorunudur.
Token Üretmek
private generateAccessToken(userId: string, email: string): string {
return this.jwtService.sign(
{ sub: userId, email, type: 'access' },
{ expiresIn: '1h' }
);
}
private generateRefreshToken(userId: string, email: string): string {
return this.jwtService.sign(
{ sub: userId, email, type: 'refresh' },
{ expiresIn: '30d' }
);
}
Refresh Endpoint
@Post('refresh')
async refresh(@Body() body: { refreshToken: string }) {
const payload = this.jwtService.verify(body.refreshToken, {
secret: this.config.get('JWT_SECRET'),
});
if (payload.type !== 'refresh') {
throw new UnauthorizedException('Invalid refresh token');
}
const user = await this.usersService.findById(payload.sub);
if (!user?.isActive) throw new UnauthorizedException();
return { token: this.generateAccessToken(user.id, user.email) };
}
Yaygın Hatalar
1. Access ve refresh token'lar için aynı secret kullanmak. Birçok uygulama için sorun değil; ancak refresh token'ları bağımsız olarak iptal etmek istiyorsanız ayrı secret'lar kullanın.
2. Refresh endpoint'inde isActive kontrolü yapmamak. Bir kullanıcı yasaklandığında ama geçerli refresh token'ı varsa, her yenilemede durumunu kontrol etmezseniz yeni access token üretmeye devam eder.
3. Refresh token'ları localStorage'da saklamak. XSS'e karşı savunmasız. Refresh token için httpOnly cookie, access token için ise bellek (veya en az sessionStorage) kullanın.
4. İstemci tarafında token süresini yönetmemek. 401 yanıtlarını yakalayın, refresh endpoint'ini çağırın ve orijinal isteği yeniden deneyin. Bunu yapmazsanız access token süresi dolduğunda kullanıcılar sessizce oturumdan çıkar.
Üretilen ve Elle Yazılan Kod
PromptForge bu tam kimlik doğrulama kurulumunu otomatik olarak üretir — refresh token akışı, JWT guard, strategy ve istemci tarafı interceptor dahil. Çoğu uygulama için üretilen kod bu durumları doğru şekilde kutunun dışında ele alır. Yalnızca gereksinimleriniz standart kalıptan ayrıldığında özelleştirmeniz gerekir.