こんにちは。サイオステクノロジーの木村です。
こちらの公開情報によると、Exchange Online での基本認証の廃止に伴い、Exchange Onlineで SMTP AUTH プロトコルを使用したメール送信について、基本認証での接続は非推奨になりました。
Exchange Online で基本認証が無効になった場合でも、SMTP AUTH は引き続き基本認証が使用できるようですが、先進認証での方法に切り替えることが推奨されています。
そこで今回は、Javaでの実装を例に、SMTP AUTH にてメール送信する際に先進認証(OAuth 認証)で行う方法をご紹介します。
アプリケーションの登録
OAuth を使用するには、アプリケーションを Azure AD に登録する必要があります。以下にその手順を記載します。
1. 管理者でAzureポータル(https://portal.azure.com/)にログインします。
2. メニューより「Azure Active Directory」をクリックします。

3. 「アプリの登録」をクリックし「+新規登録」をクリックします。 
4. アプリケーションの登録ページで、以下を入力し「登録」をクリックします。
- 名前:任意の名称
- サポートされているアカウントの種類:この組織ディレクトリのみに含まれるアカウント
- リダイレクトURI(省略可能):何も入力しない

5. 表示された [アプリケーション (クライアント) ID] と、[ディレクトリ (テナント) ID ]の値をメモしておきます。

6. 「証明書とシークレット」をクリックし、「+新しいクライアントシークレット」をクリックします。

7. 説明を入力して有効期限を選択し「追加」をクリックします。

8. 作成されたクライアントシークレット の値をメモしておきます。

9. 「APIのアクセス許可」をクリックし、「+アクセス許可の追加」をクリックします。

10. 「所属する組織で使用しているAPI」タブを選択し、検索欄に「Office 365 Exchange Online」と入力して検索されたものをクリックします。

11. 「アプリケーションの許可」をクリックします。

12. 「SMTP.Send.App」にチェックを入れ、「アクセス許可の追加」をクリックします。

13. 「[テナント名]に管理者の同意を与えます」をクリックします。

14. 「はい」をクリックします。

以上でアプリケーションの登録は完了です。
Exchange に サービスプリンシパル を登録
アプリケーションの登録の手順で登録したアプリケーションで Exchange Online に接続できるようにするために、アプリケーションのサービスプリンシパル をExchange に登録します。以下にその手順を記載します。
オブジェクトID を確認
アプリケーションのサービスプリンシパル をExchange に登録する際に、「オブジェクトID」を指定する必要があるため、以下の手順で「オブジェクトID」を確認します。([アプリの登録] の [概要] のオブジェクトID とは異なる値です。)
1. メニューより「Azure Active Directory」をクリックします。

2. 「エンタープライズ アプリケーション」をクリックします。

3. すべてのアプリケーションの一覧にて、アプリケーションの登録の手順で作成したアプリケーションを検索し、クリックします。

4. プロパティに表示される「オブジェクトID」をコピーしてメモしておきます。

サービスプリンシパル の登録とアクセス権の付与
アプリケーションのサービスプリンシパル を Exchange へ登録し、メールボックスへのアクセス権を付与します。これらの操作は、PowerShellで行います。
1. PowerShellを管理者で起動し、以下のコマンドを実行して、ExchangeOnlineManagement をインストールし、モジュールを読み込みます。
Install-Module -Name ExchangeOnlineManagement
Import-module ExchangeOnlineManagement
2. 以下のコマンドを実行して Exchange Online に接続します。
Connect-ExchangeOnline -Organization [tenantId]
3. 以下のコマンドを実行して、アプリケーションのサービスプリンシパルを Exchange に登録します。
New-ServicePrincipal -AppId [APPLICATION_ID] -ServiceId [OBJECT_ID]
- APPLICATION_ID:「アプリケーションの登録」の 5. の手順でメモした アプリケーション (クライアント) ID
- OBJECT_ID:「オブジェクトID を確認」の 4. の手順でメモした オブジェクトID
4. 以下のコマンドを実行して、アプリケーションのサービスプリンシパルに、メール送信時に送信元に指定するアカウントのメールボックスへのアクセス権を付与します。
Add-MailboxPermission -Identity "xxx@xxx.onmicrosoft.com(送信元に指定するアカウントのメールアドレス)" -User [APPLICATION_ID] -AccessRights FullAccess
※ 送信元に指定するアカウントのメールボックスの SMTP AUTH が無効の場合は、以下のコマンドで有効に設定しておきます。
Set-CASMailbox -Identity "xxx@xxx.onmicrosoft.com(送信元に指定するアカウントのメールアドレス)" -SmtpClientAuthenticationDisabled $false
メール送信
メール送信時のユーザー認証の実装には、Java 用 Microsoft 認証ライブラリ「Microsoft Authentication Library (MSAL) for Java」を使用します。(Microsoft Authentication Library (MSAL)については、こちらを参照。)
メール送信にはJavaMailを使用します。
Mavenの場合、pom.xmlへ以下のように依存関係を追加します。
pom.xml
<dependency>
  <groupId>com.microsoft.azure</groupId>
  <artifactId>msal4j</artifactId>
  <version>1.13.10</version>
</dependency>
<dependency>
  <groupId>com.sun.mail</groupId>
  <artifactId>javax.mail</artifactId>
  <version>1.6.1</version>
</dependency>
以下のようにコードを実装します。
App.java
import java.util.Collections;
import java.util.Properties;
import java.util.Set;
import com.microsoft.aad.msal4j.ClientCredentialFactory;
import com.microsoft.aad.msal4j.ClientCredentialParameters;
import com.microsoft.aad.msal4j.ConfidentialClientApplication;
import com.microsoft.aad.msal4j.IAuthenticationResult;
import com.microsoft.aad.msal4j.IClientCredential;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.Message;
public class App 
{
    public static void main( String[] args )
    {
        String CLIENT_ID = "";
        String AUTHORITY = "https://login.microsoftonline.com//";
        String CLIENT_SECRET = "";
        Set SCOPE = Collections.singleton("https://outlook.office365.com/.default");
        String FROM_ADDRESS = "";
        try {
            //アクセストークン取得
            IClientCredential credential = ClientCredentialFactory.createFromSecret(CLIENT_SECRET);
            ConfidentialClientApplication cca = ConfidentialClientApplication
                .builder(CLIENT_ID, credential)
                .authority(AUTHORITY)
                .build();
            ClientCredentialParameters parameters =
                        ClientCredentialParameters
                                .builder(SCOPE)
                                .build();
            IAuthenticationResult result = cca.acquireToken(parameters).join();
            //メール送信
            Properties properties = new Properties();
            properties.put("mail.smtp.auth", "true");
            properties.put("mail.smtp.auth.mechanisms", "XOAUTH2");
            properties.put("mail.smtp.starttls.enable", "true");
            properties.put("mail.smtp.ssl.protocols", "TLSv1.3");
            Session session = Session.getInstance(properties);
            Transport transport = session.getTransport("smtp");
            transport.connect("smtp.office365.com", 587, FROM_ADDRESS, result.accessToken());
            MimeMessage message = new MimeMessage(session);
            message.addRecipients(Message.RecipientType.TO, "");
            message.setFrom(new InternetAddress(FROM_ADDRESS));
            message.setSubject("テストメール", "ISO-2022-JP");
            message.setText("テスト", "ISO-2022-JP");
            transport.sendMessage(message, message.getAllRecipients());
        }catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
 - クライアントID:「アプリケーションの登録」の 5. の手順でメモした アプリケーション (クライアント) ID
- テナントID:「アプリケーションの登録」の 5. の手順でメモした テナントID
- クライアントシークレット:「アプリケーションの登録」の 8. の手順でメモした クライアントシークレット
App.java を実行すると、送信先メールアドレスにメールが送信されます。
※ 当記事の検証で使用したJavaのバージョン:17
 
					

