Analysis Automation

Microsoft 365 SMTP AUTH retirement: what breaks, what replaces it

Password sign-in for smtp.office365.com goes off by default at the end of December 2026. One of Microsoft's two named replacements is itself retiring.

The short read

Microsoft's setup page still sends admins to Azure Communication Services Email, which Microsoft retires on September 30, 2028.

  • Password sign-in for Microsoft 365 SMTP AUTH goes off by default at the end of December 2026. Admins can turn it back on, for now.

What you can do with this

Open Reports, then Mail flow, then SMTP AUTH clients in the Exchange admin center, export 90 days, and list every sender marked Basic Auth. Move each one before December.

Blue and grey ethernet cables plugged into the ports of two rack-mounted network switches, seen close up.
Photo: ShakataGaNai, CC BY-SA 3.0
In this article
  1. The deadline has moved twice, so check the newest post
  2. What breaks is anything that sends a password to smtp.office365.com
  3. The SMTP AUTH report shows who still signs in with a password
  4. Five replacements, and the one Microsoft is already retiring
  5. OAuth keeps the same server, if the device can do it
  6. What our test showed, and what it can’t show
  7. Graph and HVE fit code and internal senders
  8. Relay fits office devices that mail customers
  9. Azure Communication Services Email has an end date
  10. What this means for you
  11. How we made this

Scanners, line-of-business apps and scripts that send mail through smtp.office365.com with a username and password are on a deadline. Microsoft’s current timeline says that at the end of December 2026, SMTP AUTH Basic authentication “will be disabled by default for existing tenants”. Admins can still turn it back on for now, and Microsoft will announce a final removal date in the second half of 2027.

That leaves about two months to find every device and script that still signs in with a password, and to pick a replacement for each. The choice is harder than Microsoft’s own setup page suggests. It names two replacements, and one of them, Azure Communication Services Email, is itself being retired.

We read Microsoft’s announcements and documentation on October 22, 2026, and put the dates in one timeline. We also tested an OAuth sending script against a local mock of the server.

The deadline has moved twice, so check the newest post

Microsoft started removing Basic authentication from Exchange Online in 2019, and by late 2022 it was gone from every protocol except client submission (SMTP AUTH). In tenants that weren’t using SMTP AUTH at all, Microsoft switched it off as well.

Date What Microsoft said
April 15, 2024 Basic auth for SMTP AUTH will be permanently removed in September 2025, with no exceptions
Later revision Rejections start for a small share of submissions on March 1, 2026, and reach 100% on April 30, 2026
January 27, 2026 New timeline: no change until December 2026
End of December 2026 Basic auth disabled by default for existing tenants; admins can still enable it
After December 2026 New tenants get no Basic auth by default; OAuth is the supported method
Second half of 2027 Microsoft announces the final removal date

The first two rows come from the original April 2024 post, which still carries its revisions; the rest come from the January 2026 update. Microsoft’s reason: customers “continue to face real challenges modernizing legacy email workflows.”

Two things follow. The April 2026 date that some vendor pages still quote is dead. And the 2024 line that Support “cannot re-enable Basic auth for you” no longer fits the first step, because December’s change is a default an admin can reverse.

We wouldn’t plan around that switch. The January post doesn’t say which setting it is or how long it will last, and the final date is still to come.

What breaks is anything that sends a password to smtp.office365.com

SMTP AUTH is the protocol devices and applications use to submit mail, usually on port 587. Microsoft lists POP and IMAP clients, “applications, reporting servers, and multifunction devices.” Microsoft says virtually all modern mail apps, Outlook and Outlook on the web included, don’t use it, so people’s own mail keeps working.

The protocol stays; only the password sign-in goes. After the final removal, Microsoft says, a password login gets 550 5.7.30 Basic authentication is not supported for Client Submission. A scanner that shows that error, or a nightly report that stops arriving, is the symptom to watch for.

