• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

카카오 로그인 serializer 구현

23.11.09 10:43 작성 조회수 376

0

안녕하세요 제로초님.

passport-kakao를 이용해서 kakao 로그인을 구현해보고 있습니다.

로그인과 회원가입 까지는 잘 되는데, serializer을 통해서 cookie가 저장되지 않습니다.

 

kakako.strategy.ts

import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-kakao';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as _ from 'lodash';
import { AuthService } from './auth.service';
import { Platform } from 'src/entities/common/Platforms';

@Injectable()
export class KakaoStrategy extends PassportStrategy(Strategy) {
  constructor(
    private readonly configService: ConfigService,
    private authService: AuthService,
  ) {
    super({
      clientID: configService.get<string>('KAKAO_REST_API_KEY'),
      clientSecret: configService.get<string>('KAKAO_CLIENT_SECRET'),
      callbackURL: configService.get<string>('KAKAO_REDIRECT_URI'),
    });
  }

  async validate(accessToken, refreshToken, profile, done) {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const {
      _json: {
        id,
        properties: { nickname, profile_image: profileImage },
        kakao_account: { email },
      },
    } = profile;

    const user = await this.authService.findOrCreateUser(
      email,
      nickname,
      Platform.KAKAO,
    );
    return done(null, user);
  }
}

 

아래에 있는 kakao-auth.guard.ts와 kakao.serializer.ts는 로컬 로그인과 다른 점이 없을 것 같아서 그대로 썼습니다.

 

kakao-auth.guard.ts

import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class KakaoAuthGuard extends AuthGuard('kakao') {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const can = await super.canActivate(context);
    if (can) {
      const request = context.switchToHttp().getRequest();
      await super.logIn(request);
    }
    return true;
  }
}

 

kakao.serializer.ts

import { Injectable } from '@nestjs/common';
import { PassportSerializer } from '@nestjs/passport';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { AuthService } from './auth.service';
import { Users } from 'src/entities/Users';

@Injectable()
export class KakaoSerializer extends PassportSerializer {
  constructor(
    private readonly authService: AuthService,
    @InjectRepository(Users) private usersRepository: Repository<Users>,
  ) {
    super();
  }

  serializeUser(user: Users, done: CallableFunction) {
    done(null, user.id);
  }

  async deserializeUser(userId: string, done: CallableFunction) {
    return await this.usersRepository
      .findOneOrFail({
        where: { id: +userId },
        select: ['id', 'email', 'nickname'],
      })
      .then((user) => {
        done(null, user);
      })
      .catch((error) => done(error));
  }
}
  1. 이처럼 작성하면, 로그인 시 세션에 쿠키가 저장되어야 하는게 아닌가요?

  2. 사실 강의에서 설명해주신 내용중 이해가 잘 가지 않는 부분이 있습니다.


    localStrategy에서 done(null, user) -> local-auth.guard.ts에서 super.logIn(request) -> local serializer 에서 serializeUser() 을 호출한다고 말씀하셨는데,
    세 가지 파일에서 LocalStrategy, LocalAuthGuard, LocalSerializer을 서로 명시적으로 연결해준적이 없음에도 불구하고 어떻게 서로 잘 알아서 호출되는지 궁금합니다.
    그냥 앞에 모두 Local이 붙어서, 잘 찾아서 호출되는 건가요?

답변 1

답변을 작성해보세요.

0

일단 카카오시리얼라이저를 따로 두실 필요 없고 로컬 시리얼라이저로 공용으로 쓸 수 있습니다. 코드로는 컨트롤러랑 모듈쪽도 같이 봐야할 것 같습니다.

@nestjs/passport 내부적으로 처리해주는 것이라 명시적인 연결 관계가 없습니다. express+passport에서도 이 때문에 헷갈리는 부분입니다.

이민석님의 프로필

이민석

질문자

2023.11.09

authModule은 그냥 provider에 다 집어넣었습니다.

authController를 따로 만들지 않았고, 그냥 userController에 로컬로그인 + 카카오로그인 모두 처리했습니다

user.controller.ts

import {
  Body,
  Controller,
  Get,
  Post,
  Req,
  Res,
  UseGuards,
} from '@nestjs/common';
import { JoinRequestDto } from 'src/dto/join.request.dto';
import { UsersService } from './users.service';
import { User } from 'src/decorators/user.decorator';
import { LocalAuthGuard } from 'src/auth/local-auth.guard';
import { AuthGuard } from '@nestjs/passport';
import { KakaoAuthGuard } from 'src/auth/kakao-auth.guard';

@Controller('api/users')
export class UsersController {
  constructor(private userService: UsersService) {}
  @Get()
  getUsers(@User() user) {
    return user;
  }

  @Post()
  async postUsers(@Body() body: JoinRequestDto) {
    await this.userService.postUsers(body.email, body.password, body.nickname);
  }

  @Post('login')
  @UseGuards(LocalAuthGuard)
  logIn(@Req() req) {
    return req.user;
  }

  @Get('login/kakao')
  @UseGuards(KakaoAuthGuard)
  kakaoLogIn() {
    return;
  }

  @Get('login/kakao/callback')
  @UseGuards(KakaoAuthGuard)
  kakaoLogInRedirect(@Req() req) {
    return req.user;
  }

  @Post('logout')
  logOut(@Req() req, @Res() res) {
    req.logOut();
    res.clearCookie('connect.sid', { httpOnly: true });
    res.send('ok');
  }
}

로컬 시리얼라이저를 공용으로 사용하려면 어떻게 해야하나요?

카카오 시리얼라이저를 제거하고 로컬 시리얼라이저만 넣으면 됩니다. 로컬 시리얼라이저에서 로컬이라는 이름은 아무 역할도 하지 않습니다. 그냥 login 후에 req.user를 생성하는 시리얼라이저일 뿐입니다.

serializeUser랑 deserializeUser 안에 콘솔로그도 찍어보세요.