You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PHPMailer中为Exchange Online实现OAuth 2.0认证?

切换PHPMailer至Exchange Online OAuth2.0认证遇Bad Request错误排查

背景

微软已于2022年10月1日起弃用Exchange Online的基础认证(账号密码登录),现需将PHPMailer通过SMTP发送邮件的方式切换至OAuth 2.0认证。

原基础认证可用代码

<?php
    include "vendor/autoload.php";
    
    use PHPMailer\PHPMailer\PHPMailer;
    use PHPMailer\PHPMailer\Exception;

    $mail = new PHPMailer;
    $mail->IsSMTP();
    $mail->Host = "smtp.office365.com";
    $mail->Port = "587";
    $mail->SMTPAuth = true;
    $mail->Username = "my_email@my-company.com";
    $mail->Password = "my_password";
    $mail->SMTPSecure = "tls";
    $mail->From = "my_email@my-company.com";
    $mail->FromName = "my_name";
    $mail->AddAddress("my_email@my-company.com");
    $mail->IsHTML(true);
    $mail->Subject = "This is a test subject";
    $mail->Body = "Hello, how are you?";
    $mail->Send();
?>

尝试的OAuth 2.0代码(无法正常运行)

<?php
    include "vendor/autoload.php";

    use PHPMailer\PHPMailer\Exception;
    use PHPMailer\PHPMailer\OAuth;
    use PHPMailer\PHPMailer\PHPMailer;
    use PHPMailer\PHPMailer\SMTP;
    use Stevenmaguire\OAuth2\Client\Provider\Microsoft;
    
    $mail = new PHPMailer;
    $mail->isSMTP();
    $mail->Host = "smtp.office365.com";
    $mail->SMTPAuth = true;
    $mail->AuthType = "XOAUTH2";
    $mail->SMTPDebug = SMTP::DEBUG_LOWLEVEL;
    $mail->SMTPSecure = "tls";
    $mail->Port = 587;

    $username = "my_email@my-company.com";
    $clientId = "client_id_from_azure_app_registration";
    $clientSecret = "client_secret_from_azure_app_registration";
    $redirectURI = "my_redirect_uri";
    $Token = "my_token";
    $mail->refresh_token = $Token;

    $provider = new Stevenmaguire\OAuth2\Client\Provider\Microsoft(
        [
            "clientId" => $clientId,
            "clientSecret" => $clientSecret,
            "redirectUri" => $redirectURI
        ]
    );

    $provider->urlAPI = "https://outlook.office365.com/.default";
    $provider->scope = "Mail.Send";

    $mail->setOAuth(
        new OAuth(
            [
                "provider" => $provider,
                "clientId" => $clientId,
                "clientSecret" => $clientSecret,
                "refreshToken" => $Token, 
                "userName" =>$username
            ]
        )
    );

    $mail->From = $username;
    $mail->AddAddress("my_email@my-company.com");   
    $mail->IsHTML(true);
    $mail->Subject = "This is a test subject";
    $mail->Body = "Hello, how are you?";
    $mail->Send();
?>

报错信息

