webdata
DX推進をサポートする技術者向け情報提供サイト

技術者向け技術情報

TOP >技術者向け技術情報 > o365のOAuth2認証によるメール受信

【PHP】o365のOAuth2認証によるメール受信

 2023-12-24 (更新日:2025-01-11)
 OAuth2の認証よる0365メール受信の必要があり、ネットで手法を探したのですがなかなかうまくいかず、思考錯誤の結果下記の手法に落ち着きました。 概要図にもあるように、認証はpythonで行いメール受信をOAuth2対応のFetchMail7を使い、受信したメールをメールサーバ(devocot)に保存させ、 そのメールサーバにPop3でphpで受信させる手法となってます。
<概要図>
   
<利用インフラ>
 OS:almaLinux9.5
  ※almaLinuxは9.5以上が必須。
 php:php8.3
<ソフトウェア>
 python3.9
 fetchmail7
 devocot
 postfix

実装手順を記載します。
<設定の流れ>
・OS上の準備
・pythonのインストール
・token取得とプログラム作成
・postfixのインストール
・ssl証明書の入手と配置
・fetchmail7のインストールと設定
・dovecotのインストールと設定
・php受信プログラムへの反映

OS上の準備

・受信するユーザを作成によりメール受信BOXのフォルダーが生成、OSのアップデート
					# groupadd グループ名
					# useradd -g グループ名 アカウント名
					# passwd アカウント名
						********
						********

					//受信BOXのフォルダーの権限変更
					# chmod 600 /var/mail/アカウント名  //受信メールはこのファイルに格納されていきます。

					//OSのupdate
					# yum update		

ツールの準備

					//automakeのインストール
					# dnf install automake

					//wgetインストール
					# dnf -y install wget

					//perlインストール
					# dnf install perl	

pythonのインストールと準備

pythonにてOAuth2の認証を行います。
・インストール済みのpythonのアンイストール、必要ツールとpythonのインストール
					//python3 のアンインストール
					# yum erase python
					# yum erase python3

					//ツールのインストール
					# dnf install openssl-devel libffi-devel bzip2-devel -y 
					# dnf groupinstall "Development Tools"
					# gcc --version    	gccインストール確認
					  出力例)gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-2)
					//gccがない場合
					# dnf install gcc

					//python3.9のインストール
					# dnf install python39
					# python3 --version   バージョン確認
					  出力例)Python 3.9.19

					//pipのアップデート					
					# curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
					# python3 get-pip.py
					# yum remove python3-requests
					# pip3.9 install o365
					# vi /usr/local/lib64/python3.6/site-packages/O365/connection.py
					447行目ぐらいを下記のようにコメントアウトを記述しリダイレクト先を記述する
					#self.oauth_redirect_url = 'https://login.microsoftonline.com/common/oauth2/nativeclient'
					self.oauth_redirect_url = 'https://ドメインもしくはIPアドレス/PHPMailer/get_oauth_token.php'
					
					//oauthlib3.2.0のインストール
					# pip install oauthlib==3.2.0

					//pipのモジュールインストール
					# pip3.9 install urllib3==1.26.6

					//トークンの格納先作成
					# mkdir /root/oauth2
					# mkdir /root/oauth2/アカウント名/
					

OAuth2認証token取得プログラム作成

