To move an application from Basic auth SMTP to OAuth in Exchange Online, register an app in Microsoft Entra ID, give it the SMTP.SendAsApp application permission with admin consent, register its service principal in Exchange Online with New-ServicePrincipal, and grant that service principal access to the sending mailbox. The app then requests a token with the client credentials flow and the scope https://outlook.office365.com/.default, and authenticates to smtp.office365.com on port 587 with SASL XOAUTH2 instead of a password. Do this before the end of December 2026, when Basic auth for SMTP AUTH is disabled by default for existing tenants.
Who this is for and what you will have
This guide is for administrators and developers who own an application, script or service that sends mail by authenticating to smtp.office365.com with a mailbox username and password. It assumes you can change the app's code or that the app supports OAuth client credentials. At the end you will have:
- An Entra app registration with a certificate or secret and the
SMTP.SendAsApppermission. - A service principal registered in Exchange Online and scoped to one sending mailbox.
- Working token and XOAUTH2 code, with PowerShell and Python examples.
- A way to prove in the SMTP AUTH clients report that the app now uses OAuth, and to block Basic auth for that mailbox.
If the sender is a printer or scanner that can't do OAuth, use the options in Fix 550 5.7.30 Basic authentication is not supported for Client Submission instead.
Why move now
Microsoft revised the SMTP AUTH Basic authentication timeline in January 2026:
| When | What changes |
|---|---|
| Now to December 2026 | Basic auth for SMTP AUTH behavior is unchanged |
| End of December 2026 | Basic auth for SMTP AUTH is disabled by default for existing tenants; admins can still enable it |
| New tenants created after December 2026 | Basic auth for SMTP AUTH is unavailable by default; OAuth is the supported method |
| Second half of 2027 | Microsoft announces the final removal date |
After Basic auth is removed, clients that still use it receive 550 5.7.30 Basic authentication is not supported for Client Submission. Microsoft's stated reason for the change is that Basic auth sends usernames and passwords that are exposed to credential theft, phishing and brute force attacks. With OAuth client credentials, the application no longer stores a mailbox password at all.
How client credentials SMTP works
The client credentials flow lets a service authenticate as itself, without a signed-in user. For SMTP, three separate pieces must line up:
| Piece | Where it lives | What it grants |
|---|---|---|
SMTP.SendAsApp application permission with admin consent | Microsoft Entra ID | The app may request tokens for SMTP |
Service principal registered with New-ServicePrincipal | Exchange Online | Exchange recognizes the app as a security principal |
| Mailbox permission for that service principal | Exchange Online | The app may use one specific mailbox |
The token alone doesn't let the app send from any mailbox. Exchange checks that the service principal has been granted access to the mailbox named in the XOAUTH2 string, which is how you keep the app scoped to a single sender.
Prerequisites
- An account that can create app registrations and grant tenant-wide admin consent in Microsoft Entra ID.
- An Exchange Online administrator who can run
New-ServicePrincipal,Add-MailboxPermissionandSet-CASMailbox. Microsoft notes that ifNew-ServicePrincipalfails after you connect, the account most likely lacks the permissions in Exchange Online. - The ExchangeOnlineManagement and Microsoft Graph PowerShell modules.
- A licensed mailbox to send from, for example
app-mailer@contoso.com. - An app or library that supports SASL XOAUTH2 over SMTP with STARTTLS and TLS 1.2 or later.
Step 1: Register the app and add a credential
- In the Microsoft Entra admin center, open App registrations and select New registration.
- Give it a clear name, such as
Contoso Invoicing SMTP, and select Accounts in this organizational directory only. - On the Overview page, copy the Application (client) ID and Directory (tenant) ID.
- Under Certificates & secrets, upload a certificate (preferred) or create a client secret. The Microsoft identity platform accepts a certificate or federated credential as a higher level of assurance than a shared secret. Never put either in source code.
Step 2: Add SMTP.SendAsApp and grant consent
- In the app registration, open API permissions and select Add a permission.
- Select the APIs my organization uses tab and search for Office 365 Exchange Online.
- Select Application permissions, choose SMTP.SendAsApp, and select Add permissions.
- Select Grant admin consent for your tenant.
For a single-tenant app, granting consent on the app configuration page is enough. If a vendor publishes a multitenant app, the vendor or your admin uses the admin consent URL with the SMTP scope instead:
https://login.microsoftonline.com/{tenant}/v2.0/adminconsent?client_id=<CLIENT_ID>&redirect_uri=<REDIRECT_URI>&scope=https://outlook.office365.com/.defaultStep 3: Register the service principal in Exchange Online
New-ServicePrincipal needs the AppId and the ObjectId of the service principal, which is the Object ID shown under Enterprise applications, not the Object ID on the App registrations page. Using the wrong one causes an authentication failure later. Retrieve both with Microsoft Graph PowerShell:
Connect-MgGraph -Scopes 'Application.Read.All'
$sp = Get-MgServicePrincipal -Filter "appId eq '00001111-aaaa-2222-bbbb-3333cccc4444'"
$sp | Format-List Id, AppId, DisplayNameThe Id property is the value for -ObjectId. Now register it in Exchange Online:
Connect-ExchangeOnline -UserPrincipalName admin@contoso.com
New-ServicePrincipal -AppId $sp.AppId -ObjectId $sp.Id -DisplayName "EXO SP Contoso Invoicing SMTP"
Get-ServicePrincipal -Identity "EXO SP Contoso Invoicing SMTP" | Format-ListStep 4: Grant mailbox permissions and enable SMTP AUTH
Give the service principal access to the sending mailbox. Microsoft's procedure uses FullAccess:
$exoSp = Get-ServicePrincipal -Identity "EXO SP Contoso Invoicing SMTP"
Add-MailboxPermission -Identity "app-mailer@contoso.com" -User $exoSp.Identity -AccessRights FullAccessIf the app must send as a different address than the mailbox it authenticates for, Microsoft's OAuth article states that you need to grant SendAs permission with Add-RecipientPermission. It doesn't show the exact command for a service principal, so the simplest and best-documented setup is to send from the same mailbox the service principal has FullAccess on. A From address that the sign-in identity has no Send As right for fails with 5.7.60 SMTP; Client does not have permissions to send as this sender.
SMTP AUTH must be allowed for the sending mailbox. Microsoft recommends disabling SMTP AUTH for the organization and enabling it only on mailboxes that need it; the mailbox setting overrides the organization setting:
Set-CASMailbox -Identity app-mailer@contoso.com -SmtpClientAuthenticationDisabled $false
Get-CASMailbox -Identity app-mailer@contoso.com | Format-List SmtpClientAuthenticationDisabledFalse means enabled, True means disabled, and blank means the mailbox follows Get-TransportConfig | Format-List SmtpClientAuthenticationDisabled.
Step 5: Request a token
Send a POST to the tenant's v2.0 token endpoint with grant_type=client_credentials and the Exchange scope. With a secret, the request in PowerShell looks like this:
$tenantId = 'aaaabbbb-0000-cccc-1111-dddd2222eeee'
$body = @{
client_id = '00001111-aaaa-2222-bbbb-3333cccc4444'
client_secret = $env:SMTP_APP_SECRET
scope = 'https://outlook.office365.com/.default'
grant_type = 'client_credentials'
}
$token = Invoke-RestMethod -Method Post `
-Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" `
-ContentType 'application/x-www-form-urlencoded' -Body $body
$token.expires_inA successful response contains token_type, expires_in and access_token. With a certificate, the request replaces client_secret with client_assertion_type set to urn:ietf:params:oauth:client-assertion-type:jwt-bearer and a signed client_assertion. Use a Microsoft Authentication Library (MSAL) rather than building the assertion yourself. Client credentials never return a refresh token; when the token expires, request a new one.
All scopes in one request must belong to a single resource; including scopes for more than one resource, such as the Exchange scope and a Microsoft Graph scope, results in an error.
Step 6: Authenticate with SASL XOAUTH2
Connect to smtp.office365.com on port 587, issue STARTTLS, then send AUTH XOAUTH2 with this string, base64-encoded:
base64("user=" + userName + "^Aauth=Bearer " + accessToken + "^A^A")^A is the Control+A character (0x01). userName is the mailbox the service principal was granted access to. A successful exchange ends with 235 2.7.0 Authentication successful; a failure returns 535 5.7.3 Authentication unsuccessful.
A minimal Python example with MSAL and the standard library. smtplib base64-encodes the string returned by the callable, so the code passes the raw XOAUTH2 string:
import os
import smtplib
from email.message import EmailMessage
import msal
TENANT_ID = "aaaabbbb-0000-cccc-1111-dddd2222eeee"
CLIENT_ID = "00001111-aaaa-2222-bbbb-3333cccc4444"
MAILBOX = "app-mailer@contoso.com"
app = msal.ConfidentialClientApplication(
CLIENT_ID,
authority=f"https://login.microsoftonline.com/{TENANT_ID}",
client_credential=os.environ["SMTP_APP_SECRET"],
)
result = app.acquire_token_for_client(scopes=["https://outlook.office365.com/.default"])
if "access_token" not in result:
raise SystemExit(f"{result.get('error')}: {result.get('error_description')}")
xoauth2 = f"user={MAILBOX}\x01auth=Bearer {result['access_token']}\x01\x01"
msg = EmailMessage()
msg["From"] = MAILBOX
msg["To"] = "admin@contoso.com"
msg["Subject"] = "OAuth SMTP test"
msg.set_content("Sent with SMTP AUTH and OAuth client credentials.")
with smtplib.SMTP("smtp.office365.com", 587) as smtp:
smtp.starttls()
smtp.ehlo()
smtp.auth("XOAUTH2", lambda challenge=None: xoauth2)
smtp.send_message(msg)Since MSAL Python 1.23, acquire_token_for_client looks in the token cache first and only calls the identity platform when the cache has no valid token, so calling it before each send is fine.
Verify the switch
- Send a test message and confirm it is delivered.
- Check the SMTP AUTH clients report. In the Exchange admin center go to Reports > Mail flow > SMTP AUTH clients. The Authentication Protocol column shows
XOAUTH2for OAuth andTlsAuthLoginfor Basic auth. The report defaults to 7 days; widen the date range (up to 90 days) to confirm no Basic auth submissions remain from the mailbox. - Block Basic auth for the sending account. An Exchange Online authentication policy blocks Basic auth for every protocol by default unless you add
AllowBasicAuth*switches, so a new policy without-AllowBasicAuthSmtpblocks Basic SMTP for the users you assign it to:
New-AuthenticationPolicy -Name "Block Basic Auth"
Set-User -Identity app-mailer@contoso.com -AuthenticationPolicy "Block Basic Auth"
Set-User -Identity app-mailer@contoso.com -STSRefreshTokensValidFrom $([System.DateTime]::UtcNow)Policy changes take effect within 24 hours; the last command makes them apply within about 30 minutes. Then rotate the old mailbox password, because the application no longer needs it.
Troubleshooting
535 5.7.3 Authentication unsuccessful. Check, in order: the Exchange service principal was created with the Enterprise application Object ID; Add-MailboxPermission was granted to that service principal on the mailbox in the user= field; SmtpClientAuthenticationDisabled is not True for the mailbox; the token was requested with https://outlook.office365.com/.default; and the XOAUTH2 string uses 0x01 separators, not literal ^A text.
5.7.57 Client not authenticated to send mail. Microsoft groups this with 535 5.7.3 as an authentication failure. Run the same checks, confirm the AUTH step returned 235 2.7.0, and make sure the library isn't sending without authenticating.
5.7.60 SMTP; Client does not have permissions to send as this sender. The From address differs from the authenticated mailbox. Send from the mailbox named in the user= field, or grant Send As as described in Step 4 and test it.
AADSTS70011 (the provided value for the input parameter 'scope' is not valid). The scope value is wrong. Use exactly https://outlook.office365.com/.default, and don't combine it with scopes for another resource.
Connection fails or the device suggests port 465. Client submission requires TLS 1.2 or later on port 587 (or 25). Microsoft notes that a device or app that defaults to port 465 doesn't support the required TLS versions.
Sending stops at volume. Client submission is subject to mailbox sending limits; Microsoft's comparison lists 10,000 recipients per day and 30 messages per minute. For high-volume mail to internal recipients, Microsoft points to High Volume Email; for internal and external recipients, it points to Azure Communication Services Email.
New-ServicePrincipal fails. Reconnect with an account that has sufficient Exchange Online permissions, and confirm you passed both -AppId and -ObjectId.
Checklist
- App registration created, single tenant, with a certificate or secret stored outside the code.
SMTP.SendAsAppapplication permission added from Office 365 Exchange Online, admin consent granted.- Exchange service principal created with the Enterprise application Object ID.
FullAccessgranted on the sending mailbox, plusSendAsonly where a different From address is required.- SMTP AUTH enabled on that mailbox only.
- Token requested with
https://outlook.office365.com/.default; XOAUTH2 used onsmtp.office365.com:587with STARTTLS. - SMTP AUTH clients report shows
XOAUTH2for the mailbox. - Basic auth blocked for the account with an authentication policy, and the old password rotated.
While you review application access, check the other legacy dependency that is being switched off in the same period: EWS retirement and the EWSAllowedAppIDs allow list.
References
- Authenticate an IMAP, POP or SMTP connection using OAuth
- Updated Exchange Online SMTP AUTH Basic Authentication Deprecation Timeline
- Exchange Online to retire Basic auth for Client Submission (SMTP AUTH)
- OAuth 2.0 client credentials flow on the Microsoft identity platform
- Enable or disable SMTP AUTH in Exchange Online
- How to set up a multifunction device or application to send email using Microsoft 365 or Office 365
- Fix issues with printers, scanners, and LOB applications that send email using Microsoft 365
- SMTP AUTH clients report in the new EAC
- Disable Basic authentication in Exchange Online
- New-ServicePrincipal
- Get-MgServicePrincipal
- Acquire tokens with MSAL Python