inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

Slack 클론 코딩[백엔드 with NestJS + TypeORM]

@nestjs/passport

소셜 로그인 질문

1576

노른자

작성한 질문수 81

0

저는 passport-jwt를 사용하여 로그인을 구현하였는데,

passport-apple 를 사용하여 소셜 로그인 구현 중에 막혔습니다.

 아무리 구글링해봐도 passport-apple + nest.js 조합이 없어 해결이 되지않아 질문드립니다.

우선 제가 이해하고있는 전반적인 큰 흐름부터 맞나 의심됩니다.

1. passport-apple 을 통하여 유저 정보를 받아온 후

2. 유저에게 jwt를 발급

3. 유저는 클라이언트단(or 쿠키)에 jwt 저장

4. 이후 애플서버와는 연계없이 jwt를 이용해 제 서버만을 통하여 유저 정보를 줌.

이게 맞나 모르겠고 틀린 점이 있으면 지적해주시면 감사하겠습니다!

 

그리고 본 질문은 passport-apple을 통하여 어떻게 유저 정보를 받아오는지를 모르겠습니다.

 

node.js passport-apple 공식문서를 통해 유추하며 해봤습니다

/apple.strategy.ts 파일

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-apple';

@Injectable()
export class AppleStrategy extends PassportStrategy(Strategy,'apple') {
constructor() {
super({
clientID: process.env.APPLE_CLIENT_ID,
teamID: process.env.APPLE_TEAM_ID,
callbackURL: process.env.APPLE_CALLBACK_URL,
keyID: process.env.APPLE_KEY_ID,
privateKeyString: process.env.APPLE_KEY,
//참고로 privateKeyLocation로도 해보고
// passReqToCallback: true도 넣어봤습니다. (뭔진 모르겠지만)
});
}

async validate(req,accessToken, refreshToken, idToken, profile, done) {
console.log('req :', req);
console.log('idToken :', idToken);
console.log('accessToken :', accessToken);
console.log('refreshToken :', refreshToken);
console.log('profile :', profile);
done(null,idToken)
}
}

super에 필요객체를 넣고 validate 인자로 어떤값들이 들어오는지 하나하나 로그를 찍어보려했으나, 아예 저 부분으로 넘어가지않고 에러가 뜹니다.

 

/auth.module.ts 파일

import { JwtStrategy } from './strategies/jwt.strategy';
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/common/entities/user.entity';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { AppleStrategy } from './strategies/apple.strategy';

@Module({
imports: [
TypeOrmModule.forFeature([User]),
PassportModule.register({ session: false }),
JwtModule.register({
secret: process.env.JWT_SECRET_KEY,
signOptions: { expiresIn: '60s' },
}),
],
providers: [AuthService, JwtStrategy, AppleStrategy],
controllers: [AuthController],
})
export class AuthModule {}

 

/auth.controller.ts 파일

import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { Controller, Get, UseGuards, Req, Post, Body } from '@nestjs/common';

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

@Get('/apple')
@UseGuards(AuthGuard('apple'))
async appleLogin() {}
 
@Post('/apple/callback')
@UseGuards(AuthGuard('apple'))
async appleLoginCallback(@Req() req, @Body() body) {
console.log("req : ",req)
console.log('body : ',body)
return 'ok';
}
}

 

전 강의영상의 LocalAuthGuard 같은 클래스 파일을 따로 만들지 않고 바로  AuthGuard('apple')을 데코레이터에 넣어주는 식올 구현했습니다. 

여기서 "도메인/auth/apple" 로 접속시 애플 로그인 화면이 뜨는 것까지는 되는데, 로그인을 후 "계속" 버튼을 누르면 

서버에 이러한 500에러가 뜹니다

[Nest] 58  - 02/05/2022, 9:56:26 AM   ERROR [ExceptionsHandler] Failed to obtain access token

InternalOAuthError: Failed to obtain access token

    at AppleStrategy.OAuth2Strategy._createOAuthError (/usr/src/app/node_modules/passport-oauth2/lib/strategy.js:423:17)

    at /usr/src/app/node_modules/passport-oauth2/lib/strategy.js:177:45

    at /usr/src/app/node_modules/passport-apple/src/strategy.js:101:13

    at processTicksAndRejections (node:internal/process/task_queues:96:5)

[Nest] 58  - 02/05/2022, 9:56:26 AM     LOG [HTTP] 500 POST /api/auth/apple/callback - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36 ::ffff:172.18.0.3

도무지 모르겠어서 2번째 (Post부분) @UseGuard데코레이터를 빼서 결과를 봐보니 body에 

{state: '2ed****e0ed',

 code: 'ce23f9c72e7904******d8565b94b4a7.0.r**z.5nDn7h*******RJufk11LWQ'}

이러한 객체가 담겨옵니다. 제 예측으로는 이것이 access 토큰인것같고 이걸 다시 애플에 보내 유저정보를 받아내야하는것같은데, 코드를 어떻게 구현해야하는지 모르겠습니다...

