인증과 인가를 위한 Nestjs의 Guard
NestJS
에서는 인증 부분을 Guard를 통해 쉽게 구현할 수 있다. 우리는 현재 두 가지의 Guard를 사용하고 있다. 두 가지의 Guard의 이름을 criticalGuard
와 looseGuard
로 지었다.
CriticalGuard
- 우리의 핵심 서비스를 감싸는 Guard로써 쿠키의
accessToken
을 확인한다. - 게임 입장, 게시글 작성, 유저 API 호출 등 다양한 서비스에 사용된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// jwt-auth.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, "criticalGuard") {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromExtractors([
(request) => {
return request?.cookies?.accessToken;
},
]),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET_KEY,
});
}
async validate(payload: UserDataDto): Promise<UserDataDto> {
if (!payload.nickname) {
throw new UnauthorizedException(
"서버에 해당 유저가 존재하지 않습니다. 가입을 완료해주세요."
);
}
return payload;
}
}
criticalGuard
는 cookie에accessToken
이라는 토큰이 있는 지를 확인하고, 해당 토큰의 payload 부분을 가공하여validate
시킨다.- payload 의
nickname
부분을 확인하여nickname
이 정해지지 않았다면, 해당 유저는 회원가입을 완료하지 않은 유저이다. 우리는nickname
필드의 값 유무를 통해 앞선 케이스를 구분했다.
looseGuard
- 오직
signup
API 를 위해 만든 guard로accessToken
유무만 확인한다. - 만약 우리의 jwt로 검증이되는 accessToken이라면, 해당 유저는 통과한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// jwt-auth.strategy.ts
@Injectable()
export class JwtStrategy2 extends PassportStrategy(Strategy, "looseGuard") {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromExtractors([
(request) => {
return request?.cookies?.accessToken;
},
]),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET_KEY,
});
}
async validate(payload: UserDataDto): Promise<UserDataDto> {
return payload;
}
}
인증
로그인과 회원가입 로직을 완성했으니, 본격적인 인증 API를 만들어보자. 우리는 이를 auth
API라고 부른다. 앞에서 guard를 잘 만들어 놓았기에 별 다른 복잡한 코드가 필요하지 않았다.
Auth
1
2
3
4
5
6
@Get('auth')
@UseGuards(AuthGuard('criticalGuard'))
authorization(@Req() req: any) {
const { nickname, characterName }: UserDataDto = req.user;
return { nickname, characterName };
}
- 유저가 우리 서비스의 중요한 페이지를 들어갈 때마다, Frontend에서는 해당 API를 호출하여 접근 허가를 결정한다.
- criticalGuard를 통해 가입 유저인지를 확인하고,
nickname
과characterName
을 반환해준다.
로그인 총 과정 순서도