Some tenants are already there. SMTP AUTH has been off by default for organizations created after January 2020. It is also already disabled wherever security defaults are on in Microsoft Entra ID.

The SMTP AUTH report shows who still signs in with a password

The SMTP AUTH clients report sits under Reports > Mail flow in the Exchange admin center. Its table lists each sender address with an Authentication Protocol column: “Basic Auth is TlsAuthLogin and Modern Auth is XOAUTH2.”

The default view covers 7 days, and the date filter takes a range of up to 90 days. Set 90 and export the CSV, so a monthly or quarterly job shows up too. Every row marked Basic Auth needs one of the replacements below.

Five replacements, and the one Microsoft is already retiring

Microsoft’s setup page for devices and apps, updated October 5, 2026, names High Volume Email for internal mail. For mail that also goes outside, it names Azure Communication Services Email.

On September 23, 2026, Microsoft announced the retirement of Azure Communication Services as a standalone offering, Email included, effective September 30, 2028. From October 23, 2026, the same guide says, Microsoft limits onboarding to retiring services for customers without an existing resource. It starts with phone numbers and may add more limits “at any time.” It recommends migrating existing email workloads off rather than starting new ones.

So the page that tells you what to move to points at a service with an end date. Here are the options on Microsoft’s own figures, read October 22, 2026.

Option Recipients Sign-in Limits Price
SMTP AUTH with OAuth Inside and outside OAuth token for a licensed mailbox 10,000 recipients a day, 30 messages a minute In the mailbox license
Microsoft Graph sendMail Inside and outside OAuth, Mail.Send permission Exchange Online limits In the mailbox license
High Volume Email (HVE) Inside the tenant only HVE account password or OAuth No rate limit, 50 recipients a message, 10 MB $0.000042 per recipient
SMTP relay connector Inside and outside Static IP address or certificate “Reasonable limits” No licensed mailbox needed
Azure Communication Services Email Inside and outside Entra app secret as the SMTP password 30 a minute and 100 an hour at the start (custom domain) $0.00025 per email plus $0.00012 per MB

OAuth keeps the same server, if the device can do it

The smallest change keeps smtp.office365.com on port 587 and swaps the password for a token. The mailbox limits stay the same: 10,000 recipients a day and 30 messages a minute, and sent mail lands in Sent Items. The catch is the client. A scanner whose firmware only offers a username and password field can’t send a token, and no setting in Microsoft 365 changes that.

For a script or app you control, Microsoft documents a client credentials flow with no signed-in user. In outline:

  1. Register an app in Microsoft Entra and add the SMTP.SendAsApp application permission from Office 365 Exchange Online, then grant admin consent.
  2. Register its service principal in Exchange Online: New-ServicePrincipal -AppId <APPLICATION_ID> -ObjectId <OBJECT_ID>. The object ID comes from the Enterprise applications page, not App registrations; Microsoft warns that the wrong one causes an authentication failure.
  3. Allow it one mailbox: Add-MailboxPermission -Identity "reports@contoso.com" -User <SERVICE_PRINCIPAL_ID> -AccessRights FullAccess.
  4. Request a token with the scope https://outlook.office365.com/.default and send it in the SASL XOAUTH2 format.

The script below does step 4 with Python’s standard library only. Settings come from environment variables, so no secret sits in the file. It exits with an error code on every failure, so a scheduler or a failure check can see that nothing was sent.

#!/usr/bin/env python3
"""Send one message through Exchange Online SMTP AUTH with OAuth (XOAUTH2).
Settings: M365_TENANT_ID, M365_CLIENT_ID, M365_CLIENT_SECRET, M365_SENDER
Usage: python3 send_oauth.py recipient@example.com "Subject" "Body text"
"""
import base64, json, os, smtplib, ssl, sys, urllib.error, urllib.parse, urllib.request
from email.message import EmailMessage
from email.utils import formatdate, make_msgid

SCOPE = "https://outlook.office365.com/.default"


