Repository: thunderbird/thunderbird-android (language: kotlin)

Issue:
outlook.com outgoing server fails when enterprise or educational accounts
#8024 is still happening for @Hanxyz13. Let's find out why.

@Hanxyz13 could you provide the logs here instead?


Discussion hints:
[23_05-10-27-20_935.zip](https://github.com/user-attachments/files/20403111/23_05-10-27-20_935.zip)
I re-tried logging in to outlook at Thunderbird for Android, and recorded the logs, hope it works for you!
@kewisch  Fwiw, this issue (detailed in #8024) is also affecting me.
I don't know exactly when the underlying issue began, but noticed this a couple of weeks back when I tried sending an email from K9-mail, and I started getting persistent notifications that the mail couldn't be sent because of authentication issues.

I tried resolving it by configuring the outgoing mailserver again, but kept getting the similar authentication errors as in the screenshots in #8024). I tried this with both K9 and Thunderbird clients, and with both possible smtp servers `smtp.office365.com` and the newly recommended `smtp-mail.outlook.com`. No setting works.

Fwiw, following instructions from the other issue I also attach the debug logs here:
[k9mail-logs.txt](https://github.com/user-attachments/files/21959006/k9mail-logs.txt)
Same here, doesn't work neither with 10.1 nor the beta 12.0b5 from Play Store 
@kewisch any idea what the issue is?
This is blocking being able to use the app (Thunderbird or K9-mail) for any work stuff.
Even if the imap setup works, the smtp authentication failing means we can't add an outlook account.

Fwiw, this appears to be separate from #9637 (which was added to sprints earlier and marked resolved). Adding an outlook account always fails, and I then need to try adding it manually, picking servers and ports as required. In this case, for smtp, using `smtp-mail.outlook.com` manually with `STARTTLS` and `OAuth2.0` still returns the same authentication error.
I'm not sure unfortunately. The situation I was able to reproduce with my outlook account we fixed, and the recent issue you noted as well. Could you share more details about your Outlook account setup? Have you had the account back from Hotmail days? Do you have more than one email address with Outlook? Do you have all the IMAP/SMTP settings enabled in the Outlook interface (there are a few to enable I think IMAP for example)? Anything else that might seem strange?

If you are proficient with python I can share a script that will try to emulate the exchange, which might make debugging a bit easier.
I doubt you'd want to give me access to your email account, but if you can reproduce it with a new account and have steps that might help as well.
@kewisch I don't have anything special set for my outlook account; it is set up by the employer via the Office365 enterprise offering. But this is not something recent; it's been the same account for coming up to 5 years, with imap and smtp support always enabled as standard. Things have worked with K9-mail all the way through until the recent issues that prompted this thread. Even when MFA and OAuth was brought in, there wasn't any issue I faced with the office365 accounts.

Fwiw, I also use `offlineimap` and `msmtp` on my laptop to connect to the same account, and they work just fine.
For this I use [oama](https://github.com/pdobsan/oama) to handle the OAuth token generation and refresh, and the two clients can use this token just fine to fetch and send email on this account.

I'm comfortable with python, but will need to be careful about exposing any PII or related information when running any connection scripts; what did you want me to try? Were the attached logs above useful to narrow things down?
@kewisch To make absolutely sure it's not the outlook account that's the issue, I installed [FairMail](https://email.faircode.eu/) on my phone and used it to setup things. All I needed to do was provide email, and login to account on the browser, and everything works.
With Thunderbird I can't just add the account and have it autodiscover setting. I need to wait for it to fail, and then setup the account manually. I can then setup imap which requires login from the browser to authenticate, and that works fine, but smtp fails as above.
I'm running Thunderbird version 12.1 fom fdroid.
Hi @iffsid 

Your log is full of connection failures attempting to retrieve the autoconfig xml from the server url. Different ports and IP's.

Have you looked into this at all? Maybe try and manually pull down the config file using a browser to verify connection to the server.

RealAutoconfigFetcher: Error fetching Autoconfig from URL: http://<redacted>/.well-known/autoconfig/mail/config-v1.1.xml 08-24 21:50:30.192 1828 2599 D RealAutoconfigFetcher: java.net.SocketTimeoutException: failed to connect to <redacted>/129.215.97.62 (**port 80**) from /192.168.1.49 (port 45750) after 10000ms 

RealAutoconfigFetcher: Error fetching Autoconfig from URL: https://<redacted>/.well-known/autoconfig/mail/config-v1.1.xml 08-24 21:50:30.204 1828 2599 D RealAutoconfigFetcher: java.net.SocketTimeoutException: failed to connect to <redacted>/129.215.97.62 (**port 443**) from /192.168.1.49 (port 42812) after 10000ms
@platform34 Thanks for checking :)
I don't have the original unredacted logs, so I'm not sure exactly what those connection urls were. 
However, I enabled debugging logs and tried connecting to/setting up the account again, but can't replicate those autoconfig issues.
New logs attached below from when I add a new account, to when smtp authorisation fails.
[k9mail-logs-new.txt](https://github.com/user-attachments/files/22320093/k9mail-logs-new.txt)

Edit: For reference, the steps are
- enable debug logs
- add new account - use enterprise account email
- (should redirect to weblogin, but spins for a couple of mins and fails) select manual config
- provide imap server details and specify OAuth2.0; now get taken to browser to weblogin on enterprise account
- successful server connection for imap, proceed to smtp
- provide smtp server details and specify OAuth2.0; authentication failure
- click back .. to get back to settings screen, and export logs
Thanks @iffsid 

The log only confirms the smtp oAuth authentication was unsuccessful and failed.

I would start by checking the server configuration. 
Is Authenticated SMTP enabled on the mailbox (or tenant level)? Presumably yes if Fairmail works!
Has the Thunderbird app (client ID) been registered and authorised on the server for SMTP?

```
09-14 13:48:57.908 13320 13358 W SmtpTransport: saslOAuth: failed to authenticate.
09-14 13:48:57.908 13320 13358 W SmtpTransport: com.fsck.k9.mail.transport.smtp.NegativeSmtpReplyException: Authentication unsuccessful [FR2P28<REDACTED>81.PROD.OUTLOOK.COM

09-14 13:49:03.600 13320 13358 E SmtpTransport: Error while checking server settings
09-14 13:49:03.600 13320 13358 E SmtpTransport: com.fsck.k9.mail.AuthenticationFailedException: AUTH XOAUTH2 failed

09-14 13:49:03.600 13320 13358 E SmtpTransport: Caused by: com.fsck.k9.mail.transport.smtp.NegativeSmtpReplyException: Authentication unsuccessful [FR2P281CA0005.DEUP281.PROD.OUTLOOK.COM 
```

@platform34 Yes, I believe it has been registered for smtp; I seem to be able to use thunderbird on the laptop just fine, and when I do the weblogin for the app (during imap setup), I am asked if I wish to allow Thunderbird access to my account, and say yes. I don't get asked for smtp separately.

This is only a recent issue fwiw; k9-mail was working fine until about a month ago when sending email on outlook accounts started to fail. Imap continues to work fine. 

I've also checked with a different enterprise office365 login (different domain) and the same issue persists. 
Adding gmail or gsuite (with own domain) email account works fine, including autodiscovery.
Hi @iffsid 

I had looked through the below link which shows that apps need to be registered for use with Office365 corporate email. 
https://cknotes.com/office365-app-setup-smtp-pop-imap-oauth2-client-credentials

I believe Thunderbird desktop and K9-mail have a different client ID from Thunderbird for Android.

The link also shows that Imap and Smtp permissions are granted independently. This process is distinct to the approval you give when granting Thunderbird access to your account.

Your corporate setup may be different, i'm just giving some pointers to look at.
@platform34 thanks for the link; appreciate the pointers. :)
I don't believe I have access to the settings on the link; likely fully enterprise controlled.

I had considered this might have been an enterprise level change first up, but the IT team have responded that they've not enabled any restrictions on smtp. The last change was OAuth enforcement, but that was about 2 years back.
There was also [this](https://learn.microsoft.com/en-us/answers/questions/4742336/authentication-failed-for-oulook-in-k9-email-clien) issue raised in an office365 forum.

Also, from another prior [issue](https://forum.k9mail.app/t/adding-additional-e-mail-address-to-k-9-mail/10407/2), if smtp was disabled, then the error message notes that it has been disabled for the tenant. The error I get and is shown in #8024 makes no mention of any such specifics.

I'm puzzled by the fact that this is only happening with the mobile thunderbird/k9-mail app. Things work fine with thunderbird on desktop, fairmail, and my manual mu/mu4e setup (`oama` for token management, and `offlineimap` and `msmtp`).
Thanks.

Does your account give access to a "shared mailbox"?

I'm wondering if [this question](https://learn.microsoft.com/en-us/answers/questions/162233/shared-mailbox-oauth-access) on a microsoft forum is the same issue your experiencing.

_"In case of shared mailbox access using OAuth, application needs to obtain the access token on behalf of a user but replace the userName field in the SASL XOAUTH2 encoded string with the email address of the shared mailbox."_


@platform34 No, it's not a shared mailbox account either. Double checked with IT.
Incidentally, the reason I'm thinking this isn't specifically an account issue, but app one, is because I have two separate outlook enterprise accounts with two different institutions. 
This issue crops up with both, only when using the Thunderbird Android/K9-mail apps.
FWIW, I have the same OAuth SMTP authentication failure issues with my educational Outlook account which has always worked with Thunderbird Desktop and with K-9 Mail until a few months ago. IT claims nothing was changed on their side.
I've been working around it by using the Outlook App only for this account, and have been subscribed to this issue to check in from time to time.

My logs look pretty similar to the one posted above, with the only indication of anything relevant is the SMTP OAuth failure exception.

I've tried both 12.0b7 and 13.0b1.

[k9mail-logs.txt](https://github.com/user-attachments/files/22336923/k9mail-logs.txt)
In 13.0b1 (just installed from Play Store) my hotmail/outlook Microsoft account was auto-detected and just works!

Thanks! 
Hi everyone,

Thanks for all the helpful comments. This is a tricky bug because we can't reproduce it with our Outlook accounts. It seems to mostly affect **enterprise and school accounts**, which we don't have access to.

We believe this is similar to the "shared mailbox" issue, or when you have an account that uses an alias. As I understand it, when using this kind of account, IMAP authenticates correctly with your regular email, but SMTP only authenticates using an email information returned inside the `id_token`. When you log in, Outlook sometimes provides multiple email addresses, and it's not clear which one to use for SMTP authentication. Our previous fix used a single property, which doesn't work for everyone.

I've created a test build that will now **try to authenticate with every email address** that we could identify that Outlook provides in the `id_token`. Since we can't test this ourselves, we need your help.

### How You Can Help

Please test the debug APK linked below and let us know if you can authenticate with the Outgoing SMTP server after the changes.

  * **[Download the debuggable APK here](https://drive.google.com/file/d/1P3vDJkJQVHm5EQQBgPmtxu6UFEr3Nzsz/view?usp=sharing)**

If it still fails, please do the following:

1.  Try to set up the account again.
2.  If you still can't log in, share the failure log with us. This will let us see all the email options Outlook is providing via `id_token`.

You can **redact your personal info** from the log. Just replace your name and emails with placeholders like `<name>`, `<email 1>`, and `<email 2>`, as shown in the example below.

**Example of a redacted log:**

```
idToken = {"name":"<name>","preferred_username":"<email 1>","email":"<email 2>", ...}
users = [<email 1>, <email 2>]
trying to authenticate with user '<email 1>'
```

If you don't know how to export the logs, please refer to the [Collecting Debug Logs](https://thunderbird.github.io/thunderbird-android/docs/latest/user-guide/troubleshooting/collecting-debug-logs.html#collecting-debug-logs) documentation

Thank you for your help!
@rafaeltonholo 

Thank you for investigating this issue! Your assessment seems to have been correct — the provided Debug build fixed the issue and successfully sets up SMTP for my educational Outlook account. I can verify I had no problems with account setup, sending (and receiving) mail through this version!


I just came across [this thread](https://forum.k9mail.app/t/psa-outlook-com-outgoing-server-requires-your-primary-account-alias/10450) on the K9 forum from Sept 2024 where a user had the same issue. Their account had multiple alias and they found a solution was to use the user name the account was first created with for the outgoing smtp user. It may help confirm the hypothesis.
@rafaeltonholo Thanks very much, can confirm this fixes the smtp auth issue!
The autodiscovery still fails for me however; I need to manually input imap and smtp server details.
Is this still to be expected, or should I get logs and investigate?

@platform34 I did come across that one as well, but didn't apply for me. To give a bit of context, the way our accounts are setup, the email you login to your enterprise/school account with always has to be the first-setup-account for which the user id etc is created by the institution, not the user. 
One can then request an alias to make things easier, but login everywhere is with the primary user id.
> [@rafaeltonholo](https://github.com/rafaeltonholo) Thanks very much, can confirm this fixes the smtp auth issue! The autodiscovery still fails for me however; I need to manually input imap and smtp server details. Is this still to be expected, or should I get logs and investigate?

The auto-discovery is a different issue. Could you please open another ticket for that? Also, if possible, could you please share the email provider you are using (`@gmail.com`, `@outlook.com`, etc)? That will help to diagnose why the auto-discovery isn't working.



Thanks @yutotakano and @iffsid for confirming the APK fix the issue. I will push a PR soon to include the changes, and once it is released on the Daily build, I will kindly ask you for a test again, just to make sure it still works, as the patch will be slightly different from the APK version.

Pull request: fix(smtp): outlook smtp not authenticating when enterprise or educational accounts
Fixes #9160.

Test command: ./gradlew :mail:protocols:smtp:test --tests "com.fsck.k9.mail.transport.smtp.SmtpServerSettingsValidatorTest" :mail:protocols:imap:test --tests "com.fsck.k9.mail.store.imap.ImapServerSettingsValidatorTest" --tests "com.fsck.k9.mail.store.imap.RealImapConnectionTest"

Fix patch:
diff --git a/app-thunderbird/src/debug/kotlin/net/thunderbird/android/auth/TbOAuthConfigurationFactory.kt b/app-thunderbird/src/debug/kotlin/net/thunderbird/android/auth/TbOAuthConfigurationFactory.kt
index eccbf45aaf5..f95419d0855 100644
--- a/app-thunderbird/src/debug/kotlin/net/thunderbird/android/auth/TbOAuthConfigurationFactory.kt
+++ b/app-thunderbird/src/debug/kotlin/net/thunderbird/android/auth/TbOAuthConfigurationFactory.kt
@@ -65,6 +65,7 @@ class TbOAuthConfigurationFactory : OAuthConfigurationFactory {
         ) to OAuthConfiguration(
             clientId = "e6f8716e-299d-4ed9-bbf3-453f192f44e5",
             scopes = listOf(
+                "profile",
                 "openid",
                 "email",
                 "https://outlook.office.com/IMAP.AccessAsUser.All",
diff --git a/legacy/common/src/main/java/com/fsck/k9/backends/RealOAuth2TokenProvider.kt b/legacy/common/src/main/java/com/fsck/k9/backends/RealOAuth2TokenProvider.kt
index 10f56497450..6921746cdcc 100644
--- a/legacy/common/src/main/java/com/fsck/k9/backends/RealOAuth2TokenProvider.kt
+++ b/legacy/common/src/main/java/com/fsck/k9/backends/RealOAuth2TokenProvider.kt
@@ -21,13 +21,33 @@ class RealOAuth2TokenProvider(
     private val authService = AuthorizationService(context)
     private var requestFreshToken = false
 
-    override val primaryEmail: String?
+    override val usernames: Set<String>
         get() {
-            return parseAuthState()
-                .parsedIdToken
-                ?.additionalClaims
-                ?.get("email")
-                ?.toString()
+            val idTokenClaims = parseAuthState().parsedIdToken?.additionalClaims.orEmpty()
+            return buildSet {
+                // https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference#payload-claims
+                // https://docs.azure.cn/en-us/entra/identity-platform/optional-claims-reference
+                // requires profile scope
+                idTokenClaims["preferred_username"]?.let { add(it.toString()) }
+                // requires email scope
+                idTokenClaims["email"]?.let { add(it.toString()) }
+                // only present for v1.0 tokens
+                idTokenClaims["unique_name"]?.let { add(it.toString()) }
+                // requires profile scope
+                idTokenClaims["upn"]?.let { add(it.toString()) }
+                idTokenClaims["verified_primary_email"]?.let { verifiedPrimaryEmail ->
+                    when (verifiedPrimaryEmail) {
+                        is List<*> -> addAll(verifiedPrimaryEmail.map { it.toString() })
+                        else -> add(verifiedPrimaryEmail.toString())
+                    }
+                }
+                idTokenClaims["verified_secondary_email"]?.let { verifiedSecondaryEmail ->
+                    when (verifiedSecondaryEmail) {
+                        is List<*> -> addAll(verifiedSecondaryEmail.map { it.toString() })
+                        else -> add(verifiedSecondaryEmail.toString())
+                    }
+                }
+            }
         }
 
     @Suppress("TooGenericExceptionCaught")
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/OAuth2TokenProvider.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/OAuth2TokenProvider.kt
index 076f5912486..3ec99e5b7f6 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/OAuth2TokenProvider.kt
+++ b/mail/common/src/main/java/com/fsck/k9/mail/oauth/OAuth2TokenProvider.kt
@@ -11,15 +11,14 @@ interface OAuth2TokenProvider {
     }
 
     /**
-     * Fetch the primary email found in the id_token additional claims,
-     * if it is available.
+     * A set of usernames fetched from the `id_token`.
      *
      * > Some providers, like Microsoft, require this as they need the primary account email to be the username,
-     * not the email the user entered
+     * > not the email the user entered for SMTP authentication.
      *
-     * @return the primary email present in the id_token, otherwise null.
+     * @throws AuthenticationFailedException If no AuthState is available.
      */
-    val primaryEmail: String?
+    val usernames: Set<String>
         @Throws(AuthenticationFailedException::class)
         get
 
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt
index f7cfa634e66..2314e5588f5 100644
--- a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt
+++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt
@@ -155,6 +155,7 @@ class SmtpTransport(
                 AuthType.NONE -> {
                     // The outgoing server is configured to not use any authentication. So do nothing.
                 }
+
                 AuthType.PLAIN -> {
                     // try saslAuthPlain first, because it supports UTF-8 explicitly
                     if (authPlainSupported) {
@@ -165,6 +166,7 @@ class SmtpTransport(
                         throw MissingCapabilityException("AUTH PLAIN")
                     }
                 }
+
                 AuthType.CRAM_MD5 -> {
                     if (authCramMD5Supported) {
                         saslAuthCramMD5()
@@ -172,6 +174,7 @@ class SmtpTransport(
                         throw MissingCapabilityException("AUTH CRAM-MD5")
                     }
                 }
+
                 AuthType.XOAUTH2 -> {
                     if (oauthTokenProvider == null) {
                         throw MessagingException("No OAuth2TokenProvider available.")
@@ -183,6 +186,7 @@ class SmtpTransport(
                         throw MissingCapabilityException("AUTH OAUTHBEARER")
                     }
                 }
+
                 AuthType.EXTERNAL -> {
                     if (authExternalSupported) {
                         saslAuthExternal()
@@ -190,6 +194,7 @@ class SmtpTransport(
                         throw MissingCapabilityException("AUTH EXTERNAL")
                     }
                 }
+
                 else -> {
                     throw MessagingException("Unhandled authentication method found in server settings (bug).")
                 }
@@ -551,26 +556,68 @@ class SmtpTransport(
     private fun saslOAuth(method: OAuthMethod) {
         Log.d("saslOAuth() called with: method = $method")
         retryOAuthWithNewToken = true
+        checkNotNull(oauthTokenProvider) { "No OAuth2TokenProvider available." }
 
-        val primaryEmail = oauthTokenProvider?.primaryEmail
-        val primaryUsername = primaryEmail ?: username
+        val users = buildSet {
+            // add the given username to the list of users
+            add(username)
+            // add all the usernames we can fetch from the id_token to be used in case
+            // the given username fails to authenticate.
+            addAll(oauthTokenProvider.usernames)
+        }.toMutableSet()
 
-        try {
-            attempOAuth(method, primaryUsername)
-        } catch (negativeResponse: NegativeSmtpReplyException) {
-            Log.w(negativeResponse, "saslOAuth: failed to authenticate.")
-            if (negativeResponse.replyCode != SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
-                throw negativeResponse
-            }
+        val negativeResponses = authenticateUsers(users, method)
 
-            oauthTokenProvider!!.invalidateToken()
+        if (negativeResponses.isNotEmpty()) {
+            Log.w("failed to authenticate with all discovered users.")
+            val (user, negativeResponse) = negativeResponses[username]?.let { username to it }
+                ?: negativeResponses.entries.first().toPair()
+
+            logger.log("invalidating current token")
+            oauthTokenProvider.invalidateToken()
 
             if (!retryOAuthWithNewToken) {
                 handlePermanentOAuthFailure(method, negativeResponse)
             } else {
-                handleTemporaryOAuthFailure(method, primaryUsername, negativeResponse)
+                handleTemporaryOAuthFailure(
+                    method = method,
+                    username = user,
+                    negativeResponseFromOldToken = negativeResponse,
+                )
+            }
+        }
+    }
+
+    private fun authenticateUsers(
+        users: MutableSet<String>,
+        method: OAuthMethod,
+    ): MutableMap<String, NegativeSmtpReplyException> {
+        val negativeResponses = mutableMapOf<String, NegativeSmtpReplyException>()
+        val sensitiveLog = "*sensitive*"
+        logger.log("users = ${users.takeIf { K9MailLib.isDebugSensitive() } ?: "[${users.size} emails found]"}")
+
+        val iterator = users.iterator()
+        while (iterator.hasNext()) {
+            val user = iterator.next()
+            try {
+                logger.log(
+                    "trying to authenticate with user '${
+                        user.takeIf { K9MailLib.isDebugSensitive() } ?: sensitiveLog
+                    }'",
+                )
+                attempOAuth(method, user)
+                negativeResponses.clear()
+                break
+            } catch (negativeResponse: NegativeSmtpReplyException) {
+                Log.w(negativeResponse, "saslOAuth: failed to authenticate.")
+                if (negativeResponse.replyCode != SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
+                    throw negativeResponse
+                }
+                iterator.remove()
+                negativeResponses += user to negativeResponse
             }
         }
+        return negativeResponses
     }
 
     private fun handlePermanentOAuthFailure(


Test patch:
diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapServerSettingsValidatorTest.kt b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapServerSettingsValidatorTest.kt
index dcd55a9826a..b0ff10e5672 100644
--- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapServerSettingsValidatorTest.kt
+++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapServerSettingsValidatorTest.kt
@@ -351,7 +351,7 @@ class ImapServerSettingsValidatorTest {
     }
 }
 
-class FakeOAuth2TokenProvider(override val primaryEmail: String? = null) : OAuth2TokenProvider {
+class FakeOAuth2TokenProvider(override val usernames: Set<String> = emptySet()) : OAuth2TokenProvider {
     override fun getToken(timeoutMillis: Long): String {
         return AUTHORIZATION_TOKEN
     }
diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapConnectionTest.kt b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapConnectionTest.kt
index c34fdd6c2f4..27b3c133b63 100644
--- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapConnectionTest.kt
+++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapConnectionTest.kt
@@ -1273,7 +1273,7 @@ class RealImapConnectionTest {
     }
 }
 
-class TestTokenProvider(override val primaryEmail: String? = null) : OAuth2TokenProvider {
+class TestTokenProvider(override val usernames: Set<String> = emptySet()) : OAuth2TokenProvider {
     private var invalidationCount = 0
 
     override fun getToken(timeoutMillis: Long): String {
diff --git a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpServerSettingsValidatorTest.kt b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpServerSettingsValidatorTest.kt
index ad880c998a1..4865eb30925 100644
--- a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpServerSettingsValidatorTest.kt
+++ b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpServerSettingsValidatorTest.kt
@@ -124,7 +124,7 @@ class SmtpServerSettingsValidatorTest {
             trustedSocketFactory = trustedSocketFactory,
             oAuth2TokenProviderFactory = { authStateStorage ->
                 assertThat(authStateStorage.getAuthorizationState()).isEqualTo(AUTHORIZATION_STATE)
-                FakeOAuth2TokenProvider(primaryEmail = expectedUser)
+                FakeOAuth2TokenProvider(usernames = setOf(expectedUser))
             },
         )
 
@@ -136,7 +136,14 @@ class SmtpServerSettingsValidatorTest {
             output("250-AUTH PLAIN LOGIN XOAUTH2")
             output("250 HELP")
 
-            val ouathBearer = "user=${expectedUser}\u0001auth=Bearer ${AUTHORIZATION_TOKEN}\u0001\u0001"
+            var ouathBearer = "user=${USERNAME}\u0001auth=Bearer ${AUTHORIZATION_TOKEN}\u0001\u0001"
+                .encodeUtf8()
+                .base64()
+
+            expect("AUTH XOAUTH2 $ouathBearer")
+            output("535 5.7.3 Authentication unsuccessful")
+
+            ouathBearer = "user=${expectedUser}\u0001auth=Bearer ${AUTHORIZATION_TOKEN}\u0001\u0001"
                 .encodeUtf8()
                 .base64()
 
@@ -397,7 +404,7 @@ class SmtpServerSettingsValidatorTest {
     }
 }
 
-class FakeOAuth2TokenProvider(override val primaryEmail: String? = null) : OAuth2TokenProvider {
+class FakeOAuth2TokenProvider(override val usernames: Set<String> = emptySet()) : OAuth2TokenProvider {
     override fun getToken(timeoutMillis: Long): String {
         return AUTHORIZATION_TOKEN
     }