들어가기
JWT를 이용해서 token을 발급하는 것을
module로 만들어서 사용해 본다.
또한, middleware로 만들어서 사용해본다.
module은 두가지로 구분된다. UserModule같은 static모듈,
ConfingModule같은 Dynamic모듈로
https://docs.nestjs.com/modules#dynamic-modules
jwt.io
export const CONFIG_OPTIONS = 'CONFIG_OPTIONS';
CONFIG_OPTIONS라고 단어를 하나 박아놓는다.
export interface JwtModuleOptions {
privateKey: string;
}
JwtModuleOptions라는 interface를 만들어 privateKey를 string으로 type화 한다.
import { DynamicModule, Global, Module } from '@nestjs/common';
import { CONFIG_OPTIONS } from './jwt.constants';
import { JwtModuleOptions } from './jwt.interfaces';
import { JwtService } from './jwt.service';
@Module({})
@Global() ///Global로 지정하면 다른 module에서 사용할 떄, imports를 하지 않아도 된다
///그래서 이왕이면, Global()로 만든다.
export class JwtModule {
static forRoot(options: JwtModuleOptions): DynamicModule {
///Dynamic모듈을 만들기 위하 방법, options의 type은
///jwt.interfaces.ts의 JwtModuleOptions를 type으로 한다.
///options부분은 .env.dev의 PRIVATE_KEY부분인데, 이거 사용
///setting는 app.module.ts에서 한다.
return {
module: JwtModule,
providers: [
{
provide: CONFIG_OPTIONS,
useValue: options, =>JwtModuleOptions안의 PRIVATE_KEY 값임,
},
JwtService,
],
///CONFIG_OPTIONS라는 key로 options(PRIVATE_KEY)를
///JwtService에서 사용할 수 있게끔 한다.
///JwtService를 보면, 이해가 더 잘됨.
exports: [JwtService],
///JwtService를 다른 module에서 사용할 수 있게, exports 해 놓는다.
};
}
}
import { Inject, Injectable } from '@nestjs/common';
import { CONFIG_OPTIONS } from './jwt.constants';
import { JwtModuleOptions } from './jwt.interfaces';
import * as jwt from 'jsonwebtoken'; ///jsonwebtoken사용을 위한 import
@Injectable() ///service.ts는 거의 Injectable() type이다.
export class JwtService {
constructor(
@Inject(CONFIG_OPTIONS) private readonly options: JwtModuleOptions,
) {}
///constructor에 CONFIG_OPTIONS이라는 key를 넣는다.
///options: JwtModuleOptions 를 적어주므로써, PRIVATE_KEY에 접근할 수 있게한다.
sign(payload: object): string {
return jwt.sign(payload, this.options.privateKey);
}
///token을 만들어 주는 함수, 다른 모듈에서 JwtService.sign으로 사용가능
verify(token: string) {
return jwt.verify(token, this.options.privateKey);
}
///token을 확인해 주는 함수, 다른 모듈에서 JwtService.verify로 사용가능
}
.env.dev의 PRIVATE_KEY가 어떻게 jwt모듈로 넘어가는지를 잘 확인한다.
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: process.env.NODE_ENV === 'dev' ? '.env.dev' : '.env.test',
ignoreEnvFile: process.env.NODE_ENV === 'prod',
validationSchema: Joi.object({
NODE_ENV: Joi.string().valid('dev', 'prod').required(),
DB_HOST: Joi.string().required(),
DB_PORT: Joi.string().required(),
DB_USERNAME: Joi.string().required(),
DB_PASSWORD: Joi.string().required(),
DB_NAME: Joi.string().required(),
PRIVATE_KEY: Joi.string().required(),
}),
}),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DB_HOST,
port: +process.env.DB_PORT,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
synchronize: true,
logging: true,
entities: [User],
}),
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true,
context: ({ req }) => ({ user: req['user'] }),
}),
///////////////////////////////
JwtModule.forRoot({
privateKey: process.env.PRIVATE_KEY,
}),
///위와같이 지정해 주면, interface.ts의 JwtModuleOption에 들어감.
//Jwt모듈 안에서 PRIVATE_KEY를 사용가능하게 됨.
////////////////////////////
RestaurantModule,
CommonModule,
UsersModule,
AuthModule,
],
@Injectable()
export class UserService {
constructor(
@InjectRepository(User) private readonly users: Repository<User>,
private readonly config: ConfigService,
private readonly jwtService: JwtService,
) {}
///private readonly jwtSetvice: JwtSservice를 적어줌으로써,
///jwt.service.ts의 sign과 verify를 사용할 수 있게 된다.
///login으로 넘어갈것
async createAccount({
email,
password,
role,
}: CreateAccountInput): Promise<{ ok: boolean; error?: string }> {
try {
const exists = await this.users.findOneBy({ email });
if (exists) {
return { ok: false, error: 'There is a user with that email already' };
}
await this.users.save(this.users.create({ email, password, role }));
return { ok: true };
} catch (e) {
return { ok: false, error: 'Could not create account' };
}
}
async login({
email,
password,
}: LoginInput): Promise<{ ok: boolean; error?: string; token?: string }> {
try {
const user = await this.users.findOneBy({ email });
if (!user) {
return {
ok: false,
error: 'User not found',
};
}
const passwordCorrect = await user.checkPassword(password);
if (!passwordCorrect) {
return {
ok: false,
error: 'Wrong password',
};
}
// const token = jwt.sign({ id: user.id }, this.config.get('PRIVATE_KEY'));
const token = this.jwtService.sign({ id: user.id });
///jwt.service.ts에서 만든 sign을 사용함.{id:user.id}를 넘겨서 token만듬.
return {
ok: true,
token,
};
} catch (error) {
return {
ok: false,
error,
};
}