はじめに
Googleから「Migrate your OAuth out-of-band flow to an alternative method before Oct. 3, 2022」のメールが届きました。
Googleで発行するOAuth2認証に関する内容のようです。
DjangoでWebアプリケーションを作るときにメール送信を行う機能を実装することがあります。
これまでは、デスクトップアプリとして作成してflow.run_console()でトークン生成のURLを生成して、他のパソコンでトークンを発行していましたが禁止になったようです。
新規に認証情報を作成しても上記の方法ではトークンを発行時にエラーになります。
代わりにflow.run_local_server(port=0)を使い、Webアプリケーションを実行するサーバ上でトークンを生成すれば良いようです。
今回、このためだけにサーバにGUI環境をインストールしました。
環境
Debian bullseye 64bit Python 3.10.2
GUIのインストール
リモートデスクトップで行うのでxrdpもインストールします。
完了後にOSをrebootします。
sudo apt install task-gnome-desktop sudo apt install xrdp tigervnc-standalone-server sudo systemctl enable xrdp
Debian11の場合、デスクトップ環境をインストールするとスリープ設定が有効(20分)になり、アクセスできなくなるので無効化します。
スクリプト
下記のスクリプトは、指定した宛先にメールを送信するスクリプトです。
トークンを発行するためにGoogle Cloud Consoleでプロジェクトを作成、jsonファイルをダウンロードしてcredentials.jsonにファイル名を変更後にスクリプトと同じディレクトリに保存します。
リモートデスクトップで作成したアプリケーションを実行するサーバにログインします。
下記のスクリプトのOAuth認証情報を作成したGoogleアカウント、メールの差出人、メールの宛先を修正してから、ターミナルでスクリプトを実行します。
python スクリプト名
flow.run_local_server()を使うと自動でWebブラウザが起動します。
flow.run_console()を使っても、サーバ上でWebブラウザを起動してから表示されたURLを貼り付ければトークンが発行されます。
Googleにもサンプルがあります。

import base64 import os from email.mime.text import MIMEText from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build from googleapiclient.errors import HttpError API_SERVICE_NAME = 'gmail' API_VERSION = 'v1' SCOPES = ['https://www.googleapis.com/auth/gmail.send'] CLIENT_SECRETS_FILE = '/home/hoge/credentials.json' TOKEN_PICKLE_FILE = '/home/hoge/token.pickle' def get_credential(): creds = None if os.path.exists(TOKEN_PICKLE_FILE): creds = Credentials.from_authorized_user_file(TOKEN_PICKLE_FILE, SCOPES) if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES) # Webブラウザが起動して認証する creds = flow.run_local_server(port=0) # コンソールにURL表示 #creds = flow.run_console() with open(TOKEN_PICKLE_FILE, 'w') as token: token.write(creds.to_json()) return creds def create_message(_subject, _from, _msg, _to): message = MIMEText(_msg) message['from'] = _from message['to'] = _to message['subject'] = _subject encode_message = base64.urlsafe_b64encode(message.as_bytes()) return {'raw': encode_message.decode()} def main(): creds = get_credential() #service = build("gmail", "v1", credentials=creds, cache_discovery=False) #service = build('gmail', 'v1', credentials=creds) service = build(API_SERVICE_NAME, API_VERSION, credentials=creds, cache_discovery=False) try: result = service.users().messages().send( # OAuth認証情報を作成したGoogleアカウント userId = 'test@abc.jp', # 第1引数:Subject 第2引数:メール差出人 第3引数:メール委宛先 body = create_message('test', 'from@abc.jp', 'message', 'to@abc.jp') ).execute() except errors.HttpError as error: print('An error occurred: %s' % error) if __name__ == '__main__': main()
CUIに設定
トークン生成後はGUIは不要なので、CUIに切り替えます。
sudo systemctl set-default multi-user.target
GUIに切り替える場合は、下記となります。
sudo systemctl set-default graphical.target
Comments