Nest.js에서 Goolge Oauth 적용하기

Sinf·2022년 6월 18일
4

고민의 흔적

목록 보기
29/38
post-thumbnail

Package 설치

먼저, Google Oauth를 사용하기 위한 Package를 설치한다.

yarn add @nestjs/passport passport passport-google-oauth20
yarn add -D @types/passport @types/passport-google-oauth20

여기서, 다른 구글 Oauth 관련 라이브러리들이 존재하는데,
passport-google-oauth20 인가?

일단, 평균 다운로드 수가 19만회로 다른 라이브러리보다 3배 이상 높다.

그리고, Github star 갯수를 살펴봤을 때, 가장 높은 passport-google-oauth 라이브러리가 있었는데, 해당 라이브러니는 passport-google-oauth20 의 메타 모듈이라고 되어있다.

Github star 갯수도 높고, 평균 다운로드가 높은 passport-google-oauth20 을 선택하게 되었다.

Strategy 작성하기

이제, Passport를 사용해 google 로그인을 적용하기 위해 Strategy를 작성해보자.

먼저, PassportStrategy를 상속받는다.

상속 받을 때, PassportStrategy에 파라미터로 Strategystring을 전달한다.

Strategy는 해당 Strategy가 사용할 Strategy를 전달한다. passport-google-oauth20 에 정의된 Strategy를 전달한다.

그리고 string으로는 google을 전달하는데, 이는 나중에 Guard를 사용할 때, 해당 명칭으로 사용하게 된다.

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {}

그리고, 생성자를 통해 Google Oauth를 구현하기 위한 옵션을 전달한다.

super()를 통해 부모 클래스의 생성자를 사용하는데, 상속 관계를 찾아보면 옵션은 passport-google-oauth20 에서 가져온 Strategy로 전달된다.

{
	authorizationURL?: string | undefined;
    callbackURL?: string | undefined;
    clientID: string;
    clientSecret: string;
    scope?: string | string[] | undefined;
    tokenURL?: string | undefined;
    userProfileURL?: string | undefined;
	passReqToCallback?: false | undefined;
}

옵션이 정의된 값을 보면, clientID, clientSecret을 필수로 전달해야 한다. 해당 값은 GCP에서 Google Oauth를 설정할 때 가져올 수 있다.

여기에 callbackURL로 Google 로그인 후 리다이렉션 될 URL을 전달하고, scope를 통해 가져올 정보를 정의한다.

constructor() {
  super({
    clientID: process.env.OAUTH_GOOGLE_ID,
    clientSecret: process.env.OAUTH_GOOGLE_SECRET,
    callbackURL: process.env.OAUTH_GOOGLE_REDIRECT,
    scope: ['email', 'profile'],
  });
}

현재 설정 값을 .env에 저장해서 사용했다.

@nestjs/config 의 ConfigModule을 사용하면, ConfigService를 사용할 수 있음.)

scope는 email, profile을 전달해 email과 profile을 받아온다.

이제 validate 메서드를 정의해야 한다.

validate 메서드는 Strategy를 통해 받아온 구글 유저 정보를 해당 Strategy를 사용한 API Controller에 req.user의 형태로 전달할 수 있도록 한다.

validate(
  accessToken: string,
  refreshToken: string,
  profile: Profile,
){
  const { id, name, emails } = profile;

  return {
    provider: 'google',
    providerId: id,
    name: name.givenName,
    email: emails[0].value,
  };
}

Profile 타입은 passport-google-oauth20 에 정의되어있다.

GoogleStrategy 전체 코드

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, Profile } from 'passport-google-oauth20';

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
  constructor() {
    super({
      clientID: process.env.OAUTH_GOOGLE_ID,
      clientSecret: process.env.OAUTH_GOOGLE_SECRET,
      callbackURL: process.env.OAUTH_GOOGLE_REDIRECT,
      scope: ['email', 'profile'],
    });
  }

  validate(
    accessToken: string,
    refreshToken: string,
    profile: Profile,
  ){
    const { id, name, emails } = profile;

    return {
      provider: 'google',
      providerId: id,
      name: name.givenName,
      email: emails[0].value,
    };
  }
}

Controller 작성하기

이제, Google 로그인과 관련된 Controller를 작성한다.

먼저, 2가지 API가 필요하다.

  1. Google 로그인 페이지로 리다이렉션할 API
  2. Google 로그인 후 콜백 URL로 오는 요청을 처리할 API

Redirection

import { Get, UseGuards } from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport';

@Controller('auth')
export class AuthController {
	@Get('google')
	@UseGuards(AuthGuard('google'))
	async googleAuth(): Promise<void> {
	  // redirect google login page
	}
}

리다이렉션 Controller를 간단하게 요청 URL에 맞춰 UseGuardsAuthGuard(’google’) 을 전달한다. AuthGuard에 google을 전달하면, Strategy를 작성할 때 작성한 명칭을 찾아 적용한다.

Callback URL

import { Get, UseGuards } from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport';

@Controller('auth')
export class AuthController {
  // ...

  @Get('google/callback')
  @UseGuards(AuthGuard('google'))
  async googleAuthCallback(
    @Req() req: Request,
    @Res() res: Response,
  ): Promise<void> {
    // ... 
	const { user } = req;
  }
}

Strategy에 전달된 callbackURL 값대로 Controller를 작성한다.

해당 Controller에서는 req.user를 통해 Strategy의 validate에서 리턴한 값을 사용할 수 있게 된다.

AuthController 전체 코드

import { Get, UseGuards } from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport';

@Controller('auth')
export class AuthController {
  @Get('google')
  @UseGuards(AuthGuard('google'))
  async googleAuth(): Promise<void> {
    // redirect google login page
  }

  @Get('google/callback')
  @UseGuards(AuthGuard('google'))
  async googleAuthCallback(
    @Req() req: Request,
    @Res() res: Response,
  ): Promise<void> {
    // ... 
    const { user } = req;
  }
}

Module에 등록하기

이제 작성된 Strategy와 Controller를 Module로 등록한다.

import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { GoogleStrategy } from './strategies';

@Module({
  controllers: [AuthController],
  providers: [GoogleStrategy],
})
export class AuthModule {}

그 다음은?

이제 구글 로그인을 통해 구글에서 유저 정보를 가져오게 되었다.

callback URL로 요청되는 Controller에서 가져온 정보를 저장하고, 정보를 통해 JWT을 발급해 유저 기능을 인증을 구현하면 된다.

이어서 Nest.js에서 JWT을 통해 유저 인증을 구현해보자.

profile
주니어 개발자입니다. 🚀

0개의 댓글