<サンプルプログラム> get_token.py
 TENANT等の各IDは情シス管理者に確認し環境に合わせ記述変更してください。
					#!/usr/bin/env python
					# -*- coding: utf-8 -*-
					# BSD 2-clause

					from O365 import Account, MSGraphProtocol, FileSystemTokenBackend

					# TENANT等の各IDは環境に合わせ記述変更ください。
					TENANT_ID = '****7558-3**5-48**-**ac-0809******a6'			  //テナントIDを記述
					CLIENT_ID = '****7f62-9c16-49e9-b5f4-******921bd1'       //クライアントIDを記述
					CLIENT_SECRET = '****Q~v2VSq.ZF9z*****E_1b******~OTJ9J**P'  //クライアントシークレット値

					TOKEN_FILENAME='token.json'
					TOKEN_PATH='.'

					AUTH_FLOW='authorization'
					SCOPES =  ['offline_access', 'https://outlook.office365.com/POP.AccessAsUser.All', 'https://outlook.office365.com/IMAP.AccessAsUser.All', 'https://outlook.office365.com/SMTP.Send']

					token_backend = FileSystemTokenBackend(token_path=TOKEN_PATH, token_filename=TOKEN_FILENAME)
					credentials=(CLIENT_ID, CLIENT_SECRET)
					account = Account(credentials=credentials, scopes=SCOPES, token_backend=token_backend, auth_flow_type=AUTH_FLOW, tenant_id=TENANT_ID)

					if not account.is_authenticated:
						account.authenticate()
						print('Authenticated!')
					else:
						account.connection.refresh_token()
						print('Refreshed!')

					with open('access_token', mode='w') as f:
						f.write(token_backend.get_token()['access_token'])
					
<解説>
下記を環境に合わせ記述を変更
 8行目 TENANT_ID = 'テナントID'
 9行目 CLIENT_ID = 'クライアントID'
 10行目 CLIENT_SECRET = 'クライアントシークレット値'
/root/oauth2/アカウント名/ 直下にget_token.pyを配置する

tokenの生成

					//トークンの生成(初期化) ※最初は処理は必須
					# cd /root/oauth2/アカウント名
					# python3.9 ./get_token.py
					Visit the following url to give consent:
					https://login.microsoftonline.com/****7558-3**5-48**-**ac-0809******a6/
					oauth2/v2.0/authorize?response_type=code&client_id=****7f62-9c16-49e9-
					b5f4-******921bd1&redirect_uri=https%3A%2F%2F10.47.128.13%2FPHPMailer
					%2Fget_oauth_token.php&scope=https%3A%2F%2Foutlook.office365.com%2FSMTP
					.Send+https%3A%2F%2Foutlook.office365.com%2FPOP.AccessAsUser.All+offline_access
					+https%3A%2F%2Foutlook.office365.com%2FIMAP.AccessAsUser.All&state
					=gw**********ptiQ7WgdcMasbpX7DX0g&access_type=offline
					Paste the authenticated url here:
					このように表示されます。
					5〜10行目に表示されたURL https://login.microsoftonline.com/ 〜 &access_type=offline までの7行を
					コピーして任意のパソコンのブラウザのアドレス欄にペーストして接続します。
					接続後リダイレクトされブラウザのアドレス欄に新しいURLが表示されます。
					この新しくリダイレクトされたURLをコピーして
					Paste the authenticated url here: にペーストすると下記にように認証されます。
					Authentication Flow Completed. Oauth Access Token Stored. You can now use the API.
					Authenticated!

					この認証により/root/oauth2/アカウント名 配下にファイルaccess_tokenとtoken.jsonのトークンが生成されます。

					//crontabを設定 ※10分毎にトークンの更新を行う。
					1,11,21,31,41,51  *  *  *  *  root cd /root/oauth2/アカウント名/; python3 get_token.py > /dev/null 2>&1
					
<解説>
 上記ブラウザ認証時にマイクロソフトから0365の認証確認があります。その際はテナントIDに属しているID(例:操作者のアカウント)でもOKです。 もし、TENANT_IDやCLIENT_ID、CLIENT_SECRETの記述に間違いがあると0365の認証もないままマイクロソフト社によりアラートが表示されます。
認証が通った場合でも下記のPHPのエラーがブラウザに表示される場合がありますがこれは認証に問題ありません。無視してください。

  

postfixのインストール

					# dnf install postfix
					/etc/postfix/main.cfの編集 下記を最終行に追記
					 message_size_limit = 204800000       //200MB
					 mailbox_size_limit = 10000000000   //メールBOXのファイルサイズ(重要)10GB
                      //過去のメール受信で容量オーバー時配信不能メールが送信される。
                      //受信するメールBOXのサイズに合わせて設定 # systemctl start postfix     //サービスの起動 # service postfix reload      //設定の適用 設定変更時 # systemctl enable postfix        //サービスの自動起動設定