혹시 실무에서 apple login을 어떻게 구현하셨는지, passport-apple이 안되거나 더 편한 다른방법이라도 있다면 알려주시면 정말 감사하겠습니다.

express nodejs TypeORM NestJS

답변 1

0

제로초(조현영)

일단 passport-jwt랑 passport-apple은 호환이 안 될겁니다. passport-apple이 jwt 토큰으로 로그인하는 방식이 아닌 걸로 알고 있습니다.

애플쪽에서도 로그인 서비스를 만드셔야 할텐데 만드셨나요? 그리고 현재 서버의 주소를 등록하셨나요?

0

노른자

네 애플에시 로그인 서비스를 만들었고, https 적용시킨 현재 서버 주소도 등록했습니다.

passport-apple 로 유저정보만 받아온뒤 db에 저장후 그것으로 jwt를 발급한뒤 이후로는 passport-jwt를 사용하여 인증하려고 합니다.

0

제로초(조현영)

아, 지금보니까 /apple/callback이 post네요. get이어야될겁니다. /apple/callback 주소도 애플서비스에 등록하셨죠?

0

노른자

예 redirectURL로 등록하였습니다. 다만 GET으로도 해본결과 

LOG [HTTP] 404 POST /api/auth/apple/callback - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36 ::ffff:172.18.0.3

404 POST가 뜨는걸로보아하니 Post로 오는것같습니다.

0

노른자

혹시라도 여기서 잘못됐을수도있으니 말씀드려봅니다.

AWS EC2 사용중이며 앞단에 리버스프록시 nginx를 넣고 backend 서버를 넣고 도커로 감싼형태입니다.

0

제로초(조현영)

음, 저기서 POST로 찍히는 것으로 보아서는 애플이 post로 쏘는 게 맞나보네요.

https://github.com/ananay/passport-apple/issues/33

같은 질문이 있는데

https://github.com/ananay/apple-auth/blob/master/SETUP.md

에 따라서 세팅이 문제였다고밖에 생각되지 않긴 합니다.

 

0

제로초(조현영)

소스코드 까보니

https://github.com/ananay/passport-apple/blob/master/src/strategy.js#L61-L65

이 줄이 문제인건데 해당 옵션들에 문제가 있는 것 같습니다.

0

노른자

흠.. 분명히 설정은 똑바로 돼있는데, 밑에 privateKeyLocation과 privateKeyString을 둘다 넣어줘야하는걸까요

const _tokenGenerator = new AppleClientSecret({
"client_id": options.clientID,
"team_id": options.teamID,
"key_id": options.keyID
}, options.privateKeyLocation, options.privateKeyString);


하나 걸리는건 privateKeyString을 .env에 넣을때 한줄 파일로 만들었는데

-----BEGIN PRIVATE KEY-----

MIGTAgEAMBMGByqGSM49AgE********wdwIBAQQgdtJipdkgPvvNyjK6

ZS0Bux9rK4ioPDbk********Izj0DAQehRANCAASqwfykwIPl8TmQ

wIR1NHHVtQPMRv*********4hokF/5m+SPo9kLLDuF/ieGc19CCp04Un

Yp***bE

-----END PRIVATE KEY-----

이랬던걸

-----BEGIN PRIVATE KEY-----\n

MIGTAgEAMBMGByqGSM49AgE********wdwIBAQQgdtJipdkgPvvNyjK6\n

ZS0Bux9rK4ioPDbk********Izj0DAQehRANCAASqwfykwIPl8Tm\n

wIR1NHHVtQPMRv*********4hokF/5m+SPo9kLLDuF/ieGc19CCp04Un\n

Yp***bE\n

-----END PRIVATE KEY-----

이런식으로 \n 을 붙여 한줄로 만들었습니다.

하지만 privateKeyLocation으로도 해봤기때문에, 이것때문은 아니라고 보이고

 

두번째로 의심되는건 scope인데, passport-apple 공식문서엔 scope적는 필드가 보이지 않아서 넣지않았습니다.

 

 

0

노른자

무튼 아무래도 답은 설정문제 밖에 없어보이네요... 차라리 다행이네요

 

이것저것 해보고 안되면 다시 질문드리겠습니다.

주말 늦은시간까지 도와주셔서 정말 감사합니다!

0

제로초(조현영)

privateKeyString에서 줄바꿈 문제일 수 있으니 그건 빼시고 privateKeyLocation으로 키 위치를 지정하시는 게 더 나을 것 같습니다. (키는 따로 텍스트로 저장)

0

노른자

strategy 폴더 안에 키파일과 apple.strategy.ts를 같이 넣고,

privateKeyLocation: './AuthKey_T****99Y.p8',

를 추가해도 안되네요... 

도커때문인가,,,

ack:
build:
context: ./back
dockerfile: Dockerfile
restart: always
volumes:
- "./back:/usr/src/app"
- "/usr/src/app/node_modules"

node_modules는 마운트 안시킨게 문제되는건 아니겠죠?

 

0

제로초(조현영)

location을 적으실 때 저렇게 적으면 문제가 될 수 있습니다. 반드시 path.join이나 path.resolve 메서드를 사용하셔야 합니다.

0

노른자

그렇군요! 바로 적용해보겠습니다

0

제로초(조현영)

보니까 keyfile을 config 폴더에 넣으시는 것 같은데 path.join으로 config 폴더 가리키시면 될 것 같네요.

0

제로초(조현영)

https://developoerty.tistory.com/125?category=853583

여기서는 privateKeyString 넣는 방법이 잘 나와있네요. `` 쓰시면 \n같은거 없이 하실 수 있습니다.

0

노른자

와 백틱으로 하니 됐습니다

[Nest] 76  - 02/05/2022, 3:15:56 PM   ERROR [ExceptionsHandler] done is not a function

TypeError: done is not a function

이제 이런에러가 뜨는거보니 validate쪽으로 넘어가나보네요!!

이것때문에 하루종일,,, 희열감과 허탈감이 공존하네요

정말 감사합니다 나머지는 혼자 할 수 있을것같습니다!

0

Soon Yeol Choi

안녕하세요. 위에 토론 내용을 보고 저도 애플 로그인을 구현중인데요. 

 

validate 부분에서 accessToken, refreshToken 값은 정상적으로 받아오는데요.

 

idToken 값은 "{}" 으로 받아와 지네요. 혹시 idToken 값을 불러와야 그 속에 사용자 계정 정보에 접근할 수 있을 것 같은데 이 부분에 대해서 어떻게 처리해야 될지 답변을 요청 드려도 될까요? 

 

미리 감사드리며 부탁드립니다. ^^ 

0

노른자

이거 passport-apple 이 nestjs랑 호환문제가 있습니다. node_modules/passport-apple  소스코드 안으로 들어가서 코드를 조금 수정해야합니다.

 

https://github.com/pekosong/passport-apple

이 소스코드로 수정해보시면 될겁니다.

0

Soon Yeol Choi

소중한 답변 감사드립니다. 참조해서 적용해보겠습니다. ㅠㅠ

0

Soon Yeol Choi

저는 이것때문에 며칠동안 골머릴 쌓는데 노른자 님 덕분에 해결할 수 있을것 같습니다. 다시 한번 감사드립니다. 꾸벅!!

0

Soon Yeol Choi

노른자 님 덕분에 저도 해결되었습니다. 감사드립니다. ㅠㅠ

0

노른자

몇주전 저를 보는듯하네요. 축하드립니다.

0

Soon Yeol Choi

넵넵 감사합니다. ^^

0

괴릿

혹시 어떻게 해결하셨는지 알수 있을까요? 위에 알려주신 깃헙 주소에 접근이 안되네요

0

이기적인 개발자

nestjs의 validation에서 id_token이 없는 경우

같은 문제로 다른 분들 도움 되실까봐 공유 드립니다 !

 저는 해당 문제를 passport-apple을 fork하여 해결하였습니다.

 

https://github.com/kiJu2/passport-apple-for-nestjs/commit/5982b9428820a748aeab8232e13930c305d132eb

@노른자님 감사드립니다. 덕분에 문제 해결에 큰 도움이 되었습니다 !

 

추가로, validation은 다음과 같이 작성하였습니다.

async validate(
    req: any,
    accessToken: string,
    results: {
      id_token: string;
      refresh_token: string;
      expires_in: number;
      access_token: string;
      token_type: string;
    },
    profile: any,
    done: (error: any, user?: any, info?: any) => void,
  )

강의자료는 어디서 다운받나요?

0

110

3

질문 있습니다.

0

294

3

코드 편집기 확장 프로그램

0

209

2

(질문)비밀 저장소에 접근하기 위한 인증 정보는 로컬 .env에 저장하는지?

0

143

2

(질문)외부 저장소를 통한 환경변수 불러오기 비동기 질문

0

166

3

로그인을 해도 LoggedInGuard쪽에서 false값이 나옵니다.

0

151

2

로그인방법이 고민됩니다.

0

190

2

yarn seed 명령어 실행 시 데이터 삽입 안됨

0

296

4

yarn run db:create 시에 발생하는 데코레이터 오류

0

242

2

npm run db:create 시에 발생하는 decorating 오류

0

231

2

RxJS 디버깅 질문 있습니다.

0

187

3

CacheManager에 대해 질문 있습니다.

0

173

2

로깅은 어떻게 하는게 효율적일까요?

0

222

1

CORS 질문 있습니다.

0

417

2

쿠키 옵션에 대해서 질문 있습니다.

0

184

2

로그아웃 요청이 403 forbidden 에러가 나는데 왜그런걸까요??

0

446

1

401 unauthorized문제

0

285

1

가드의 장점에 대해서 질문이 있습니다.

0

225

1

로그 관리에 대해 질문 있습니다.

0

251

2

CORS 에러 질문 있습니다.

0

318

2

배포 환경 DB 연결 질문 있습니다.

0

410

2

socket io 미 연결 문제 (nest & flutter)

1

1151

3

no elements in sequence 에러 관해서 질문이 있습니다.

0

453

1

start:dev-backup으로 돌리면 핫 리로딩이 되요 정상인가요?

0

319

1