本文へ移動
ブログに戻る

Gmail OAuth2への対応: 一つのフローで二つのプロバイダー

4分で読めます

v1.2.0でMicrosoftのOAuth2に対応したときの狙いははっきりしていました。アプリパスワードを生成する代わりに、既存のMicrosoftアカウントでサインインできるようにすることです。Outlook、Hotmail、M365のアカウントではうまく動きました。次の一歩として自然だったのがGmail — 世界でもっとも使われているメールプロバイダーです。

OAuth2が重要な理由

Googleは何年もかけてIMAPへのアクセスを絞ってきました。2FAを有効にしているなら(そうすべきです)、第三者のメールクライアントを使うにはApp Passwordが必要です。アプリパスワードは動きますが、分かりにくく、忘れやすく、回り道のように感じられます。OAuth2なら、「Sign in with Google」を押し、ブラウザーで認証して、アプリに戻ってくるだけです — パスワードを写すことも、設定のページを探し回ることもありません。

実装

MailVaultにはすでに、Rustで書かれたMicrosoft向けのOAuth2 PKCEのフローがありました。構成は3つの部分からなります。ブラウザーでのログインのあとリダイレクトを受け取る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)を、IMAPとSMTPへの完全なアクセスを与えるhttps://mail.google.com/のスコープとともに使います。

コードで変わったこと

Rustの側には、Microsoftを前提にするのではなく、提供元ごとのパラメーター(認可のURL、トークンのURL、スコープ、クライアントID)を受け取る新しいoauth2_startのコマンドが必要でした。コールバックのサーバーとPKCEの処理はそのままです — 提供元が誰であれ、OAuth2はOAuth2です。

フロントエンドでは、AccountModalのコンポーネントがGmailのアカウントに対して「Sign in with Google」のボタンを表示するようになりました(従来の「Use App Password instead」の代替も残しています)。ボタンは同じOAuth2のフローを起動し、違うのはMicrosoftではなくGoogleのエンドポイントとスコープを使うところだけです。認証のあと、アクセストークンはIMAP(imap.gmail.com)とSMTP(smtp.gmail.com)の双方でXOAUTH2 SASLとともに使われます。

トークンの更新もMicrosoftとまったく同じです。アクセストークンの期限が切れると(通常は1時間後)、MailVaultは保存してあるリフレッシュトークンを使って自動的に更新します。Googleアカウントの設定からアクセスを取り消さない限り、ユーザーが再認証する必要はありません。

結果

Gmailのユーザーは、ボタンを1つ押してGoogleの通常のログインページでサインインすれば、すぐにMailVaultを使い始められます。アプリパスワードも、探すべきIMAPの設定も、「安全性の低いアプリ」の切り替えもありません。二要素認証、Google Workspace、個人のGmailアカウントのいずれでも動作します。

この同じOAuth2の基盤は、標準のOAuth2に対応する将来のどのプロバイダーにも使えます — Rustのモジュールは完全にプロバイダー非依存です。