Gmail OAuth2 추가하기: 하나의 흐름, 두 개의 제공업체
v1.2.0에서 Microsoft OAuth2를 내놓을 때 목표는 분명했습니다. 앱 비밀번호를 만들지 않고도 기존 Microsoft 계정으로 로그인하게 하는 것이었습니다. Outlook, Hotmail, M365 계정에서 잘 작동했습니다. 다음 차례는 자연스럽게 Gmail이었습니다 — 세계에서 가장 많이 쓰이는 이메일 제공업체입니다.
OAuth2가 중요한 이유
Google은 여러 해에 걸쳐 IMAP 접근을 조여 왔습니다. 2단계 인증을 켜 두었다면(켜야 합니다) 제3자 메일 클라이언트를 쓰려면 앱 비밀번호가 필요합니다. 앱 비밀번호는 작동하기는 하지만 찾기 어렵고, 잊기 쉽고, 우회책처럼 느껴집니다. OAuth2를 쓰면 "Google로 로그인"을 눌러 브라우저에서 인증하고 앱으로 돌아오면 됩니다 — 비밀번호를 옮겨 적을 일도, 설정 페이지를 뒤질 일도 없습니다.
구현 방식
MailVault에는 Rust로 쓴, Microsoft용으로 이미 작동하는 OAuth2 PKCE 흐름이 있었습니다. 구조는 세 부분입니다. 브라우저 로그인 뒤 리디렉션을 받는 localhost:19876의 로컬 TCP 콜백 서버, 인가 코드를 액세스/리프레시 토큰으로 바꾸는 토큰 교환 함수, 그리고 IMAP과 SMTP를 위한 XOAUTH2 SASL 인증입니다.
Google을 추가한다는 것은 이 모두를 제공업체에 구애받지 않게 만든다는 뜻이었습니다. Microsoft의 엔드포인트를 코드에 박아 두는 대신, Rust OAuth2 모듈이 실행 시점에 제공업체 설정을 받도록 리팩터링했습니다 — 인가 URL, 토큰 URL, 스코프, 클라이언트 ID를 모두 흐름을 시작할 때 프런트엔드에서 넘겨받습니다.
클라이언트 ID는 Microsoft 때와 같은 방식을 따랐습니다. 널리 알려진 공개 클라이언트 ID를 쓰는 것입니다. Mozilla의 오픈소스 메일 클라이언트인 Thunderbird는 Google과 Microsoft 양쪽에 공개 OAuth2 클라이언트를 등록해 두었습니다. MailVault는 Thunderbird의 Google 클라이언트 ID(406964657835-aq8lmia8j95dhl1a2bvharmfk3t1hgqj.apps.googleusercontent.com)를 https://mail.google.com/ 범위와 함께 쓰며, 이 범위는 IMAP과 SMTP 전체 접근을 허용합니다.
코드에서 바뀐 것
Rust 쪽에는 Microsoft를 전제하는 대신 제공업체별 매개변수(인증 URL, 토큰 URL, 범위, 클라이언트 ID)를 받는 새 oauth2_start 명령이 필요했습니다. 콜백 서버와 PKCE 로직은 그대로 두었습니다 — 제공업체가 무엇이든 OAuth2는 OAuth2입니다.
프런트엔드에서는 AccountModal 컴포넌트가 이제 Gmail 계정에 "Sign in with Google" 버튼을 보여 줍니다(기존의 "Use App Password instead" 대체 경로와 나란히). 이 버튼은 Microsoft 대신 Google의 엔드포인트와 범위를 쓸 뿐 같은 OAuth2 흐름을 시작합니다. 인증이 끝나면 IMAP(imap.gmail.com)과 SMTP(smtp.gmail.com) 양쪽에 액세스 토큰을 XOAUTH2 SASL로 씁니다.
양쪽에서 XOAUTH2 SASL과 함께 사용합니다. 토큰 갱신은 Microsoft와 동일하게 작동합니다. 액세스 토큰이 만료되면(보통 한 시간 뒤) MailVault가 저장해 둔 리프레시 토큰으로 자동 갱신합니다. Google 계정 설정에서 접근 권한을 직접 취소하지 않는 한 다시 인증할 일은 없습니다.
결과
이제 Gmail 사용자는 버튼 하나를 누르고 Google의 일반 로그인 페이지에서 로그인한 다음, 곧바로 MailVault를 쓸 수 있습니다. 앱 비밀번호도, 찾아 넣어야 할 IMAP 설정도, "보안 수준이 낮은 앱" 스위치도 없습니다. 2단계 인증, Google Workspace, 개인 Gmail 계정 모두에서 작동합니다.
같은 OAuth2 기반은 이제 표준 OAuth2를 지원하는 어떤 제공업체에도 그대로 쓸 수 있습니다 — Rust 모듈은 완전히 제공업체 중립적입니다.