2022-09-27 13:51:38 Connection: opening to smtp.office365.com:587, timeout=300, options=array()
2022-09-27 13:51:38 Connection: opened
2022-09-27 13:51:38 SMTP INBOUND: "220 AS4P191CA0011.outlook.office365.com Microsoft ESMTP MAIL Service ready at Tue, 27 Sep 2022 13:51:37 +0000"
2022-09-27 13:51:38 SERVER -> CLIENT: 220 AS4P191CA0011.outlook.office365.com Microsoft ESMTP MAIL Service ready at Tue, 27 Sep 2022 13:51:37 +0000
2022-09-27 13:51:38 CLIENT -> SERVER: EHLO
2022-09-27 13:51:38 SMTP INBOUND: "250-AS4P191CA0011.outlook.office365.com Hello [2a02:4780:8:2::25]"
2022-09-27 13:51:38 SMTP INBOUND: "250-SIZE 157286400"
2022-09-27 13:51:38 SMTP INBOUND: "250-PIPELINING"
2022-09-27 13:51:38 SMTP INBOUND: "250-DSN"
2022-09-27 13:51:38 SMTP INBOUND: "250-ENHANCEDSTATUSCODES"
2022-09-27 13:51:38 SMTP INBOUND: "250-STARTTLS"
2022-09-27 13:51:38 SMTP INBOUND: "250-8BITMIME"
2022-09-27 13:51:38 SMTP INBOUND: "250-BINARYMIME"
2022-09-27 13:51:38 SMTP INBOUND: "250-CHUNKING"
2022-09-27 13:51:38 SMTP INBOUND: "250 SMTPUTF8"
2022-09-27 13:51:38 SERVER -> CLIENT: 250-AS4P191CA0011.outlook.office365.com Hello [2a02:4780:8:2::25]250-SIZE 157286400250-PIPELINING250-DSN250-ENHANCEDSTATUSCODES250-STARTTLS250-8BITMIME250-BINARYMIME250-CHUNKING250 SMTPUTF8
2022-09-27 13:51:38 CLIENT -> SERVER: STARTTLS
2022-09-27 13:51:38 SMTP INBOUND: "220 2.0.0 SMTP server ready"
2022-09-27 13:51:38 SERVER -> CLIENT: 220 2.0.0 SMTP server ready
2022-09-27 13:51:38 CLIENT -> SERVER: EHLO
2022-09-27 13:51:38 SMTP INBOUND: "250-AS4P191CA0011.outlook.office365.com Hello [2a02:4780:8:2::25]"
2022-09-27 13:51:38 SMTP INBOUND: "250-SIZE 157286400"
2022-09-27 13:51:38 SMTP INBOUND: "250-PIPELINING"
2022-09-27 13:51:38 SMTP INBOUND: "250-DSN"
2022-09-27 13:51:38 SMTP INBOUND: "250-ENHANCEDSTATUSCODES"
2022-09-27 13:51:38 SMTP INBOUND: "250-AUTH LOGIN XOAUTH2"
2022-09-27 13:51:38 SMTP INBOUND: "250-8BITMIME"
2022-09-27 13:51:38 SMTP INBOUND: "250-BINARYMIME"
2022-09-27 13:51:38 SMTP INBOUND: "250-CHUNKING"
2022-09-27 13:51:38 SMTP INBOUND: "250 SMTPUTF8"
2022-09-27 13:51:38 SERVER -> CLIENT: 250-AS4P191CA0011.outlook.office365.com Hello [2a02:4780:8:2::25]250-SIZE 157286400250-PIPELINING250-DSN250-ENHANCEDSTATUSCODES250-AUTH LOGIN XOAUTH2250-8BITMIME250-BINARYMIME250-CHUNKING250 SMTPUTF8
2022-09-27 13:51:38 Auth method requested: XOAUTH2
2022-09-27 13:51:38 Auth methods available on the server: LOGIN,XOAUTH2

Fatal error: Uncaught League\OAuth2\Client\Provider\Exception\IdentityProviderException: Bad Request in /home/u760208683/vendor/stevenmaguire/oauth2-microsoft/src/Provider/Microsoft.php:79 Stack trace: #0 /home/u760208683/vendor/league/oauth2-client/src/Provider/AbstractProvider.php(628): Stevenmaguire\OAuth2\Client\Provider\Microsoft->checkResponse(Object(GuzzleHttp\Psr7\Response), Array) #1 /home/u760208683/vendor/league/oauth2-client/src/Provider/AbstractProvider.php(537): League\OAuth2\Client\Provider\AbstractProvider->getParsedResponse(Object(GuzzleHttp\Psr7\Request)) #2 /home/u760208683/vendor/phpmailer/phpmailer/src/OAuth.php(115): League\OAuth2\Client\Provider\AbstractProvider->getAccessToken(Object(League\OAuth2\Client\Grant\RefreshToken), Array) #3 /home/u760208683/vendor/phpmailer/phpmailer/src/OAuth.php(128): PHPMailer\PHPMailer\OAuth->getToken() #4 /home/u760208683/vendor/phpmailer/phpmailer/src/SMTP.php(598): PHPMailer\PHPMailer\OAuth->getOauth64() #5 /home/u760208683/vendor/phpmailer/phpmailer/src/PHPMailer in /home/u760208683/vendor/stevenmaguire/oauth2-microsoft/src/Provider/Microsoft.php on line 79

解决方向

  • 确认令牌类型:代码中$Token必须是刷新令牌(refresh token),而非Postman生成的短期访问令牌(access token)。访问令牌无法用于刷新获取新令牌,会直接导致Bad Request。
  • 修正Provider配置:
    • 移除手动设置的$provider->urlAPI,该库默认已配置正确的微软令牌端点,手动修改会导致请求地址错误。
    • 在实例化Provider时以数组形式传入scope,而非事后赋值:
      $provider = new Stevenmaguire\OAuth2\Client\Provider\Microsoft([
          "clientId" => $clientId,
          "clientSecret" => $clientSecret,
          "redirectUri" => $redirectURI,
          "scope" => ["Mail.Send", "offline_access"] // 需包含offline_access以获取可刷新的令牌
      ]);
      
  • 检查Azure应用配置:
    • 确保应用已添加Delegated类型的Mail.Send权限,且完成了管理员同意(若为租户级应用)。
    • 确认代码中的redirectURI与Azure应用注册里配置的重定向URI完全一致,包括协议(http/https)、端口、路径。
  • 移除冗余代码:删除$mail->refresh_token = $Token;,OAuth实例已通过构造参数传入refreshToken,无需重复设置。
  • 验证令牌获取流程:若使用授权码流程获取refresh token,需确保授权请求中包含offline_access scope,否则无法生成可用于刷新的令牌。

内容的提问来源于stack exchange,提问作者Laurents_Gits

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 05:15:42