Nest.js 소셜 로그인 - 구글 (1)

jegw·2023년 9월 13일
0

TIL

목록 보기
75/77
post-custom-banner

nest.js 프로젝트를 만들면서 소셜 로그인을 구현해보았다.
소셜 로그인은 이미 가입하고 있는 소셜 미디어 계정(예: 페이스북, 구글, 트위터 등)을 이용해 로그인하는 방식이다.

  • 사용자가 구글 계정으로 로그인할때
    1. 구글 이메일로 가입된 계정이 있다면 로그인
    2. 없다면 회원가입 후 로그인

장점

  • 별도의 회원가입 과정 없이 로그인 할 수 있으므로 서비스 이용 장벽을 낮춘다.

  • 웹사이트에서 아이디와 비밀번호를 관리하지 않고 대형 소셜 미디어 회사들의 보안 시스템에 의존할 수 있어 안전하다.

구현하기

0. 패키지 설치

npm i @nestjs/passport passport-google-oauth20 @nestjs/passport

1. OAuth 웹 클라이언트 ID 만들기

https://console.cloud.google.com/
먼저 구글 클라우드 플랫폼에서 프로젝트를 만들어서 클라이언트ID를 발급받아야 한다.
구글링을 하면 자세히 설명한 글이 많으므로 여기선 생략하겠다.

발급이 끝나면 이런 화면을 볼 수 있다. 우리는 세가지를 사용하게 된다.

  • 클라이언트 ID
  • 클라이언트 보안 비밀번호
  • 승인된 리디렉션 URI

ID와 비밀번호는 .env에 환경변수로 설정하여 사용한다.

GOOGLE_CLIENT_ID = <발급한 클라이언트 ID>
GOOGLE_CLIENT_SECRET = <발급한 클라이언트 비밀번호>

2. auth.controller

import { Controller, Post, Body, Res, Delete, Get, UseGuards, Req } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

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

// 'google 로그인'버튼 클릭시 호출
  @Get('google/login') // 구글 로그인으로 이동하는 라우터 메서드
  @UseGuards(AuthGuard('google'))  // 여기에서 가드로 가고 googleStrategy에서 validate호출
  async googleAuth(@Req() req) {
    console.log('GET google/login - googleAuth 실행');
  }

  @Get('oauth2/redirect/google')
  @UseGuards(AuthGuard('google'))
  async googleAuthRedirect(@Req() req, @Res() res) {
    console.log('GET oauth2/redirect/google - googleAuthRedirect 실행');

    const { user } = req;
    return res.send(user);  // 화면에 표시.
  }
}

3. strategy

OAuth에서 "strategy"는 인증 프로세스를 처리하는 방식을 설명하는 용어이다.
나는 auth폴더에서 인증을 처리하고 있기에 auth아래에 strategies폴더를 만들었다.

google.stratey.ts

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

export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
  constructor() {
    super({
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: '/auth/oauth2/redirect/google', // 이 부분은 구글 콘솔에서 설정한대로. 승인된 리디렉션 URI
      scope: ['email', 'profile'],
    });
  }

  async validate(accessToken: string, refreshToken: string, profile: any, done: VerifyCallback) {
    try {
      const { name, emails, photos } = profile;
      console.log('🚀 🔶 GoogleStrategy 🔶 validate 🔶 profile:', profile);
      const user = {
        email: emails[0].value,
        firstName: name.familyName,
        lastName: name.givenName,
        photo: photos[0].value,
      };
      console.log('🚀 🔶 GoogleStrategy 🔶 validate 🔶 user:', user);
      done(null, user);
    } catch (error) {
      done(error);
    }
  }
}

4. auth.module

import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { JwtModule } from '@nestjs/jwt';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Member } from 'src/_common/entities/member.entity';
import { GoogleStrategy } from './strategies/google.strategy';

@Module({
  imports: [TypeOrmModule.forFeature([Member]), JwtModule],
  controllers: [AuthController],
  providers: [AuthService, GoogleStrategy], // providers에 추가
})
export class AuthModule {}

Member엔티티는 User엔티티라고 보면된다. user를 나는 member테이블에 저장하고있다.
auth.module.ts의 providers에 GoogleStrategy를 추가한다.


여기까지 작성한 후 실행을 해보자.

그 전에 @Get('google/login')를 호출하는 버튼을 만들어야 한다.

코드 흐름

  • 버튼 클릭 -> @Get('google/login') (첫번째 컨트롤러)
  • -> @UseGuards(AuthGuard('google')) 가드에서 구글로그인 화면으로 리디렉션
  • -> 인증 성공시 콜백URL로 리디렉션 (두번째 컨트롤러)
  • -> 두번째 가드 - validate메서드 실행

자세한 설명

AuthGuard는 Passport 라이브러리에서 제공하는 기능으로, 요청이 전략에 의해 인증되었는지 확인합니다. 사용자가 Google로 로그인을 시도하면 다음과 같은 흐름을 거칩니다

  1. 사용자가 /auth/google 라우트로 요청을 보냅니다.
  2. AuthGuard('google') 가 이를 가로챕니다. 'google'이라는 문자열은 GoogleStrategy를 참조합니다.
  3. AuthGuard는 GoogleStrategy의 인스턴스를 생성하고, Strategy 내부에서 정의한 설정(클라이언트 ID, 클라이언트 비밀번호 등)에 따라 Google OAuth 서비스로 리다이렉션합니다.
  4. 사용자가 Google 페이지에서 자신의 계정으로 로그인하고 애플리케이션에 대한 접근 권한을 부여하면, Google은 설정된 callback URL(/auth/google/callback)로 리다이렉션합니다.
  5. 이 때 함께 전달되는 query string 내부의 코드(code) 값을 Passport 구글 전략(GoogleStrategy)이 받아서 accessToken, refreshToken, 그리고 profile 정보를 얻습니다.
  6. 이 정보들은 validate 메서드로 전달됩니다.
async validate(accessToken: string, refreshToken: string, profile: any) {
    const { name, emails } = profile;
    const user = {
        email: emails[0].value,
        firstName: name.givenName,
        lastName: name.familyName,
    };
    return user;
}
  1. 여기서 validate 메서드는 프로필 정보를 추출하여 사용자 객체를 반환합니다.
  2. 반환된 사용자 객체는 Request 객체(req.user)에 추가되어 후속 처리 과정에서 사용할 수 있게 됩니다.

인증에 성공하면 화면에서 구글에서 받아온 사용자 정보를 볼 수 있을 것이다.
다음 글에서 받아온 정보를 가지고 로그인, 회원가입을 시키는것을 알아볼 것이다.

post-custom-banner

0개의 댓글