본 게시물은 이전에 운영하던 velog에서 작성됨 (2023년 4월 22일 작성)
프로젝트에는 카카오 로그인과 네이버 로그인 둘 다 구현했는데,
둘 다 흐름이 매우 비슷해서 네이버 로그인만 정리해도 될 것 같다.
node.js 진영에서는 인증 구현할 때 passport 라이브러리를 굉장히 많이 쓴다고 들었는데,
OAuth 방식으로 인증이 어떻게 이루어지는 지 흐름도 익힐 겸 passport 없이 했다.
한 번 훑으면 passport도 쉽게 익힐 수 있지 않을까 싶다.
네이버 로그인 API 공식 문서를 참조했다.
🐱 전체적인 흐름
- 사전에 Naver Developer에서 애플리케이션을 등록하고 클라이언트 ID와 클라이언트 secret을 발급받는다.
- 프론트엔드에서
https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id={클라이언트 아이디}&state={스테이트}&redirect_uri={Naver Developer에 등록한 redirect_uri}
URL로 사용자를 리다이렉트하여 네이버 계정으로 로그인하도록 한다. 로그인이 성공하면 accessToken 발급에 사용되는 인증코드와 state를 발급한다. - 인증코드와 스테이트를 백엔드로 보낸다.
- 백엔드에서 네이버 API에 접근할 수 있는 AccessToken를 포함한 해당 유저의 데이터를 받기위해
https://nid.naver.com/oauth2.0/token?grant_type=authorization_code&client_id={클라이언트 Id}&client_secret={클라이언트 Secret}&code={프론트엔드에게 받은 인증코드}&state={스테이트}
URL로 요청한다. - 성공적으로 데이터를 받으면 JWT Token을 발급하고 해당 토큰으로 인가 처리
🐑 코드
컨트롤러
프론트엔드로부터 code와 state를 전달받는다. code는 인증코드, state는 사이트 간 요청 위조(CSRF) 공격을 방지하기 위해 애플리케이션에서 생성한 값이다. 내가 알고 있기로는 로그인 요청하는 유저마다 state 값을 애플리케이션에서 생성해야하는 것으로 알고 있다.
서비스
프론트 엔드에서 받은 code와 state를 받는다. state 값 확인 로직의 경우 프론트엔드에서 체크하는 게 나은 방법인 것 같아서 주석처리했다.
그 다음 accessToken을 받기 위해 네이버에 요청하고, 발급받은 accessToken으로 유저 계정의 프로필 데이터를 네이버에 요청한다.
정상적으로 요청이 되었으면 유저 계정의 프로필 데이터를 받아올 수 있다. 다만 사전에 Naver Developer에 등록한 애플리케이션에서 설정해 놓은 값만 가져올 수 있다. 본 프로젝트에서는 유저의 고유 아이디 외에는 필요하지 않았다.
아무튼 유저의 고유 아이디를 반환하고, 고유 아이디에 해당하는 유저 데이터가 DB에 있는 지 확인한다. 존재하면 바로 JWT 토큰을 발급하여 로그인 처리하고, 없다면 회원 가입할 수 있도록 리다이렉션 한다.
JWT 토큰을 반환하기 전 await this.registerAuthInfo(jwtAccessToken, user.id);
이 코드는 중복 로그인을 방지하기 위해 유저 아이디와 해당 유저의 jwt 토큰을 저장한다. 참 마음에 안드는 부분인데, 이 방법 외의 로직이 떠오르지 않는다.
JWT 발급
우선 app.module에 JWTModule을 등록한다.
이후 import { JwtService } from '@nestjs/jwt';
와 같이 임포트하고 JwtService를 객체에 주입하여 사용하면 쉽게 발급할 수 있다.
AuthGuard
식별된 유저, 즉 로그인한 유저가 아니면 API 사용을 제한해야 하는데, 이는 Guard를 정의하여 처리한다.
로그인한 유저는 항상 Authorization 헤더에 JWT 토큰을 담고 요청한다. 우선 이 헤더에서 JWT 토큰만 가져온 뒤, verify 또는 decode를 하여 토큰 내부에 저장된 userId를 가져온다.
만약 verify 과정에서 에러가 난다면 변조되었거나 만료된 토큰으로 간주하고 예외처리한다. 프론트에서는 로그아웃 처리하고 로그인 창으로 리다이렉션 한다.
그 다음 redis에 저장된 JWT 토큰과 비교하는데, 중복 로그인을 확인하는 과정이다. 헤더에 담겨서 온 토큰이 redis에 저장된 토큰과 다르면 현재 하나의 계정에 두 사람이 접근하고 있다는 것으로 간주한다. 이를 해결하기 위해 현재 인가 처리 중인 유저를 로그아웃 처리하고 토큰을 브라우저 삭제한다.
이 모든 과정을 통과하면 API를 사용할 수 있게 된다.
'NestJS' 카테고리의 다른 글
[NestJS | Redis | Docker] 용도별로 Redis 인스턴스 만들기 (0) | 2023.07.30 |
---|---|
[NestJS | Redis] 데이터 캐싱하기 (Look-Aside) (0) | 2023.07.30 |
[NestJS | Redis] 세션 스토리지 만들기 (0) | 2023.07.30 |
[NestJS] DTO 만들 때에는 interface보다 class를 사용하자 (0) | 2023.07.30 |