Microsoft 365

SMTP AUTH with OAuth client credentials for apps on Exchange Online

Move an application from Basic auth SMTP to OAuth: register an Entra app with SMTP.SendAsApp, register its service principal in Exchange Online and send with SASL XOAUTH2.

11 min read
On this page

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.SendAsApp permission.
  • 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:

WhenWhat changes
Now to December 2026Basic auth for SMTP AUTH behavior is unchanged
End of December 2026Basic auth for SMTP AUTH is disabled by default for existing tenants; admins can still enable it
New tenants created after December 2026Basic auth for SMTP AUTH is unavailable by default; OAuth is the supported method
Second half of 2027Microsoft 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:

PieceWhere it livesWhat it grants
SMTP.SendAsApp application permission with admin consentMicrosoft Entra IDThe app may request tokens for SMTP
Service principal registered with New-ServicePrincipalExchange OnlineExchange recognizes the app as a security principal
Mailbox permission for that service principalExchange OnlineThe 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-MailboxPermission and Set-CASMailbox. Microsoft notes that if New-ServicePrincipal fails 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

  1. In the Microsoft Entra admin center, open App registrations and select New registration.
  2. Give it a clear name, such as Contoso Invoicing SMTP, and select Accounts in this organizational directory only.
  3. On the Overview page, copy the Application (client) ID and Directory (tenant) ID.
  4. 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.
  1. In the app registration, open API permissions and select Add a permission.
  2. Select the APIs my organization uses tab and search for Office 365 Exchange Online.
  3. Select Application permissions, choose SMTP.SendAsApp, and select Add permissions.
  4. 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/.default

Step 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, DisplayName

The 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-List

Step 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 FullAccess

If 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 SmtpClientAuthenticationDisabled

False 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_in

A 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

  1. Send a test message and confirm it is delivered.
  2. Check the SMTP AUTH clients report. In the Exchange admin center go to Reports > Mail flow > SMTP AUTH clients. The Authentication Protocol column shows XOAUTH2 for OAuth and TlsAuthLogin for 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.
  3. 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 -AllowBasicAuthSmtp blocks 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.SendAsApp application permission added from Office 365 Exchange Online, admin consent granted.
  • Exchange service principal created with the Enterprise application Object ID.
  • FullAccess granted on the sending mailbox, plus SendAs only where a different From address is required.
  • SMTP AUTH enabled on that mailbox only.
  • Token requested with https://outlook.office365.com/.default; XOAUTH2 used on smtp.office365.com:587 with STARTTLS.
  • SMTP AUTH clients report shows XOAUTH2 for 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

Questions people ask

When does Basic authentication for SMTP AUTH stop working in Exchange Online?

Microsoft's current timeline keeps Basic auth for SMTP AUTH unchanged until December 2026. At the end of December 2026 it is disabled by default for existing tenants, although administrators can still enable it, and new tenants created after December 2026 don't get it by default. Microsoft plans to announce the final removal date in the second half of 2027.

Which permission does an app need to send mail over SMTP with client credentials?

Add the SMTP.SendAsApp application permission from the Office 365 Exchange Online API and grant admin consent. That alone is not enough: an Exchange administrator must also register the app's service principal with New-ServicePrincipal and grant it permission on the mailbox it sends from.

What scope should the token request use for SMTP?

For the client credentials flow, Microsoft requires the scope https://outlook.office365.com/.default in the token request body. Delegated flows use https://outlook.office.com/SMTP.Send instead.

Why do I get 535 5.7.3 Authentication unsuccessful with a valid token?

The most common causes are registering the Exchange service principal with the Object ID from App registrations instead of the Object ID from Enterprise applications, a missing mailbox permission for the service principal, or SMTP AUTH disabled on the sending mailbox. Check all three before you look at the code.

Exchange OnlineSMTP AUTHOAuth 2.0Entra ID app registration
  1. Fix 550 5.7.30 Basic authentication is not supported for Client Submission

    A printer, scanner or app stopped sending mail through Exchange Online with 550 5.7.30. Find the sender and move it to OAuth, High Volume Email or an SMTP relay connector.

    Microsoft 36512 min read
  2. SMTP AUTH vs Direct Send vs relay connector: sending mail from devices

    Compare SMTP AUTH, Direct Send, an SMTP relay connector, High Volume Email and Azure Communication Services, and pick the right way for printers and apps to send through Microsoft 365.

    Microsoft 36511 min read
  3. Calendar permissions in Exchange Online: Add-MailboxFolderPermission guide

    Share calendars, change the organization-wide Default permission and add calendar delegates in Exchange Online with Add-, Set- and Remove-MailboxFolderPermission, including localized folder names.

    Microsoft 3659 min read