def env(name):
    value = os.environ.get(name)
    if not value:
        sys.exit(f"missing setting {name}")
    return value


def get_token():
    url = f"https://login.microsoftonline.com/{env('M365_TENANT_ID')}/oauth2/v2.0/token"
    form = urllib.parse.urlencode({
        "client_id": env("M365_CLIENT_ID"),
        "client_secret": env("M365_CLIENT_SECRET"),
        "scope": SCOPE,
        "grant_type": "client_credentials",
    }).encode()
    try:
        with urllib.request.urlopen(url, data=form, timeout=30) as resp:
            data = json.load(resp)
    except urllib.error.HTTPError as err:
        try:
            detail = json.loads(err.read() or b"{}").get("error_description", "")[:200]
        except ValueError:
            detail = ""
        sys.exit(f"token request failed: HTTP {err.code} {detail}")
    except urllib.error.URLError as err:
        sys.exit(f"token endpoint unreachable: {err.reason}")
    except ValueError:
        sys.exit("token endpoint did not return JSON")
    if "access_token" not in data:
        sys.exit("token response had no access_token")
    return data["access_token"]


def xoauth2(user, token):
    # base64("user=" + user + "^Aauth=Bearer " + token + "^A^A"), ^A = 0x01
    return base64.b64encode(f"user={user}\x01auth=Bearer {token}\x01\x01".encode()).decode()


def send(to, subject, body):
    if "@" not in to:
        sys.exit(f"not an email address: {to!r}")
    sender = env("M365_SENDER")
    token = get_token()
    msg = EmailMessage()
    msg["From"], msg["To"], msg["Subject"] = sender, to, subject
    msg["Date"], msg["Message-ID"] = formatdate(localtime=False), make_msgid()
    msg.set_content(body)
    ctx = ssl.create_default_context()
    ctx.minimum_version = ssl.TLSVersion.TLSv1_2
    try:
        with smtplib.SMTP("smtp.office365.com", 587, timeout=60) as smtp:
            smtp.ehlo()
            smtp.starttls(context=ctx)
            smtp.ehlo()
            code, reply = smtp.docmd("AUTH", "XOAUTH2 " + xoauth2(sender, token))
            if code != 235:
                sys.exit(f"auth failed: {code} {reply.decode(errors='replace')}")
            smtp.send_message(msg)
    except (OSError, smtplib.SMTPException) as err:
        sys.exit(f"not sent: {err}")
    print(f"sent to {to} as {sender}")


if __name__ == "__main__":
    if len(sys.argv) != 4:
        sys.exit(__doc__)
    send(*sys.argv[1:4])

The script asks for a new token on every run, so a scheduled job never reuses an old one. The client secret is different: Entra asks for a duration when you create it. Put its end date in a calendar, because an expired secret stops the mail as quietly as the password it replaced.

What our test showed, and what it can’t show

We have no Microsoft 365 tenant to test against, so we wrote a local mock of the documented behavior. It requires STARTTLS, accepts XOAUTH2 and answers a password login with Microsoft’s documented 550 5.7.30 reply. We ran the script against it in eight cases, with four lines changed to point at the mock and trust its test certificate.

Case Result
Our encoder against Microsoft’s published XOAUTH2 example Same string, byte for byte
Correct settings Sent; the mock recorded one message from the right sender
Wrong client secret Stopped at the token request, exit code 1
Token used for a mailbox the app wasn’t granted 535 5.7.3, exit code 1
Password login from the same client 550 5.7.30 Basic authentication is not supported
Mail sent without signing in Refused by the mock
Server unreachable Exit code 1 with a one-line message
Empty recipient Refused before connecting, exit code 1

The last two cases caught bugs in our first draft. An unreachable server crashed it with a Python traceback, the kind of failure that hides in a cron log. An empty recipient printed “sent” and exited 0, because our first mock accepted a message with no recipients. The listing above has both fixes, and the mock now refuses that too.

