【Java】Exchange Online SMTP AUTH にて OAuth認証でメール送信

こんにちは。サイオステクノロジーの木村です。

こちらの公開情報によると、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 

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 = "<クライアントID>";
        String AUTHORITY = "https://login.microsoftonline.com/<テナントID>/";
        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

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です