Exchange Onlineの基本認証廃止で普通に困ってる人のためのエントリ

# はじめに
基本認証を使っている方は2022年10月1日からExchange Onlineに接続できなくなります。
ある日会社に行ったら、Xデー迄あと１か月ないので至急対処してくれとお達しが来ていました。
現実を受け止めきれなかったのですが、公式声明文が出ていたので、どうやら本当のようです。

https://docs.microsoft.com/ja-jp/exchange/clients-and-mobile-in-exchange-online/deprecation-of-basic-authentication-exchange-online


> 2021 年 9 月に、2022 年 10 月 1 日 より、Exchange Online の Outlook、EWS、RPS、POP、IMAP、EAS プロトコルの基本認証の無効化が開始されることを発表しました。 SMTP 認証が使用されていない場合も無効になります。


2022/09/12 追記：影響が大きいという判断なのか、若干の延命ができるよというアナウンスがでていました。
> 2022 年 9 月 1 日に、この変更を延期する最終的な機会が 1 つあると発表しました。 テナントは、2022 年 10 月 1 日から 2022 年 12 月 31 日の間にプロトコルを再度有効にすることができます。 プロトコルの例外または再有効化されたプロトコルは、2023 年 1 月の早い時期に無効になり、今後使用される可能性はありません。


なにはともあれ、この記事は次のような人に向けて書かれています。
- OfficeのメールボックスをIDPWで開いてなんやかんやする自家製アプリを書いている。
- 2022年10月1日までにOAuth2.0認証に切り替えないといけないし、何ならもう10月1日になった
- 正直何からやっていいか不明だし何で検索すればいいかよくわからない
- こんなことをやっている場合ではないし、とにかく悲しい

# 要点

## Azure ADにアプリを登録する

先進認証では、トークンの要求元となるシステムが管理側に登録されている必要があります。
システムに問題があった場合に、管理側が直ぐアクセスを止められるようにするためです。
こちらの記事を参考に、Azure ADにアプリを登録します。

https://qiita.com/megasys1968/items/530f92dcaf0ea798acbf

やることは以下です。
1. アプリを好きな名前で登録する。
1. テナントID、アプリケーションIDをメモする。
1. アプリにIMAP.AccessAsUser.Allのアクセス許可をつける。
（POPでやってる場合はPOP.AccessAsUser.All）
1. クライアントシークレットを発行して、それもメモする。
クライアントシークレットの有効期限は最大2年なので、期限切れる前に新しいシークレットを発行要。

## トークンをゲットするスクリプトを書く

これまではIDとPWで直接メールサーバに接続しにいっていたと思いますが、
先進認証では、①トークンを認証サーバに貰いに行って、②もらったトークンでメールサーバに接続します。
①の認証の際、いつものID/PWに加え、さっきメモしたテナントID、クライアントID、クライアントシークレットを使います。

認証サーバへの問い合わせですが、アクセスできるなら言語はなんでもいいです。（何なら、curlでもよい）
シンプルなpythonのスクリプトを書いてた方がいたので、参考にしました。感謝感謝。

https://qiita.com/turupon/items/038b893712433ed23f0b

```Python:GetAzureAccessToken.py
# -*- coding: utf-8 -*-
#
# Usage: 
# python GetAzureAccessToken.py 'XXXXXX@XXXXX.com' 'XXXXXXXX' > token_XXXXXXX.txt
#
import json
import requests
import sys

# Settingの読み込み
TENANT_ID = your_tenant_id
CLIENT_ID = your_client_id
SECRET = your_client_secret
SCOPE = [ 'https://outlook.office365.com/IMAP.AccessAsUser.All' ]

# コマンドライン引数の読み込み
USERNAME = sys.argv[1]
PASSWORD = sys.argv[2]

# アクセストークンの取得
def get_azure_access_token():

    # POSTするときのヘッダ
    headers = {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    # POSTするときのデータ
    payload = {
        'client_id': CLIENT_ID,
        'scope': SCOPE,
        'grant_type': 'password',
        'username': USERNAME,
        'password': PASSWORD,
        'client_secret': SECRET
    }

    # access_token を取得するURL
    TokenGet_URL = "https://login.microsoftonline.com/" + TENANT_ID + "/oauth2/v2.0/token"

    # トークンの要求アクセス
    response = requests.get(
        TokenGet_URL,
        headers=headers,
        data=payload
    )
    # 要求処理のクローズ
    response.close

    # 結果をjsonにつっこむ
    jsonObj = json.loads(response.text)

    # トークンだけ返却
    return jsonObj["access_token"]


if __name__ == '__main__':

    access_token = get_azure_access_token()
    print(access_token)


```

取得したトークンは１時間で有効期限がきれるので、これをcronなどで動かして定期的にファイルを更新させればいいです。

余談ですが、Microsoft的には、pythonであればMSALというライブラリを使うのが推奨されてます。

https://github.com/AzureAD/microsoft-authentication-library-for-python

うちはそもそもpipが無かった。悲しいね。

## いまやってる接続をトークン認証に書き換える

例えばjavamailの場合はこんな調子で接続できました。

https://javaee.github.io/javamail/OAuth2

```Java
Properties props = new Properties();
props.put("mail.imap.ssl.enable", "true"); 
props.put("mail.imap.auth.mechanisms", "XOAUTH2"); //ここ
Session session = Session.getInstance(props);
Store store = session.getStore("imap");
store.connect( host_address, username, oauth2_access_token); //これ
```

ご利用中の自家製アプリの接続ライブラリが何か分かりかねますが、OAuth認証に対応していることを祈ってください。
多分対応していると思います。なんせこの話、本当は2年以上猶予があったのです。あっはっは…

# おわりに
手数は少しかかりますが、ノリが分かってしまえば意外と何とかなると思います。
この記事が参考になることを祈ります。良きOAuth2.0ライフをお過ごしください。