ssl証明書の入手と配置

fetchmailが利用するssl証明書(o365、DigiCertのルート、DigiCertのグローバルルートCA)が必要となります。
<入手> ・o365
 ブラウザよりhttps://outlook.office365.com/にアクセスしサインインを行う。
 https://outlook.office365.comにリダイレクトされ自分のメールが表示されます。
 表示後、URLの直ぐ左にある「サイト情報を表示」のアイコンをクリックしここから証明書をエキスポートします。
 「この接続は保護されてます」ー「証明書は有効です」ータブ「詳細」を順にクリック
 証明書フィールドの下段(下から2段目)の「証明書」をクリックしフィールド値を表示させます。
 その下の「エクスポート(X)」ボタンのクリックによりダウンロードできます。 
 ダウンロードされたファイル名を識別しやすいようにoutlook.comをo365.pemに変更します。(ファイル名は任意)
・DigiCertのルート
 ブラウザよりhttp://cacerts.digicert.com/DigiCertGlobalRootCA.crt をダウンロード
 ダウンロードしたファイルをpem形式に変換
 openssl x509 -inform der -in DigiCertGlobalRootCA.crt -out DigiCertGlobalRootCA.crt.pe (ファイル名は任意)
・DigiCertのグローバルルートCA
 ブラウザよりhttp://cacerts.digicert.com/DigiCertCloudServicesCA-1.crt をダウンロード
 ダウンロードしたファイルをpem形式に変換
 openssl x509 -inform der -in DigiCertCloudServicesCA-1.crt -out ca.pem (ファイル名は任意)
<証明書の配置>
 /root/oauth2/fetchmail/certs/のディレクトリーを作成しこの3つの証明書を配置します。
<証明書のシンボリックを作成>
 fecthmailの場合、直接証明書ファイルを見てくれないのでシンボリックを作成します。
 シンボリック作成のため下記をインストールします。
 # dnf install openssl-perl
 # dnf install /usr/bin/c_rehash
 # cd /root/oauth2/fetchmail/certs
 シンボリック作成コマンド
 # openssl rehash /root/oauth2/fetchmail7/certs
 # c_rehash シンボリックの反映

fetchmailのインストール

					//ツールautoconf-2.71インストール
					# cd /root/oauth2
					# wget --no-check-certificate https://ftp.gnu.org/gnu/autoconf/autoconf-2.71.tar.gz
					# tar zxvf autoconf-2.71.tar.gz
					# cd autoconf-2.71
					# ./configure
					# make 
					# make install
					# cd /root/oauth2

					//ツールbison3.8をインストール
					# wget --no-check-certificate https://ftp.gnu.org/gnu/bison/bison-3.8.tar.gz
					# tar zxvf bison-3.8.tar.gz
					# cd bison-3.8
					# ./configure
					# make
					# make install
					# cd /root/oauth2

					//fetchmail7のインストール
					# git clone https://gitlab.com/fetchmail/fetchmail.git
					# cd fetchmail
					# git checkout next
					# ./autogen.sh
					# ./configure --with-ssl
					# make
					# make install
					# cd /root/oauth2

					/root の直下に.fetchmailrc7 を作成
					記述内容は環境に合わせサンプルファイルの記述を変更する
					  ※サンプルファイル、証明書は下記に記述
					/root/.fetchmailrc7 のパーミッションを700に変更
					# chmod 700 /root/.fetchmailrc7

					//Fetchmail7起動
					# /usr/local/bin/fetchmail -f /root/.fetchmailrc7
					※下記警告がでますが問題なく稼働しております。
					12月 19 14:54:16 fetchmail: 警告: root アカウントでの実行はお勧めできません。
					fetchmail:/root/.fetchmailrc7:24: アカウント WARNING: ssl is obsolescent. Pleas  use tlsmode wrapped instead. , サーバ ssl
					fetchmail: unable to access /root/oauth2/アカウント名/access_token: そのようなファイルや ディレクトリはありません

					※重要:サーバを再起動した場合、自動でFetchmail7起動できません。(OSに登録できなできない)
