nest.js 프로젝트를 만들면서 소셜 로그인을 구현해보았다.
소셜 로그인은 이미 가입하고 있는 소셜 미디어 계정(예: 페이스북, 구글, 트위터 등)을 이용해 로그인하는 방식이다.
별도의 회원가입 과정 없이 로그인 할 수 있으므로 서비스 이용 장벽을 낮춘다.
웹사이트에서 아이디와 비밀번호를 관리하지 않고 대형 소셜 미디어 회사들의 보안 시스템에 의존할 수 있어 안전하다.
npm i @nestjs/passport passport-google-oauth20 @nestjs/passport
https://console.cloud.google.com/
먼저 구글 클라우드 플랫폼에서 프로젝트를 만들어서 클라이언트ID를 발급받아야 한다.
구글링을 하면 자세히 설명한 글이 많으므로 여기선 생략하겠다.
발급이 끝나면 이런 화면을 볼 수 있다. 우리는 세가지를 사용하게 된다.
ID와 비밀번호는 .env에 환경변수로 설정하여 사용한다.
GOOGLE_CLIENT_ID = <발급한 클라이언트 ID>
GOOGLE_CLIENT_SECRET = <발급한 클라이언트 비밀번호>
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); // 화면에 표시.
}
}
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);
}
}
}
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')를 호출하는 버튼을 만들어야 한다.
AuthGuard는 Passport 라이브러리에서 제공하는 기능으로, 요청이 전략에 의해 인증되었는지 확인합니다. 사용자가 Google로 로그인을 시도하면 다음과 같은 흐름을 거칩니다
/auth/google
라우트로 요청을 보냅니다.AuthGuard('google')
가 이를 가로챕니다. 'google'이라는 문자열은 GoogleStrategy를 참조합니다./auth/google/callback
)로 리다이렉션합니다.accessToken
, refreshToken
, 그리고 profile
정보를 얻습니다.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;
}
인증에 성공하면 화면에서 구글에서 받아온 사용자 정보를 볼 수 있을 것이다.
다음 글에서 받아온 정보를 가지고 로그인, 회원가입을 시키는것을 알아볼 것이다.