Search code examples
oauth-2.0outlookmicrosoft-graph-apismtp-authmicrosoft365

Authenticating to Microsoft 365 SMTP servers via OAuth2 only works for users without MFA


I am trying to migrate a legacy application that sends emails via SMTP to use OAuth2 tokens instead of the classic username/password combination.

I'm authenticating against Microsoft 365, but I have a problem: the authentication only works for users that have MFA disabled. If I try with users that have MFA enabled I always get the following error:

451: 4.7.0 Temporary server error. Please try again later. PRX5 [MI0P293CA0009.ITAP293.PROD.OUTLOOK.COM 2025-01-15T10:49:05.867Z 08DD352FCDF1BC4A]

I've double checked everything I can think of, including:

  • My app registration in Microsoft 365: I can confirm that I've added and approved the Delegated SMTP.Send permission that is required for this operation
  • The token: I'm generating it via the official Microsoft C# client, with the delegated flow and requesting the scopes that are used for Outlook operations (https://outlook.office365.com/.default)
  • Tried with the same user multiple times and can confirm it works without issues if I disable MFA for the user but it stops working if I enable it

In case it is important, I'm using C# and MailKit to try to send the email, but I don't think it matters since it authenticates fine if MFA is disabled, so it is definitely a configuration/token issue.

If it is useful, here is a copy of the JWT token's payload (with sensitive informations redacted for privacy reasons):

{
  "aud": "https://outlook.office365.com",
  "iss": "https://sts.windows.net/REDACTED/",
  "iat": 1736937330,
  "nbf": 1736937330,
  "exp": 1736942177,
  "acct": 0,
  "acr": "1",
  "aio": "REDACTED",
  "amr": [
    "pwd",
    "mfa"
  ],
  "app_displayname": "My App Name",
  "appid": "REDACTED",
  "appidacr": "1",
  "enfpolids": [],
  "family_name": "REDACTED",
  "given_name": "REDACTED",
  "idtyp": "user",
  "ipaddr": "REDACTED",
  "login_hint": "REDACTED",
  "name": "REDACTED",
  "oid": "REDACTED",
  "puid": "REDACTED",
  "rh": "REDACTED",
  "scp": "Files.ReadWrite.All Group.ReadWrite.All SMTP.Send User.Read",
  "sid": "REDACTED",
  "signin_state": [
    "kmsi"
  ],
  "sub": "REDACTED",
  "tid": "REDACTED",
  "unique_name": "REDACTED",
  "upn": "REDACTED",
  "uti": "REDACTED",
  "ver": "1.0",
  "wids": [
    "REDACTED",
    "REDACTED",
    "REDACTED",
    "REDACTED",
    "REDACTED",
    "REDACTED",
    "REDACTED",
    "REDACTED",
    "REDACTED",
    "REDACTED",
    "REDACTED",
    "REDACTED",
    "REDACTED"
  ],
  "xms_aud_guid": "REDACTED",
  "xms_idrel": "1 12"
}

Solution

  • I've done more research and it seems (no direct confirmation on this in Microsoft documentation, but sources on various dev forums and github seems to confirm this) that if MFA is enabled you simply cannot authenticate to SMTP via Delegated access token.

    An alternative solution is to use the Application-level permission Office 365 Exchange Online -> SMTP.SendAsApp and use the app directly to send the email on behalf of the users we want.

    Note that this is not a Microsoft Graph API, it's an Exchange Online API and thus it requires additional configuration to allow the Application's service principal to send emails on behalf of the user(s). The detailed configuration instructions can be found here:

    https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth