Skip to content

Commit

Permalink
Merge pull request #313 from Quickchive/feat/#312-use-redis
Browse files Browse the repository at this point in the history
Feat/#312 use redis
  • Loading branch information
stae1102 authored Mar 29, 2024
2 parents d8a80e4 + f479e1b commit d350627
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 64 deletions.
104 changes: 100 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"cross-env": "^7.0.3",
"crypto-js": "^4.1.1",
"form-data": "^4.0.0",
"ioredis": "^5.3.2",
"joi": "^17.6.0",
"openai": "^3.3.0",
"passport": "^0.6.0",
Expand Down
10 changes: 3 additions & 7 deletions src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { CacheModule, Module } from '@nestjs/common';
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthController } from './auth.controller';
import { OAuthController } from './oauth.controller';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt/jwt.strategy';
import * as redisStore from 'cache-manager-redis-store';
import { GoogleStrategy } from './passport/google/google.strategy';
import { customJwtService } from './jwt/jwt.service';
import { TWOHOUR } from './jwt/jwt.payload';
Expand All @@ -14,6 +13,7 @@ import { OAuthUtil } from './util/oauth.util';
import { ContentsModule } from '../contents/contents.module';
import { OAuthService } from './oauth.service';
import { CategoryModule } from '../categories/category.module';
import { RedisModule } from '../infra/redis/redis.module';

const accessTokenExpiration = TWOHOUR;
export const refreshTokenExpirationInCache = 60 * 60 * 24 * 365; // 1 year
Expand All @@ -31,12 +31,8 @@ export const verifyEmailExpiration = 60 * 5;
}),
UsersModule,
ContentsModule,
CacheModule.register({
store: redisStore,
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
}),
CategoryModule,
RedisModule,
],
controllers: [AuthController, OAuthController],
providers: [
Expand Down
61 changes: 32 additions & 29 deletions src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import {
BadRequestException,
CACHE_MANAGER,
Inject,
Injectable,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import { Cache } from 'cache-manager';
import { v4 as uuidv4 } from 'uuid';

import { MailService } from '../mail/mail.service';
Expand All @@ -27,15 +24,16 @@ import { ValidateUserDto, ValidateUserOutput } from './dtos/validate-user.dto';
import { ONEYEAR, Payload } from './jwt/jwt.payload';
import { customJwtService } from './jwt/jwt.service';
import { UserRepository } from '../users/repository/user.repository';
import { RedisService } from '../infra/redis/redis.service';
import { PASSWORD_CODE_KEY, REFRESH_TOKEN_KEY } from './constants';

@Injectable()
export class AuthService {
constructor(
private readonly jwtService: customJwtService,
private readonly userRepository: UserRepository,
private readonly mailService: MailService,
@Inject(CACHE_MANAGER)
private readonly cacheManager: Cache,
private readonly redisService: RedisService,
) {}

async jwtLogin({
Expand All @@ -51,11 +49,14 @@ export class AuthService {
user.id,
);
const refreshToken = await this.jwtService.generateRefreshToken(payload);
await this.cacheManager.set(refreshToken, user.id, {
ttl: auto_login

await this.redisService.set(
`${REFRESH_TOKEN_KEY}:${user.id}`,
refreshToken,
auto_login
? refreshTokenExpirationInCache
: refreshTokenExpirationInCacheShortVersion,
});
);

return {
access_token: this.jwtService.sign(payload),
Expand All @@ -76,18 +77,15 @@ export class AuthService {
throw new BadRequestException('Refresh token is required');
}

const refreshTokenInCache: number | undefined =
await this.cacheManager.get(refreshToken);
const refreshTokenInCache: string | null = await this.redisService.get(
`${REFRESH_TOKEN_KEY}:${user.id}`,
);

if (refreshTokenInCache === undefined) {
if (!refreshTokenInCache) {
throw new NotFoundException('Refresh token not found');
}

if (refreshTokenInCache !== userId) {
throw new UnauthorizedException('Invalid refresh token');
}

await this.cacheManager.del(refreshToken);
await this.redisService.del(`${REFRESH_TOKEN_KEY}:${user.id}`);

return {};
} else {
Expand All @@ -107,19 +105,21 @@ export class AuthService {
} catch (e) {
throw new UnauthorizedException('Invalid refresh token');
}
const refreshTokenInCache = await this.cacheManager.get(refreshToken);
const user = await this.userRepository.findOneBy({ id: decoded.sub });
if (!user) {
throw new NotFoundException('User not found');
}

const refreshTokenInCache = await this.redisService.get(
`${REFRESH_TOKEN_KEY}:${user.id}`,
);

if (!refreshTokenInCache) {
throw new NotFoundException('There is no refresh token');
}

const user = await this.userRepository.findOneBy({ id: decoded.sub });
const auto_login: boolean = decoded.period === ONEYEAR;

if (!user) {
throw new NotFoundException('User not found');
}

const payload: Payload = this.jwtService.createPayload(
user.email,
auto_login,
Expand All @@ -128,12 +128,13 @@ export class AuthService {
const accessToken = this.jwtService.sign(payload);
const newRefreshToken = await this.jwtService.generateRefreshToken(payload);

await this.cacheManager.del(refreshToken);
await this.cacheManager.set(newRefreshToken, user.id, {
ttl: auto_login
await this.redisService.set(
`${REFRESH_TOKEN_KEY}:${user.id}`,
newRefreshToken,
auto_login
? refreshTokenExpirationInCache
: refreshTokenExpirationInCacheShortVersion,
});
);

return {
access_token: accessToken,
Expand All @@ -151,9 +152,11 @@ export class AuthService {
}
// Email Verification
const code: string = uuidv4();
await this.cacheManager.set(code, user.id, {
ttl: verifyEmailExpiration,
});
await this.redisService.set(
`${PASSWORD_CODE_KEY}:${code}`,
user.id.toString(),
verifyEmailExpiration,
);

// send password reset email to user using mailgun
this.mailService.sendResetPasswordEmail(user.email, user.name, code);
Expand Down
2 changes: 2 additions & 0 deletions src/auth/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const REFRESH_TOKEN_KEY = 'refresh_token';
export const PASSWORD_CODE_KEY = 'password_code';
Loading

0 comments on commit d350627

Please sign in to comment.