What the mock can’t prove is the Microsoft side: consent, the service principal and the mailbox permission. Send one test message through your own tenant before you switch a production job over.

Graph and HVE fit code and internal senders

Microsoft Graph. For code you write, POST /users/{id}/sendMail avoids SMTP entirely. A 202 Accepted reply means the request was accepted, not delivered, and Exchange Online’s limits still apply.

Watch the permission. Granted in Entra, Mail.Send as an application permission lets an app “send mail as any user.” Microsoft’s fix is RBAC for Applications: assign the Application Mail.Send role with a management scope, and remove the tenant-wide consent in Entra, because the two grants add up.

High Volume Email. HVE is for apps and devices that mail people inside the company, such as payroll notices, alerts and scan-to-email. It sends only to internal recipients and needs no license. But each HVE account must be tied to a pay-as-you-go billing policy on an Azure subscription, or it can’t send at all. The endpoint is smtp.hve.mx.microsoft on port 587.

HVE still accepts an account password, which makes it the practical home for an internal-only device that can’t do OAuth. The HVE pages we read don’t say how long it will keep accepting passwords, so we would treat that as borrowed time too.

Relay fits office devices that mail customers

SMTP relay through a connector. For a device in your own office that mails customers, an inbound connector on your MX endpoint (for example contoso-com.mail.protection.outlook.com, port 25) authenticates by static IP or certificate instead of a password. It reaches outside recipients and needs no licensed mailbox.

The IP must be static and not shared with another organization, and port 25 must be open. Microsoft also says relay can’t be used from a third-party hosted service such as Azure. Direct Send, the unauthenticated method on the same page, can’t reach external recipients, and Microsoft says it is working on turning it off by default.

Azure Communication Services Email has an end date

It still works. Of the five, it is the only one that lets a password-only device reach outside recipients from any network, because the SMTP password is an Entra app’s client secret, sent to smtp.azurecomm.net. But a new custom domain starts at 30 emails a minute and 100 an hour until you ask for more, and the service ends on September 30, 2028.

Microsoft lists $0.00025 per email and $0.00012 per MB, and notes those example prices may not be the latest. A hypothetical office sending 2,000 messages of 0.2 MB a month, one recipient each, would pay $0.50 plus $0.048 on those figures. The same 2,000 messages to staff through HVE would cost $0.084.

Price isn’t the issue with either. The end date is.

What this means for you

The decision is per sender, and it turns on two questions: can it do OAuth, and does it mail anyone outside the company?

  • Your own scripts and apps: move them to OAuth on smtp.office365.com with the steps above, or to Graph sendMail. Nothing else changes, and nothing extra is billed.
  • A device that mails only staff and can’t do OAuth: point it at HVE. Set up the billing policy first, or the account stays inactive.
  • A device that mails customers and can’t do OAuth: use a relay connector if it sits behind a static IP you own or can present a certificate. If it doesn’t, replace the device or route it through a mail service you control. We wouldn’t start a new setup on Azure Communication Services Email two years before its end date.
  • A sender in the report you can’t match to a device: set Set-CASMailbox -Identity <mailbox> -SmtpClientAuthenticationDisabled $true on that one mailbox and see what complains. That turns off SMTP AUTH for that mailbox alone, so you find out in a week you chose instead of in late December.

Start with the 90-day export from the SMTP AUTH clients report. The rows marked Basic Auth are your list. Give each one a replacement and a test date before December, while turning Basic auth back on is still an option rather than the plan.

How we made this

We read Microsoft’s two Exchange Team posts on the SMTP AUTH timeline, the Microsoft Learn pages listed below and the Azure Communication Services retirement guide on October 22, 2026. We wrote the sending script and a local mock of the token endpoint and SMTP server, and ran the eight cases in the table on that date. The mock imitates Microsoft’s documented replies; it is not Microsoft’s service. We did not test against a real Microsoft 365 tenant, a scanner or the paid HVE and Azure services.

Sources