[NestJS] OAuth 2.0을 활용한 로그인 구현

허창원·2024년 4월 22일
0
post-custom-banner

Google 소셜 로그인 구현하기

NestJS 프로젝트에서 소셜 로그인 기능을 추가하는 방법을 안내합니다. 이 과정을 통해 사용자는 Google 계정을 이용하여 소셜 로그인을 할 수 있게 됩니다.

필요한 패키지 설치

먼저 passport와 Google OAuth 2.0을 사용하기 위한 passport-google-oauth20 패키지를 설치합니다. 타입스크립트 사용자의 경우, 해당 @type 패키지도 설치해야합니다.

npm install passport-google-oauth20
npm install --save @types/passport-google-oauth20

Google Cloud Platform에서 프로젝트 생성

Google Cloud Platform(GCP)에서 OAuth 클라이언트 ID를 생성하기 위해 프로젝트를 만들어야 합니다. 다음 단계를 따라 프로젝트를 생성하세요

  1. Google Cloud Console에 접속하여 Google 계정을 로그인합니다.
  2. 우측 상단에 '새 프로젝트' 버튼을 클릭하여 프로젝트를 생성합니다.

해당 사이트로 접속하여 구글 계정으로 로그인을 실시하고 우측 상단의 새 프로젝트를 생성해줍니다.

GCP Console에서 OAuth를 이용하기 위해서 프로젝트를 생성합니다.

사용자 인증 정보 설정

OAuth 클라이언트 ID와 Secret을 발급받기 위해 다음과 같이 사용자 인증 정보를 설정합니다.
1. API 및 서비스 > 사용자 인증 정보로 이동합니다.
2. 사용자 인증 정보 만들기 버튼을 클릭한 후 OAuth 클라이언트 ID를 선택합니다.


  1. 클라이언트 ID 생성 시, 승인된 자바스크립트 원본에 프론트엔드 웹페이지 주소를, 승인된 리디렉션 URI에 로그인 성공 후 처리할 API 서버 주소를 입력합니다.

Google Passport Strategy

위 단계를 완료하면, Google Cloud Platform에서 발급 받은 클라이언트 ID와 Secret을 이용하여 Passport Google Strategy를 설정하고, 소셜 로그인 기능을 구현할 수 있습니다.

import { Injectable } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { PassportStrategy } from '@nestjs/passport'
import { Profile, Strategy, VerifyCallback } from 'passport-google-oauth20'
import { User } from 'src/user/entities/user.entity'
import { UserService } from 'src/user/services/user.service'

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
  constructor(
    private configService: ConfigService,
    private userService: UserService,
  ) {
    super({
      clientID: configService.get('GOOGLE_CLIENT_ID'),
      clientSecret: configService.get('GOOGLE_CLIENT_SECRET'),
      callbackURL: configService.get('GOOGLE_CALLBACK_URL'),
      scope: ['email', 'profile'],
    })
  }

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

    const providerId = id
    const email = emails[0].value

    const user: User = await this.userService.findByEmailOrSave(
      email,
      name.familyName + name.givenName,
      providerId,
    )
    done(null, user)
  }
}
  • 생성자: GCP에서 발급받은 client ID와 client Secret을 입력하고, callbackURL에 구글 로그인 성공시 처리할 URL을 입력해줍니다. 해당 URL은 아까 승인된 URI 리다이렉션 목록에 있어야합니다. scope에는 로그인 성공 시 구글 측으로부터 받을 데이터를 입력해줍니다.
  • validate에서는 accessToken, refreshToken, profile을 성공 시 구글측으로부터 받으며, 해당 Strategy Guard를 사용할 때, 인가 성공 시 넘겨줄 데이터들을 작성합니다.

Strategy는 프로바이더이므로 AuthModule에 등록해야합니다.

import { Module } from '@nestjs/common'
import { AuthService } from './services/auth.service'
import { AuthController } from './controllers/auth.controller'
import { JwtModule } from '@nestjs/jwt'
import { PassportModule } from '@nestjs/passport'
import { JwtAccessTokenStrategy } from './strategy/jwt-access.strategy'
import { JwtRefreshTokenStrategy } from './strategy/jwt-refresh.strategy'
import { LocalStrategy } from './strategy/local.strategy'
import { UserModule } from 'src/user/user.module'
import { GoogleStrategy } from './strategy/google.strategy'
import { TypeOrmExModule } from 'src/common/decorator/typeorm-ex.module'
import { UserRepository } from 'src/user/user.repository'

@Module({
  imports: [
    UserModule,
    PassportModule.register({ defaultStrategy: 'jwt', session: false }), // 세션 쿠키를 사용하지 않는다.
    TypeOrmExModule.forCustomRepository([UserRepository]),
    JwtModule.register({}),
  ],
  controllers: [AuthController],
  providers: [
    AuthService,
    LocalStrategy,
    GoogleStrategy,
    JwtAccessTokenStrategy,
    JwtRefreshTokenStrategy,
  ],
})
export class AuthModule {}

Google Auth Gaurd

이 가드는 컨트롤러에서 Google 계정으로 로그인해야 하는 경로를 보호하는 데 사용될 수 있습니다. Google 자격증명을 사용하여 로그인할 수 있는 경로가 있을 때, 이 가드는 인증 과정이 설정한 전략에 따라 처리되도록 합니다.

import { Injectable } from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport'

@Injectable()
export class GoogleAuthGuard extends AuthGuard('google') {}

Google 로그인 API

import { Controller, Get, Req } from '@nestjs/common'
import { AuthService } from '../services/auth.service'
import { UseGuards } from '@nestjs/common'
import { CurrentUser } from '../../common/decorator/current-user.decorator'
import { User } from 'src/user/entities/user.entity'
import { GoogleAuthGuard } from '../guard/google.guard'

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  // 생략
  
  @Get('/google/login')
  @UseGuards(GoogleAuthGuard)
  async googleAuth(@Req() req) {
    console.log('GET google/login')
  }

  @Get('/google/callback')
  @UseGuards(GoogleAuthGuard)
  async googleAuthRedirect(@CurrentUser() user: User): Promise<User> {
    return user
  }

'/google/login'은 로그인할 경로이고, '/google/callback'은 로그인 후 다시 해당 페이지로 연결해주는 경로입니다.

테스트 하기

터미널에서 npm run start:dev로 서버를 실행합니다. 서버가 실행되었으면 http://localhost:3000/auth/google/login에 접속합니다.

데이터베이스에 유저가 잘 생성된것을 확인할 수 있습니다.

정리

위 과정을 통해 구현한 NestJS의 Google OAuth 로그인 기능은 사용자가 Google 계정으로 간편하게 로그인할 수 있게 해줍니다. 모듈화된 구조 덕분에 다른 소셜 로그인 기능도 유사한 방식으로 쉽게 추가할 수 있습니다.

이 글이 NestJS를 사용하여 Google 로그인 기능을 구현하는 데 도움이 되길 바랍니다. 필요한 모든 설정과 코드가 포함되어 있으며, 추가적인 구현에 필요한 서비스 로직은 프로젝트의 요구사항에 따라 자유롭게 추가하시면 됩니다.

post-custom-banner

0개의 댓글