오늘 소셜 로그인 연동에 대해 배웠다. 정리 들어가기 전에 전에 정리했던 OAuth에 다시 한번 말하자면
OAuth
: 인터넷 사용자들이 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여 할
수 있는 공통적인 수단으로서 사용되는, 접근 위임을 위한 개방형 표준.
이다.
각종 브라우저 로그인 연동에 대해 정리하고자 한다.
수도코드를 적으면서 데이터의 흐름을 파악하면서 적어보자.
사용자가 브라우저를 통해 서비스 접근 하였을 때 백엔드에서 유저에게 로그인 페이지 제공
// auth.Controller.ts
@UseGuards(AuthGuard('naver'))
// 가드를 통과해주려면 naver.strategy 를 작성해줘야한다.
@Get('/login/naver') //로그인 페이지 EndPoint 작성
async loginGoogle(
@Req() req: Request & IOAuthUser, //
@Res() res: Response, //
) {
// OAuth 의 로직은 중복되기 때문에 auth.service에 하나의 함수를 만들어줘 합쳐준다.
// 이건 아래에서 !!
this.authService.loginOauthService({ req, res });
}
시크릿 코드 전달 후 정상일 경우 플랫폼에서 백엔드에게 AccessToken / refreshToken / profile 전달
// jwt-social.naver.strategy.ts
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-naver';
// passport-naver 모듈 설치
// yarn add passport-naver
// 도커 타입설정을 위한 모듈 설치
// yarn add --dev @types/passport-naver
export class JwtNaverStrategy extends PassportStrategy(Strategy, 'naver') {
constructor() {
super({
clientID: process.env.NAVER_CLIENT_ID,
clientSecret: process.env.NAVER_CLIENT_SECRET,
callbackURL: 'http://localhost:3000/login/naver',
scope: ['email', 'profile'],
});
}
validate(accessToken, refreshToken, profile) {
console.log(accessToken);
console.log(refreshToken);
console.log(profile);
백엔드API에서 회원가입을 검증하고 회원가입이 안되어있다면 회원가입 시킨뒤 로그인
@UseGuards(AuthGuard('naver'))
// 가드를 통과해주려면 naver.strategy 를 작성해줘야한다.
@Get('/login/naver') //로그인 페이지 EndPoint 작성
async loginGoogle(
@Req() req: Request & IOAuthUser, //
@Res() res: Response, //
) {
// OAuth 의 로직은 중복되기 때문에 auth.service에 하나의 함수를 만들어줘 합쳐준다.
// 이건 아래에서 !!
//this.authService.loginOauthService({ req, res });
//프로필을 받아온 다음, 로그인 처리해야 하는 곳
// 1. 회원조회
// 회원이 있으면 findOne 으로 찾아서 기다렸다가 유저 값을 반환한다.
let user = await this.usersService.findOne({ email: req.user.email });
// 회원이 없다면 회원등록하고 그 등록된 데이터를 유저에 담아준다.
// 2. 회원가입이 안돼있다면? 자동회원가입
if (!user) user = await this.usersService.create({ ...req.user });
// 3. 회원가입이 돼있다면? 로그인하기
// (refreshToken, accessToken 만들어서 브러우저에 전송)
this.authService.setRefreshToken({ user, res });
}
로그인 완료 화면으로 페이지 이동(refreshToken 포함)
// auth Controller.ts
@UseGuards(AuthGuard('google'))
// 가드를 통과해주려면 google.strategy 를 작성해줘야한다.
@Get('/login/google')
async loginGoogle(
@Req() req: Request & IOAuthUser, //
@Res() res: Response, //
) {
//프로필을 받아온 다음, 로그인 처리해야 하는 곳
// 1. 회원조회
// 회원이 있으면 findOne 으로 찾아서 기다렸다가 유저 값을 반환한다.
let user = await this.usersService.findOne({ email: req.user.email });
// 회원이 없다면 회원등록하고 그 등록된 데이터를 유저에 담아준다.
// 2. 회원가입이 안돼있다면? 자동회원가입
if (!user) user = await this.usersService.create({ ...req.user });
// 3. 회원가입이 돼있다면? 로그인하기
// (refreshToken, accessToken 만들어서 브러우저에 전송)
this.authService.setRefreshToken({ user, res });
res.redirect(
'http://localhost:5500/class/section11/11-06-login-google/frontend/social-login.html',
);
}
}
모듈이 잘 실행되게 하기 위해 app.module.ts 와 auth.module.ts 수정
// auth.module.ts
@Module({
imports: [
JwtModule.register({}),
UsersModule, //
],
providers: [
JwtAccessStrategy,
JwtRefreshStrategy,
//추가
JwtNaverStrategy,
AuthResolver, //
AuthService,
],
controllers: [
//추가
AuthController, //
],
})
export class AuthModule {}
//app.module.ts
@Module({
imports: [
//생략
ConfigModule.forRoot(),
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: 'src/commons/graphql/schema.gql',
context: ({ req, res }) => ({ req, res }),
}),
TypeOrmModule.forRoot({
//생략
}),
],
providers: [
JwtRefreshStrategy, //
JwtAccessStrategy,
//추가
JwtNaverStrategy,
],
})
export class AppModule {}
Google People API 사용 설정하기
들어가서 사용 버튼 클릭하기!
**OAuth 동의 설정하기**
UserType은 "외부" 선택 → 만들기 클릭하기.
앱 정보 창 설정하기 ( 필수입력들만 입력하기)
People API 체크 후 업데이트 클릭 후 저장후 계속
테스트 사용자 본인 이메일 작성
콜백 URL 작성
클라이언트 ID 와 SERCET KEY 생성완료
//auth.controller.ts
@Controller()
export class AuthController {
constructor(
private readonly userService: UsersService, //
private readonly authService: AuthService, //
) {}
@UseGuards(AuthGuard('google'))
@Get('/login/google')
async loginGoogle(
@Req() req: Request & IOAuthLoginUser, //
@Res() res: Response, //
) {
let user = await this.userService.findOne({ email: req.user.email });
// 회원이 없다면 회원등록하고 그 등록된 데이터를 유저에 담아준다.
// 2. 회원가입이 안돼있다면? 자동회원가입
if (!user) user = await this.userService.create({ ...req.user });
console.log(user);
// 3. 회원가입이 돼있다면? 로그인하기x
// (accessToken,refreshToken 만들어서 브라우저 전송)
this.setRefreshToken({ user, res });
res.redirect('http://localhost:5500/mainproject/frontend/login/index.html');
}
}
//jwt.social.google.strategy.ts
export class JwtGoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor() {
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: 'http://localhost:3000/login/google',
scope: ['email', 'profile'],
});
}
validate(accessToken, refreshToken, profile) {
console.log(accessToken);
console.log(refreshToken);
console.log(profile);
return {
name: profile.displayName,
email: profile.emails[0].value,
password: '1234',
phone_number: '010',
};
}
}
//app.module.ts 와 main.ts 컨트롤 설정하기