구글 OAuth

여기 에 구글 OAuth 사용 준비 방법이 나와있다. 해당 포스트를 따라 구글 OAuth 동의화면까지 완료하면 로직을 작성해야 한다.


 

구글 로그인 실행 순서

 

  1. 구글 OAuth를 사용해 구글에 사용자 정보를 요청한다
  2. 구글에서 이메일과 프로필 등 유저의 정보를 GoogleStrategy의 validate() 메서드에서 콜백으로 받는다.
  3. 이때 넘어오는 정보 중에 id 값을 확인 후 DB에 정보가 있는지 확인한다
  4. 정보가 있다면 JWT 토큰 발급, 없다면 DB에 정보 넣은 후 JWT토큰을 발급한다.

 

 

Package 설치

 

npm i passport-google-oauth20
npm i -D @types/passport-google-oauth20

 

 

strategy 클래스 작성

 

nestjs에서 구글 로그인을 만들기 위해선 GoogleStrategy 클래스를 정의해야 한다. 이 클래스는 PassportStrategy 클래스를 상속받아서 구글 인증에 필요한 설정을 하고, validate 메서드를 통해 구글에서 받은 토큰과 프로필 정보를 검증 및 처리한다.

 

import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy } from "passport-google-oauth20";

interface GoogleProfile {
  displayName: string;
  emails: { value: string }[];
  photos: { value: string }[];
  id: string;
  provider: string;
}

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly configService: ConfigService) {
    super({
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_SECRET_KEY,
      callbackURL: process.env.GOOGLE_CALLBACK_URL,
      scope: ["email", "profile"],
    });
  }
  
  // refreshToken를 얻기 위한 필수 코드
  authorizationParams(): {[key: string]: string; } {
    return ({
      access_type: 'offline',
      prompt: 'select_account',
    });
  }
  
  validate(accessToken: string, refreshToken: string, profile: GoogleProfile) {
    return {
      id: profile.id,
      provider:profile.provider,
      name: profile.displayName,
      email: profile.emails[0].value,
      photo: profile.photos[0].value,
    };
  }
}

 

옵션에 정의된 값들은 env 파일에 설정해 뒀다.

 

authorizationParams 메서드는 google에서 refresh token을 받기 위해서 필수로 작성해야 하는 메서드이다. 여기서 access_type을 "offline" 값을 리턴해야 한다.

이는 구글에 access token으로 api를 날릴 때 토큰이 만료되었을 경우 갱신해야 할 때 refresh token 이 필요한 경우에만 작성하면 된다.

 

validate 메서드는 Strategy를 통해 받아온 유저 정보를 해당 Strategy를 사용한 컨트롤러에 리턴해준다.

위의 로직에서는 name, email, photo, id, provider 다섯 가지만 return 하는데 profile을 console에 찍어보고 필요한 부분은 추가해서 return 하면 된다. 여기서 id 와 email, provider 은 DB에 확인하기 위해서 저장해둔다. provider로 어느 소셜로그인인지 확인, id는 구글에서 가입을 한적이 있는지 확인, email은 다른 소셜로그인에서 중복된 email을 사용했는지 확인하기 위해서 저장한다.


 

GoogleAuthGuard  작성

import { Injectable } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";

@Injectable()
export class GoogleAuthGuard extends AuthGuard("google") {
  async canActivate(context: any): Promise<boolean> {
    const result = (await super.canActivate(context)) as boolean;
    return result;
  }
}

controller에 사용할 guard 를 작성한다.

 


 

Controller 작성

컨트롤러에는 두 개의 라우터를 작성해야 한다

  • 구글 로그인 페이지로 redirection 할 라우터
  • 구글 로그인 후 callback URL로 오는 요청을 처리할 라우터

 

import { GoogleAuthGuard } from "./google/auth.google.guard";
import { Controller, Get, Query, Req, Res, UseGuards } from "@nestjs/common";
import { AuthService } from "./auth.service";
import { Request, Response } from "express";

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

  @Get("google-login")
  @UseGuards(GoogleAuthGuard)
  async googleAuth(@Req() req: Request) {}

  @Get("google")  
  @UseGuards(GoogleAuthGuard)
  async googleAuthRedirect(@Req() req: Request, @Res() res: Response) {
    const { user } = req;
    return user;
  }
}

google-login 라우터와 콜백 라우터인 google을 만들었다. 

 

콜백 라우터에서 user를 콘솔에 찍어보면 해당 데이터가 잘 나온다

 

 

 

여기까지 코드의 실행순서는 이러하다.

  1. 클라이언트가 auth/google-login 에 GET 요청을 보내고, 해당 라우터가 실행된다
  2. @UseGuards(GoogleAuthGuard) 데코레이터에 의해 GoogleAuthGuard 가 호출된다.
  3. GoogleAuthGuard 클래스의 canActivate 메서드에 의해서 부모 클래스의 canActivate 메서드를 실행한다.
  4. 부모 클래스인 AuthGuard는 Passport의 인증 메서드를 호출하여 인증을 시도한다
  5. 인증 성공 후 canActivate 메서드는 true를 반환한다.
  6. 인증이 성공된 경우 클라이언트는 Google의 OAuth 서비스에 리디렉션 되어 로그인을 시도하게 된다.
  7. 로그인 완료하면 Passport의 콜백 함수가 호출되며 작성한 GoogleStrategy 클래스의 validate 메서드가 호출된다.
  8. 추출된 유저 정보를 콜백라우터에 전달한다.

 


 

모듈에 등록

GoogleStrategy 와 controller는 module에 등록해야 한다.

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { GoogleStrategy } from './google/auth.google.strategy';

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

 


 

 

 

 

개인 기록용입니다. 만약 잘못된 부분이 있다면 댓글로 알려주신다면 감사하겠습니다!

'프레임워크\라이브러리 > Nest.js' 카테고리의 다른 글

[NestJS] 구글 로그인 (2)/JWT 발급  (0) 2024.03.03
[NestJS] JWT 모듈  (1) 2024.03.01
[NestJS] Guard  (0) 2024.02.22
[NestJS] 커스텀 파이프  (0) 2024.02.20
[NestJS] 파이프  (1) 2024.02.18