Skip to content

SmtpHook OAuth2 (XOAUTH2) fails with 530 5.7.57 Client not authenticated #62775

Description

@manipatnam

Apache Airflow Provider(s)

smtp

Versions of Apache Airflow Providers

apache-airflow-providers-smtp==2.4.2

Apache Airflow version

2.11

Operating System

Linux

Deployment

Other 3rd-party Helm chart

Deployment details

No response

What happened

When using SmtpHook with auth_type="oauth2" against a server that requires STARTTLS (e.g. Microsoft 365 / smtp.office365.com:587), sending an email fails with:

smtplib.SMTPSenderRefused: (530, b'5.7.57 Client not authenticated to send mail.

 [SG2PR01CA0006.apcprd01.prod.exchangelabs.com 2025-05-14T10:01:47.209Z 08DD21B26F11F297]', 'user@example.com')

The XOAUTH2 AUTH command itself succeeds (the server returns 235 2.7.0 Authentication successful), but the subsequent MAIL FROM command is rejected because the session state has been reset.

What you think should happen instead

Email is sent successfully after XOAUTH2 authentication, as it does when using smtplib directly with the correct EHLO → STARTTLS → EHLO → AUTH XOAUTH2 → MAIL FROM sequence.

How to reproduce

  1. Configure an SMTP connection with OAuth2 credentials for Microsoft 365:

    • Host: smtp.office365.com
    • Port: 587
    • Login: user@example.com
    • Extra: {"disable_ssl": true, "from_email": "user@example.com", "auth_type": "oauth2", "client_id": "...", "client_secret": "...", "tenant_id": "..."}
  2. Use the connection via SmtpHook:

     with SmtpHook(smtp_conn_id="my_smtp_conn", auth_type="oauth2") as hook:
           hook.send_email_smtp(to="recipient@example.com", subject="Test", html_content="<p>Hello</p>")
    
  3. Observe SMTPSenderRefused: (530, b'5.7.57 Client not authenticated to send mail.').

Anything else

  1. Async path (aget_conn) has no OAuth2 support at all — it only handles basic auth via auth_login, silently ignoring auth_type="oauth2".
  2. get_conn uses self._auth_type instead of self.auth_type property — The auth_type property reads from connection extras and falls back to the constructor argument. Using the raw _auth_type attribute means setting auth_type in the connection extras JSON has no effect; the constructor default "basic" always wins.

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions