[error] ID key value - type error
Error message
Argument of type '[{ clientID: string | undefined; clientSecret: string | undefined; callbackURL: string | undefined; scope: string[]; }]' is not assignable to parameter of type '[options: StrategyOptionsWithRequest] | [options: StrategyOptions] | [options: StrategyOptions] | [options: StrategyOptionsWithRequest]'.
Type '[{ clientID: string | undefined; clientSecret: string | undefined; callbackURL: string | undefined; scope: string[]; }]' is not assignable to type '[options: StrategyOptionsWithRequest]'.
Property 'passReqToCallback' is missing in type '{ clientID: string | undefined; clientSecret: string | undefined; callbackURL: string | undefined; scope: string[]; }' but required in type 'StrategyOptionsWithRequest'.
해결방법
요약하면, passport-google-oauth20 Strategy 생성자의 오버로드와 전달한 옵션 객체의 타입이 맞지 않아 발생한 타입스크립트 컴파일 오류이며, 핵심 원인은 process.env에서 읽은 값이 string | undefined로 추론되어 StrategyOptions가 요구하는 string과 불일치하고, 그 결과 타입 체크가 WithRequest 오버로드로 흘러가 passReqToCallback가 없다고 지적하는 상황입니다 stackoverflow+2.
즉, 옵션의 clientID/clientSecret/callbackURL을 확실한 문자열로 보장하거나, WithRequest 오버로드를 쓰려면 passReqToCallback을 명시하고 validate 시그니처를 맞춰야 합니다.stackoverflow+2
원인
-
passport-google-oauth20의 Strategy 생성자는 여러 오버로드를 가지며, 일반 옵션(StrategyOptions)에서는 clientID/clientSecret/callbackURL을 문자열로 요구합니다.passportjs
-
현재 코드는 process.env에서 값을 읽기 때문에 각 필드가 string | undefined로 추론되며, 이 상태로는 StrategyOptions에 맞지 않아 타입스크립트가 다른 오버로드(StrategyOptionsWithRequest)를 시도하다가 passReqToCallback 누락 오류를 내고 있습니다 stackoverflow+1.
해결 방법
-
가장 간단한 해결책은 각 환경변수를 “반드시 문자열”로 보이게 만드는 것으로, 널 단언 연산자나 기본값을 사용합니다(예: process.env.GOOGLE_OAUTH_CLIENT_ID ?? 'dev'). 이 방식은 동일 이슈를 해결한 사례로도 제시되어 있습니다.stackoverflow
-
혹시 요청 객체(req) 또는 Google의 추가 콜백 파라미터가 필요한 경우에는 옵션에 passReqToCallback: true를 추가하고, validate 시그니처를 req와 done(또는 Nest의 반환 방식)에 맞춰 조정합니다. passport-google-oauth20은 passReqToCallback 옵션을 공식적으로 제공하며, NestJS에서도 해당 패턴이 일반적으로 사용됩니다.stackoverflow+1
참고 코드
아래는 환경변수를 문자열로 보장하여 현재 오류를 제거하는 최소 수정 예시입니다 :passportjs+1
tsimport { Injectable } from '@nestjs/common' import { PassportStrategy } from '@nestjs/passport' import { Strategy } from 'passport-google-oauth20' @Injectable() export class GoogleStrategy extends PassportStrategy(Strategy, 'google') { constructor() { super({ clientID: process.env.GOOGLE_OAUTH_CLIENT_ID!, // 혹은 ?? 'dev' clientSecret: process.env.GOOGLE_OAUTH_CLIENT_SECRET!, // 혹은 ?? 'dev' callbackURL: process.env.GOOGLE_OAUTH_REDIRECT_URI!, // 혹은 ?? 'http://localhost:3000/auth/google/callback' scope: ['email', 'profile', 'openid'], }) } async validate(accessToken: string, refreshToken: string, profile: any) { const { id, name, emails, photos } = profile return { id, email: emails?.?.value, firstName: name?.familyName, lastName: name?.givenName, photo: photos?.?.value, } } }
req가 필요한 경우(예: id_token 등 콜백 파라미터 추가 활용) 다음처럼 passReqToCallback과 시그니처를 맞출 수 있습니다 :stackoverflow+1
tssuper({ clientID: process.env.GOOGLE_OAUTH_CLIENT_ID!, clientSecret: process.env.GOOGLE_OAUTH_CLIENT_SECRET!, callbackURL: process.env.GOOGLE_OAUTH_REDIRECT_URI!, scope: ['email', 'profile', 'openid'], passReqToCallback: true, }) // req, params(GoogleCallbackParameters) 등을 활용하려면 validate 시그니처 확장 async validate(req: Request, accessToken: string, refreshToken: string, profile: any) { // 필요 시 req 또는 추가 파라미터 사용 }
추가 체크
-
passport-google-oauth20 패키지의 옵션과 콜백 시그니처는 공식 문서의 정의를 따르므로, 문서 기준으로 StrategyOptions/WithRequest와 passReqToCallback의 의미를 확인하면 타입 오류 해석이 수월합니다.passportjs
-
id_token 등 OpenID 관련 정보를 원할 경우 scope에 openid를 포함하고, 필요 시 validate 시그니처와 옵션(passReqToCallback)을 조합해 콜백 파라미터를 수신하도록 구성할 수 있습니다.stackoverflow+1
핵심은 환경변수 타입을 확실한 문자열로 보이게 하여 StrategyOptions의 요구사항을 충족시키는 것이며, 그렇지 않으면 타입스크립트가 다른 오버로드로 분기하면서 passReqToCallback 누락 오류가 표면화된다는 점입니다.stackoverflow+2
- https://stackoverflow.com/questions/77697948/passport-js-google-oauth20-strategy-typescript-error
- https://www.passportjs.org/packages/passport-google-oauth20/
- https://stackoverflow.com/questions/71536002/how-to-get-id-token-from-passport-google-oauth20-package-nestjs
- https://github.com/nestjs/docs.nestjs.com/issues/75
- https://stackoverflow.com/questions/74970150/this-expression-is-not-constructable-error-passport-google-oauth-nodejs
- https://github.com/nestjs/passport/issues/57
- https://velog.io/@alsry922/Nest.js-passport%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-OAuth2.0-%EA%B5%AC%ED%98%84
- https://www.reddit.com/r/node/comments/1bc9y1h/oauth2_not_constructor_error_with/
- https://sectumsempra.tistory.com/136
- https://a3magic3pocket.github.io/posts/nestjs-google-oauth2/
- https://stackoverflow.com/questions/34738799/argument-of-type-x-is-not-assignable-to-parameter-of-type-x
- https://docs.nestjs.com/recipes/passport
- https://blog.cloneot.dev/development/nestjs-passport-google-oauth2
- https://github.com/nextauthjs/next-auth/issues/7569
- https://cdragon.tistory.com/entry/NestJS-Passport-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-feat-authentication
- https://dev.to/chukwutosin_/implement-google-oauth-in-nestjs-using-passport-1j3k
- https://jaylee-log.tistory.com/46
- https://velog.io/@do0ori/NestJS-OAuth-2.0-%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-3-Passport%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0
- https://velog.io/@samiosae/Argument-of-type-is-not-assignable-to-parameter-of-type-%EC%98%A4%EB%A5%98-%EB%A9%94%EC%8B%9C%EC%A7%80
- https://dev.to/andisiambuku/user-authentication-with-passport-js-and-jwt-in-nest-js-1ag3
- https://www.passportjs.org/packages/passport-jwt/
- https://lightrun.com/answers/nestjs-passport-extending-passportstrategy-does-not-take-provider-specific-oauth2-options-in-account
- https://stackoverflow.com/questions/70518459/nestjs-loosing-request-with-passport-jwt-strategy
- https://github.com/DefinitelyTyped/DefinitelyTyped/issues/51105
- https://stackoverflow.com/questions/tagged/passport-google-oauth?tab=newest&page=4
- https://velog.io/@haron/NestJS-Authentication
- https://stackoverflow.com/questions/tagged/nestjs-passport?tab=newest&page=2
- https://hs-archive.tistory.com/99
- https://www.reddit.com/r/node/comments/eeaw6c/passportgoogleoauth20_not_working_in_production/
- https://dev.to/tugascript/nestjs-authentication-with-oauth20-adding-external-providers-2kj
- https://www.inflearn.com/community/questions/434756/%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%A7%88%EB%AC%B8
- https://stackoverflow.com/questions/tagged/passport-google-oauth?tab=Active
- https://velog.io/@hoyahoya/PassportStrategy%EC%99%80-passReqToCallback
- https://orangebrother.dev/blog/%08nestjs-google-oauth-passport
- https://github.com/nestjs/passport/issues/1857
- https://stackoverflow.com/questions/72054260/error-cannot-set-headers-after-they-are-sent-to-the-client-when-using-passport
- https://velog.io/@do0ori/PassportStrategy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-auth-%EB%A1%9C%EC%A7%81-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0
- https://stackoverflow.com/questions/62671831/how-to-implement-multiple-passport-jwt-authentication-strategies-in-nestjs
- https://93960028.tistory.com/90
- https://velog.io/@dev_leewoooo/NestJs%EC%97%90%EC%84%9C-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%9D%B8%EC%A6%9D%EC%B2%98%EB%A6%AC-with-Passport
- https://93960028.tistory.com/19
- https://moca9012.tistory.com/540
- https://www.jsdocs.io/package/@nestjs/passport
학습 개념
핵심적으로 학습할 개념은 TypeScript의 함수 오버로드/튜플 인자 해석, 환경변수의 타입 안전성과 Non‑null 단언, passport-google-oauth20의 StrategyOptions/WithRequest와 passReqToCallback 의미, NestJS의 validate 시그니처 매핑, 그리고 OIDC scope(openid)/id_token 처리다.typescriptlang+4
이들을 이해하면 현재 오류의 원인(환경변수로 인해 옵션이 string | undefined가 되며 오버로드 해석이 WithRequest로 흘러가 passReqToCallback 요구)과 해결책을 구조적으로 설명할 수 있다 typescriptlang+2.
TS 오버로드·튜플 인자
-
Passport의 Strategy 생성자는 여러 오버로드를 갖고, TypeScript는 전달된 인자의 타입에 맞는 단 하나의 시그니처를 고른다.typescriptlang
-
인자가 튜플 형태로 해석되거나 유니온을 포함하면, 가장 구체적인 시그니처와 일치하지 않을 때 다른 오버로드로 분기되어 “해당 속성 누락” 같은 오류가 표면화될 수 있다.2ality+1
환경변수 타입과 Non‑null
-
process.env에서 읽은 값은 기본적으로 라서, StrategyOptions가 요구하는 순수 과 타입이 맞지 않는다 stackoverflow.
-
Non‑null 단언 연산자 로 “값이 반드시 존재한다”를 타입 체커에 알리거나, 런타임 스키마 검증(envalid/zod)으로 애플리케이션 시작 시 누락을 즉시 차단하는 방법이 있다.github+1
StrategyOptions vs WithRequest
-
passReqToCallback을 사용하지 않는 일반 옵션은 StrategyOptions로, clientID/clientSecret/callbackURL이 문자열이어야 한다.passportjs
-
요청 객체가 필요한 경우 passReqToCallback: true를 설정해 StrategyOptionsWithRequest를 사용하고, 이에 맞춰 콜백/validate 시그니처에 req를 포함해야 한다.passportjs+1
NestJS validate 시그니처
-
NestJS의 PassportStrategy는 passport의 verify 함수를 validate로 매핑하며, 선택한 옵션에 따라 시그니처가 달라진다(WithRequest면 req가 첫 번째 인자).nestjs
-
커뮤니티 예제에서도 StrategyOptionsWithRequest를 명시하고 validate(request, accessToken, …) 형태로 구현하는 패턴을 확인할 수 있다.github
OIDC scope와 id_token
-
Google에서 OpenID 정보를 원하면 scope에 openid를 포함해야 하며, id_token이 필요한 경우 콜백 파라미터 취급 또는 요청 객체 활용 방식에 주의해야 한다.stackoverflow
-
구현에 따라 profile 외 추가 토큰을 다루려면 passReqToCallback 사용이나 콜백 시그니처 확장이 요구될 수 있다.passportjs+1
안전한 환경설정 베스트 프랙티스
-
envalid로 필수 ENV를 스키마로 정의·검증하여 타입과 존재를 보장하면 컴파일/런타임 간 불일치를 예방할 수 있다.github
-
zod로 ENV 스키마를 만들고 parse/safeParse로 검증한 뒤, 검증된 값을 앱 전역에서 사용하도록 타입을 연결하는 접근도 유효하다.dev
왜 오류가 났는가(요약 개념 연결)
-
ENV가 라 오버로드 해석이 일반 StrategyOptions에 맞지 않았고, 그 결과 WithRequest 오버로드로 시도되며 passReqToCallback 누락을 지적했다는 점이 핵심이다 stackoverflow+1.
-
따라서 옵션을 확실한 문자열로 만들거나(passReqToCallback 없이), WithRequest를 쓴다면 passReqToCallback 및 validate 시그니처를 맞추면 된다.stackoverflow+1
- https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html
- https://www.typescriptlang.org/docs/handbook/2/functions.html
- https://www.passportjs.org/packages/passport-google-oauth20/
- https://docs.nestjs.com/recipes/passport
- https://stackoverflow.com/questions/71536002/how-to-get-id-token-from-passport-google-oauth20-package-nestjs
- https://stackoverflow.com/questions/77697948/passport-js-google-oauth20-strategy-typescript-error
- https://2ality.com/2025/01/typescript-tuples.html
- https://github.com/microsoft/TypeScript/issues/14107
- https://stackoverflow.com/questions/45194598/using-process-env-in-typescript
- https://github.com/af/envalid
- https://www.passportjs.org/packages/passport-google-oauth2/
- https://github.com/nestjs/docs.nestjs.com/issues/75
- https://dev.to/schead/ensuring-environment-variable-integrity-with-zod-in-typescript-3di5
- https://hwani.dev/ts-non-null-assertion-operator/
- https://velog.io/@qhflrnfl4324/Non-null-assertion-operator-TypeScript
- https://inpa.tistory.com/entry/TS-%F0%9F%93%98-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%8A%90%EB%82%8C%ED%91%9C-Non-null-%EB%8B%A8%EC%96%B8-%EC%97%B0%EC%82%B0%EC%9E%90
- https://learntypescript.dev/07/l2-non-null-assertion-operator
- https://stackoverflow.com/questions/70914096/how-to-export-and-overload-function-argument-list-with-tuples-of-different-lengt
- https://toby2009.tistory.com/61
- https://rainbowcode.tistory.com/359
- https://norwayy.tistory.com/366
- https://velog.io/@dayeong0120/%EC%86%8C%EC%85%9C%EB%A1%9C%EA%B7%B8%EC%9D%B8-passport-%EC%BD%94%EB%93%9C-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0
- https://www.typescriptlang.org/docs/handbook/2/everyday-types.html
- https://instil.co/blog/crazy-powerful-typescript-tuple-types/
- https://www.npmjs.com/package/@types/passport-google-oauth2
- https://stackoverflow.com/questions/74383150/jsdoc-non-null-assertion
- https://jsdev.space/howto/env-ts-zod/
- https://blog.foujeupavel.com/manage-your-node-js-env-file-with-envalid-a-clean-way-to-access-your-secrets-keys-in-prod/
- https://www.youtube.com/shorts/jNdJhEDiK6Q
- https://www.npmjs.com/package/envalid/v/7.3.0
- https://douglasmoura.dev/en-US/validate-your-environment-variables-with-zod
- https://dev.to/yuvraj_raghuvanshi_1279d6/simplify-environment-variable-validation-in-nodejs-with-env-valid-58jh
- https://catalins.tech/validate-environment-variables-with-zod/
- https://jfranciscosousa.com/blog/validating-environment-variables-with-zod
- https://www.youtube.com/watch?v=lVHHpm4kuhc
- https://www.hojinlee.dev/posts/zod-environment-variables-validation/
- https://creatures.sh/blog/env-type-safety-and-validation/
댓글
댓글 쓰기