Passport 라이브러리에 키를 동적으로 넣어보자!

maketheworldwise·2023년 5월 14일
0


이 글의 목적?

나는 회사에서 NestJS를 기반으로 "인증" 부분을 담당하고 있다. 많은 사람들이 Passport 라이브러리에 열광(?)하다보니 나도 Passport를 사용하기로 결정했는데... 소셜 로그인을 구현하는 과정에서 발급받은 키값을 동적으로 넣어줘야하는 상황이 발생했다.

혼자서 끙끙앓으며 해결책을 찾던 와중, 나와 같은 문제를 겪고 있는 친구(?)를 발견했다. 내가 겪은 과정을 기록해보자.

Passport-*

사용법은 Passport 사이트에 다 나와있으며, 자신이 구현하고자 하는 소셜에 대한 전략(Strategy)을 검색해보면 된다. 내가 모든 전략들을 살펴본건 아니지만, 대체로 문서들이 Express 기반으로 작성되어있는 것 같다.

나는 NestJS를 사용하기 때문에 Injectable한 클래스로 만들어 처리했다. 네이버 전략만 보면 다음과 같다.

import { Strategy } from 'passport-naver';

export class NaverStrategy extends PassportStrategy(Strategy, 'naver') {
  constructor() {
    super({
      clientID: env.social.naver.clientId,
      clientSecret: env.social.naver.clientSecret,
      callbackURL: env.social.naver.callbackURL,
    });
  }

  async validate(accessToken: string, refreshToken: string, profile: any) {
    const { email, nickname, profile_image, age, birthday, id } = profile._json;
    return { email, nickname };
  }
}

동적으로 키를 넣는다?

다시 이 글의 주제로 돌아와보자. 위에서 보여준 코드에서는 ClientID, ClientSecret, CallbackURL에 직접 값을 지정하여 넣어주고 있다. 즉, 키가 고정된 채로 동작하고 있다는 의미다.

그럼 DB에 발급받은 소셜 키 정보가 있다고 가정했을 때, 어떻게 해당 키를 가져와 전략에 등록해줄 수 있을까? 🤔

열심히 구글링하면서 찾아본 결과, 가드를 하나 더 추가하여 해결할 수 있음을 깨달았다. 즉, 앞단에 가드를 하나 더 추가하여 전략을 구성하기 전에 키값을 가져와 등록하는 것이 메인 아이디어인 셈이다. (이렇게 간단한 것을 왜 난 생각해내지 못했을까... 🥲)

@UseGuards(NaverDynamicGuard, AuthGuard('naver-dynamic'))

나는 일단 네이버만 구현해보았는데, 다른 소셜도 동일하게 작업하면 될 것 같다.

@Injectable()
export class NaverDynamicGuard implements CanActivate {
  constructor(private readonly service: UserSocialKeysService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const query = request.query;
    const userId = query.userId;

    if (!userId) {
      throw GlobalException.create('BAD_REQUEST');
    }

    const isNaverLogined = !!query.code;
    if (isNaverLogined) {
      return true;
    }

    const { clientId, clientSecret, callbackURL } =
      await this.service.findByUserIdAndProvider(userId, ProviderType.NAVER);

    new NaverDynamicStrategy(clientId, clientSecret, callbackURL);
    return true;
  }
}

정말 간단하게 테스트를 위해 구현하다보니 완벽하지는 않지만, 자세한 내용을 확인해보고 싶다면 내가 직접 구현한 소셜 로그인 테스트 프로젝트를 참고하면 된다.


참고로 내가 구성한 방법은 일종의 꼼수다. 내가 찾은 내용에 따르면, Passport는 글로벌하게 구성하는게 일반적이며, 각 요청별로 전략들이 생성되는 것을 기반으로 디자인되지 않았다고 한다. 즉, 해당 라이브러리의 올바른 사용법이 아니라는 것이다...

이 글의 레퍼런스

profile
세상을 현명하게 이끌어갈 나의 성장 일기 📓

0개의 댓글