# /usr/local/bin/fetchmail -f /root/.fetchmailrc7 //都度コマンドで起動のこと
//起動確認 # ps -aux /root/oauth2/fetchmail/fetchmail -f /root が表示されていればOK

設定ファイル作成

<サンプルプログラム> .fetchmailrc7 コピペして記述変更してください。 ファイル名先頭の.忘れずに
					set daemon 600 # 600秒間隔でメールチェックを行なう
					set postmaster root # 最終的なメールの送信先
					set no bouncemail # エラーメールをpostmasterに送る
					set syslog

					# 全サーバー共通デフォルト設定
					defaults
					bad-header accept # ヘッダ異常のあるメールも取り込む
					protocol auto
					uidl
					no mimedecode
					no flush
					no fetchall # 未読メールのみ取り込む場合
					#fetchall # 既読・未読にかかわらず全てのメールを取り込む場合
					keep # 取り込んだメールをサーバー上に残しておく場合
					fetchsizelimit 0

					# プロバイダアカウント宛メール取り込み設定※POP3Sが提供されている場合
					poll outlook.office365.com # プロバイダ受信メールサーバー名
					protocol pop3
					port 995
					auth oauthbearer username メールアカウント
					passwordfile /root/oauth2/アカウント名/access_token
					is sis here keep
					ssl
					sslcertck
					sslcertpath '/root/oauth2/fetchmail/certs'
					
<解説>
 このファイルはfetchmailの受信するアカウントの設定ファイルです。このファイルを/rootの配下に配置して下さい。
 /root/.fetchmailrc7 のパーミッションを700に変更
 # chmod 700 /root/.fetchmailrc7

dovecotのインストール

					//dovecotのインストール
					https://www.sakura-vps.net/centos7-setting-list/dovecot-settings-for-sakura-vps-centos7/に従い設定
					# yum -y install dovecot
					各設定ファイルの編集
					/etc/dovecot/dovecot.conf 編集
					/etc/dovecot/10_mail.con 編集
						10_mail.confのみ下記追加で設定(32行目)
						mail_location = mbox:~/mail:INBOX=/var/mail/%u
					/etc/dovecot/10-auth.conf
					/etc/dovecot/10-ssl.conf
					dovecotの起動と自動起動設定
					# systemctl start dovecot
					# systemctl enable dovecot
					firewallの設定
					# firewall-cmd --permanent --zone=public --add-port=25/tcp
					# firewall-cmd --permanent --zone=public --add-port=587/tcp
					# firewall-cmd --permanent --zone=public --add-port=110/tcp
					# firewall-cmd --permanent --zone=public --add-port=143/tcp
					# firewall-cmd --reload
					

受信プログラム

 このあとは、本サイトのメール受信に従ってdovecotよりメール受信できます。
<変数・定数(メールアカウント)>
 $mail_host = 'dovecotをインストールしたサーバドメインもしくはIPアドレス';
        //利用している送信サーバを指定
 $port = "995";    //ポート番号
 $user = 'OSの準備で設定したアカウント名@sample.jp';  //認証ユーザアドレス
 $pass = 'OSの準備で設定したパスワード';         //認証ユーザパスワード
 $pop = new Net_POP3();

既知の不具合と対処について

不具合事象として、認証がうまくできないことがあります。原因は不明です。その際fatchmailがo365の認証ができないとアラートメールが送信されます。 このメールはdovecotのメールBOXに登録されるのでこのアラートメールはPHPで受信ができます。

・エラーメール件名
fetchmail authentication failed
この件名のメールが受信された場合、認証エラーとして識別できるようプログラムを作っていくことが必要です。

・対処方法
認証の初期化を行います。認証トークン(access_token、token.json)を削除します。
# cd /root/oauth2/アカウント名
# rm -f access_token token.json
次に、tokenの生成に記載されている同様の作業により認証トークン(access_token、token.json)を生成します。
これにより認証トークンが初期化され受信が再開されます。