인증 방식에는 크게 쿠키-세션 방식과 jwt 방식이 있는데, 여기서는 jwt 방식을 사용했다. 그리고 NestJS의 Passport 모듈(인증을 위한 미들웨어)를 활용하여 인증을 구현했다.
npm install @nestjs/jwt @nestjs/passport passport passport-jwt
먼저 auth.module.ts에 jwt 모듈을 등록해준다. 이때 jwt에서 seceretKey로 사용할 값과 만료 시간등의 정보를 지정해준다.
JwtModule.register({
secret: process.env.JWT_SECRET || jwtConfig.secret,
signOptions: {
expiresIn: jwtConfig.expiresIn,
}
})
이 부분을 imports의 리스트에 넣어준다.
starategy 설정을 위해 PassportStrategy
클래스를 확장하는 클래스를 생성한다. 이때 super를 통해 PassportStrategy
클래스를 설정해줘야 하며 validate 메서드를 구현해야 한다.
import { Strategy, ExtractJwt } from 'passport-jwt';
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET,
});
}
async validate(payload: any) {
// 데이터베이스와 연동하여 user가 있는지 확인
}
}
jwt 모듈과 마찬가지로 AuthModule에 등록을 해야 한다. 이때 기본 strategy는 jwt
를 사용하기 때문에 jwt
로 설정한다.
PassportModule.register({ defaultStrategy: 'jwt'})
이 부분을 imports 리스트에 추가해준다.
그리고 providers 리스트에 아까 생성한 JwtStrategy
클래스를 추가해주고 exports 리스트에 PassportMoudule
을 추가하여 authModule을 import하는 다른 모듈에서 PassportMoudule
을 사용할 수 있도록 해준다.
providers: [AuthService, UserRepository, JwtStrategy],
exports: [PassportModule]
AuthGuard를 사용하여 인증을 요구하도록 설정한다. 이때 Board controller 전체에 인증을 요구하도록 설정하기 위해 controller를 선언할 때 UseGuards() 데코레이터를 사용한다.
import { AuthGuard } from '@nestjs/passport'; // 이 부분 추가
@Controller('board')
@UseGuards(AuthGuard())
export class BoardsController {
// 기존 controller 내용
}
이때 AuthGuard('jwt')를 통해 명시적으로 jwt의 strategry를 사용한다고 설정할 수 있지만 현재 기본 strategy를 jwt로 설정했기 때문에 생략했다.
그리고 boardModule에서 authModule을 사용했으니, board.module.ts에 authModule을 import 해준다.
imports:[AuthModule],
이렇게 하고 나면 이제 jwt를 활용한 인증을 사용할 수 있다. HTTP 요청의 Authroization 헤더에 "Bearer " + 토큰
의 형태로 전송을 하게 되면 이 토큰의 값을 통해 인증을 진행하게 된다.
이제 이 토큰의 값을 클라이언트에 제공하기 위해 로그인 부분에 accessToken을 생성하여 반환하는 부분을 추가한다.
import { JwtService } from '@nestjs/jwt'; // 이 부분 추가
@Injectable()
export class AuthService {
constructor(
private UserRepository: UserRepository,
private jwtService: JwtService) {} // 이부분 추가
// 회원가입
// 로그인
async singin(authCredentialDto: AuthCredentialDto): Promise<{accessToken: string}> {
const { username, password } = authCredentialDto;
if (// 유저가 존재하고 비밀번호가 일치하면) {
const payload = { username };
const accessToken = await this.jwtService.sign(payload);
return { accessToken };
} else {
throw new UnauthorizedException('login failed');
}
}
위와 같이 jwtService를 선언하고 sign 메서드를 통해 원하는 정보를 담은 payload의 값을 이용하여 accessToken을 생성하고 클라이언트에게 반환한다. 클라이언트는 이렇게 받은 accessToken을 인증이 필요한 경우 요청과 함께 위에서 설명한 것처럼 전송하면 된다.