devlog_owen
[TIL] nest.js에서 네이버 소셜로그인 기능구현하기 본문
728x90
설치 및 세팅
하단의 명령어로 passport-Naver를 다운받는다.
$ npm install passport-naver
https://developers.naver.com/apps/#/myapps/g4Sz3QoF8ZQFrEPBLmmq/config
애플리케이션 - NAVER Developers
developers.naver.com
여기서 적당히(?) API 설정을 하면 된다.
naver-auth.guard.ts
import { AuthGuard } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
@Injectable()
export class NaverAuthGuard extends AuthGuard('naver') {}
Nest.js에서 제공하는 passport 라이브러리를사용해 네이버 소셜로그인을 처리하는 커스텀 가드다.
AuthGuard('naver')의 naver는 passport에서 사용되는 전략 이름을 나타낸다. 이런식으로 구현해두면 nest.js 안에서 자동적으로 naver 가드를 적용해서 전략파일을 거칠 수 있다.
naver.strategy.ts, naver-admin.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-naver';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class NaverStrategy extends PassportStrategy(Strategy, 'naver') {
constructor(private configService: ConfigService) {
super({
clientID: configService.get<string>('NAVER_CLIENT_ID'),
clientSecret: configService.get<string>('NAVER_CLIENT_SECRET'),
callbackURL: configService.get<string>('NAVER_REDIRECT_URI'),
});
}
async validate(
accessToken: string,
refreshToken: string,
profile: any,
done: any,
) {
const userEmail = profile._json.email;
const userNick = profile._json.nickname;
const userProfileImage = profile._json.profile_image;
const userProvider = profile.provider;
const userProfile = {
userEmail,
userNick,
userProvider,
userProfileImage,
};
return { accessToken, userProfile };
}
}
passportStrategy를 상속받아서 소셜로그인 전략을 구현한다.프로젝트에서는 유저와 사장이 나눠져서 두가지 전략파일을 만들고 콜백링크도 두가지로 나눴다.
NAVER_CLIENT_ID=''
NAVER_CLIENT_SECRET=''
NAVER_REDIRECT_URI='https://localhost:3000/auth/naver/callback'
NAVER_ADMIN_REDIRECT_URI='http://localhost:3000/auth/naver/admin/callback'
auth.controller.ts
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
//네이버 일반사용자 소셜로그인
@Get('naver')
@UseGuards(AuthGuard('naver'))
async naverLogin(): Promise<void> {}
@Get('naver/callback')
@UseGuards(AuthGuard('naver'))
async naverLoginCallback(@Req() req, @Res() res) {
return await this.authService.naverLoginCallback({ req, res });
}
//네이버 지점업주 소셜로그인
@Get('naver-admin')
@UseGuards(AuthGuard('naver-admin'))
async naverAdminLogin(): Promise<void> {}
@Get('naver/admin/callback')
@UseGuards(AuthGuard('naver-admin'))
async naverAdminLoginCallback(@Req() req, @Res() res) {
return await this.authService.naverAdminLoginCallback({ req, res });
}
}
auth.service.ts
@Injectable()
export class AuthService {
//네이버 로그인
async naverLoginCallback({ req, res }) {
// 네이버 이메일로 사용자를 찾는다.
const email = req.user.userProfile.userEmail;
const nickname = req.user.userProfile.userNick;
let OAuthUser = await this.userRepository.findOne({
where: { email },
});
// 네이버 사용자의 이메일 값을 변수 지정
// user의 아이디 값을 해시화 해서 변수 지정
const hashedNaverPassword = await bcrypt.hash(email, 10);
// 해당 이메일 사용자가 없다면 회원가입 로직처럼 회원 생성
// 비밀번호는 user의 아이디 값을 해시화 해서 변수 지정한 값을 사용
if (!OAuthUser) {
OAuthUser = await this.userRepository.save({
email,
nickname,
password: hashedNaverPassword,
});
}
const accessToken = this.generateAccessToken(
OAuthUser.id,
OAuthUser.nickname,
);
const refreshToken = this.generateRefreshToken(OAuthUser.id);
await this.userService.update(OAuthUser.id, {
currentRefreshToken: refreshToken,
});
if (OAuthUser)
res.redirect(
`http://localhost:3000/login/success?accessToken=${accessToken}&refreshToken=${refreshToken}`, //받아주는 페이지 만들어야함
);
else res.redirect('http://localhost:3000/login/failure');
}
//네이버 지점업주 회원가입/로그인
async naverAdminLoginCallback({ req, res }) {
// 네이버 이메일로 사용자를 찾는다.
const email = req.user.userProfile.userEmail;
const nickname = req.user.userProfile.userNick;
let OAuthUser = await this.userRepository.findOne({
where: { email },
});
// 네이버 사용자의 이메일 값을 변수 지정
// user의 아이디 값을 해시화 해서 변수 지정
const hashedNaverPassword = await bcrypt.hash(email, 10);
// 해당 이메일 사용자가 없다면 회원가입 로직처럼 회원 생성
// 비밀번호는 user의 아이디 값을 해시화 해서 변수 지정한 값을 사용
if (!OAuthUser) {
OAuthUser = await this.userRepository.save({
email,
nickname,
password: hashedNaverPassword,
role: 1,
});
}
const accessToken = this.generateAccessToken(
OAuthUser.id,
OAuthUser.nickname,
);
const refreshToken = this.generateRefreshToken(OAuthUser.id);
await this.userService.update(OAuthUser.id, {
currentRefreshToken: refreshToken,
});
if (OAuthUser)
res.redirect(
`http://localhost:3000/login/success?accessToken=${accessToken}&refreshToken=${refreshToken}`, //받아주는 페이지 만들어야함
);
else res.redirect('http://localhost:3000/login/failure');
}
}
위에서 말했듯이 유저, 사장별로 컨트롤러를 다르게했다. 애초에 유저,사장 회원가입 컨트롤러가 따로 있기 때문이다.
일반유저의 경우는
1. localhost:3000/auth/naver에서 네이버에서 로그인하고
2. 다시 웹페이지로 돌아와서 이번에는 콜백 엔드포인트로가서 localhost:3000/auth/naver/callback에서 회원가입/로그인을 한 후 accesstoken과 refreshtoken을 준다.
728x90
'TIL' 카테고리의 다른 글
[TIL] .env 파일이 gitignore에 있는데도 푸시될때 해결법 (0) | 2024.01.19 |
---|---|
[TIL] [기술면접] OAuth에 대해서 설명해주세요. (0) | 2024.01.18 |
[TIL] 최종프로젝트 와이어프레임 figma로 나타내기, erd 테이블 설정 수정 (3) | 2024.01.15 |
[TIL][기술면접준비] 1. NoSQL과 RDBMS의 특징과 차이점에 대해서 장, 단점을 들어 설명해주세요 (1) | 2024.01.13 |
[TIL] Redis란? Redis와 MySQL의 차이, Redis 설치 (1) | 2024.01.12 |