[Nodemailer] 일반 gmail 계정으로 2LO authentication을 구성했을 때 unauthorized_client 에러가 발생하는 이유

서해빈·2021년 6월 29일
0

Trouble

목록 보기
15/15

기존 3LO authentication 구성의 문제점

기존에는 3-legged OAuth2 authentication 구성으로 nodemailer transporter를 생성했었다. 이는
=> 사용자 동의 => 사용자 데이터 획득의 흐름으로 진행되는데 이 사용자 동의를 위해서는 로그인이 필요했다. 하지만 서버에서 메일을 발송할 때마다 사용자 동의를 구할 수 없으므로 Nodemailer에서는 access token 혹은 refresh token을 발급받았다는 전제하에 이를 이용해 로그인 단계를 생략하고 메일을 발송하게 했다.

OAuth 2.0 프로토콜 흐름

이를 위해 google OAuth 2.0 Playground을 통해 발급받은 토큰을 사용했는데, 최근 refresh token이 만료되었다는 에러를 확인했다. 매번 이렇게 수동적으로 토큰을 발급받아 갱신해줄 수 없다는 생각이 들었고, 토큰을 자동으로 발급받을 수 있는 방법을 찾게 되었다.

// 문서의 3-legged OAuth2 authentication 예제 코드
let transporter = nodemailer.createTransport({
    host: 'smtp.gmail.com',
    port: 465,
    secure: true,
    auth: {
        type: 'OAuth2',
        user: 'user@example.com',
        clientId: '000000000000-xxx0.apps.googleusercontent.com',
        clientSecret: 'XxxxxXXxX0xxxxxxxx0XXxX0',
        refreshToken: '1/XXxXxsss-xxxXXXXXxXxx0XXXxxXXx0x00xxx', // optional
      	// Required only if refreshToken is not available  
      	accessToken: 'ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x',
        expires: 1484314697598
    }
});

2LO authentication 구성

service accounts를 사용하면 알아서 access token을 생성할 수 있음을 공식 문서 2LO authentication (service accounts)에서 확인할 수 있었다.

2LO authentication (service accounts)

Nodemailer also allows you to use service accounts to generate access tokens.

// 문서의 2-legged OAuth2 authentication 예제 코드
let transporter = nodemailer.createTransport({
    host: 'smtp.gmail.com',
    port: 465,
    secure: true,
    auth: {
        type: 'OAuth2',
        user: 'user@example.com',
        serviceClient: '113600000000000000000',
        privateKey: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...',
        accessToken: 'ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x', // optional
        expires: 1484314697598
    }
});

이를 위해 구글 개발자 문서의 서버 간 애플리케이션에 OAuth 2.0 사용을 참고해 룰루랄라 service account를 생성했다.

그런데 서비스 계정에 도메인 전체 권한 위임 파트의 1번 항목에서 문제가 생겼다.

서비스 계정에 도메인 전체 권한 위임
...중략...

  1. Google 작업 공간 도메인의 관리 콘솔에서 기본 메뉴 menu > 보안> API 제어로 이동 합니다.
  2. ....

오잉...?

혹시나 싶어 이 부분을 생략하고 생성을 마친 뒤, client_id와 private_key를 사용해 테스트해봤지만....

예상은 빗나가지 않았다

Error: unauthorized_client: Client is unauthorized to retrieve access tokens \
using this method, or client not authorized for any of the scopes requested.

Service accounts only work with GSuite

stackoverflow에서 해당 에러에 대한 글을 찾아볼 수 있었다. 결론부터 말하자면 일반 계정은 안되고 오직 GSuite 계정만 service account 기능을 사용할 수 있다고 한다.

Service accounts only work with GSuite because you have to be able to preauthorize the service account and grant it access to the users account. There is no way to preauthorize a normal user Gmail account. So no you cant use a service account with a normal users gmail account.

출처: Client is unauthorized to retrieve access tokens using this method Gmail API C# - 바로가기

여기서 preauthorize에 대해 짧게 이야기하자면 service account는 사용자 동의를 통해 실시간 인가를 받는 것이 아니라, service account email address를 사용함으로써 access가 미리 인가된다고 한다.

With a service account access is pre-authorized by taking the service account email address and adding it as a user for data in question.

출처: When Service Account should be used to access google api? - 바로가기

결론

  1. GSuite 계정 생성 및 결제 후 재시도
  2. 기존의 3LO Authentication을 롤백

아직은 프로토타입 단계이므로 3LO Authentication을 사용해 마무리 해야겠다.

참고 및 출처

  1. [Nodemailer] 2LO authentication (service accounts) - 바로가기
  2. [Google developer docs] 서버 간 애플리케이션에 OAuth 2.0 사용 - 바로가기
  3. [stackoverflow] Client is unauthorized to retrieve access tokens using this method Gmail API C# - 바로가기
  4. [stackoverflow] When Service Account should be used to access google api? - 바로가기

0개의 댓글