From ae6aeb9dbce75c2f2f262eb1adc6971046ccaf64 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 Apr 2026 09:54:55 -0500 Subject: [PATCH] thunderbird: map account auth methods Closes #5137 --- modules/programs/thunderbird.nix | 69 ++++++++++++-- .../modules/programs/thunderbird/default.nix | 2 + .../thunderbird/thunderbird-auth-methods.nix | 94 +++++++++++++++++++ .../thunderbird/thunderbird-auth-warning.nix | 85 +++++++++++++++++ 4 files changed, 241 insertions(+), 9 deletions(-) create mode 100644 tests/modules/programs/thunderbird/thunderbird-auth-methods.nix create mode 100644 tests/modules/programs/thunderbird/thunderbird-auth-warning.nix diff --git a/modules/programs/thunderbird.nix b/modules/programs/thunderbird.nix index 46b6f6700..67fa883d7 100644 --- a/modules/programs/thunderbird.nix +++ b/modules/programs/thunderbird.nix @@ -87,6 +87,49 @@ let if (builtins.isString address) then address else (address.address + address.realName) )); + thunderbirdAuthMethods = { + anonymous = 1; + clear = 3; + cram_md5 = 4; + digest_md5 = 4; + gssapi = 5; + login = 3; + ntlm = 6; + plain = 3; + xoauth2 = 10; + }; + + toThunderbirdAuthMethod = + authentication: if authentication == null then 3 else thunderbirdAuthMethods.${authentication} or 3; + + authMethodWarning = + name: authentication: + lib.optional (authentication != null && !builtins.hasAttr authentication thunderbirdAuthMethods) '' + ${moduleName}: accounts.email.accounts.${name} uses authentication method + '${authentication}', which Thunderbird does not support directly. Falling back + to password-based authentication. + ''; + + unsupportedAuthMethodWarnings = + account: + let + aliasWarnings = + alias: + lib.optionals (builtins.isAttrs alias && alias.smtp != null && alias.smtp != account.smtp) ( + authMethodWarning "${account.name}.aliases.${alias.address}.smtp" alias.smtp.authentication + ); + in + lib.optionals (account.imap != null) ( + authMethodWarning "${account.name}.imap" account.imap.authentication + ) + ++ lib.optionals (account.smtp != null) ( + authMethodWarning "${account.name}.smtp" account.smtp.authentication + ) + ++ lib.optionals (account.ews != null) ( + authMethodWarning "${account.name}.ews" account.ews.authentication + ) + ++ lib.concatMap aliasWarnings account.aliases; + toThunderbirdIdentity = account: address: # For backwards compatibility, the primary address reuses the account ID. @@ -128,7 +171,7 @@ let addressIsString = builtins.isString address; in optionalAttrs (!addressIsString && address.smtp != null) { - "mail.smtpserver.smtp_${id}.authMethod" = 3; + "mail.smtpserver.smtp_${id}.authMethod" = toThunderbirdAuthMethod address.smtp.authentication; "mail.smtpserver.smtp_${id}.hostname" = address.smtp.host; "mail.smtpserver.smtp_${id}.port" = if (address.smtp.port != null) then address.smtp.port else 587; "mail.smtpserver.smtp_${id}.try_ssl" = @@ -173,8 +216,11 @@ let "mail.server.server_${id}.type" = "imap"; "mail.server.server_${id}.userName" = account.userName; } + // optionalAttrs (account.imap != null && account.imap.authentication != null) { + "mail.server.server_${id}.authMethod" = toThunderbirdAuthMethod account.imap.authentication; + } // optionalAttrs (account.smtp != null) { - "mail.smtpserver.smtp_${id}.authMethod" = 3; + "mail.smtpserver.smtp_${id}.authMethod" = toThunderbirdAuthMethod account.smtp.authentication; "mail.smtpserver.smtp_${id}.hostname" = account.smtp.host; "mail.smtpserver.smtp_${id}.port" = if (account.smtp.port != null) then account.smtp.port else 587; "mail.smtpserver.smtp_${id}.try_ssl" = @@ -188,7 +234,7 @@ let } // optionalAttrs (account.ews != null) { "mail.smtpserver.ews_${id}.type" = "ews"; - "mail.outgoingserver.ews_${id}.auth_method" = 3; + "mail.outgoingserver.ews_${id}.auth_method" = toThunderbirdAuthMethod account.ews.authentication; "mail.outgoingserver.ews_${id}.ews_url" = account.ews.serviceDescriptionURL; "mail.outgoingserver.ews_${id}.key" = "ews_${id}"; "mail.outgoingserver.ews_${id}.username" = account.userName; @@ -210,6 +256,9 @@ let "mail.server.server_${id}.type" = "ews"; "mail.server.server_${id}.userName" = account.userName; } + // optionalAttrs (account.ews != null && account.ews.authentication != null) { + "mail.server.server_${id}.authMethod" = toThunderbirdAuthMethod account.ews.authentication; + } // builtins.foldl' (a: b: a // b) { } (map (address: toThunderbirdSMTP account address) addresses) // optionalAttrs (account.smtp != null && account.primary) { "mail.smtp.defaultserver" = "smtp_${id}"; @@ -951,12 +1000,14 @@ in ) ]; - warnings = lib.optionals (!cfg.darwinSetupWarning) [ - '' - Using programs.thunderbird.darwinSetupWarning is deprecated and will be - removed in the future. Thunderbird is now supported on Darwin. - '' - ]; + warnings = + lib.optionals (!cfg.darwinSetupWarning) [ + '' + Using programs.thunderbird.darwinSetupWarning is deprecated and will be + removed in the future. Thunderbird is now supported on Darwin. + '' + ] + ++ lib.flatten (map unsupportedAuthMethodWarnings enabledEmailAccounts); home.packages = [ cfg.package diff --git a/tests/modules/programs/thunderbird/default.nix b/tests/modules/programs/thunderbird/default.nix index 0fddeec16..c4700f2d1 100644 --- a/tests/modules/programs/thunderbird/default.nix +++ b/tests/modules/programs/thunderbird/default.nix @@ -1,5 +1,7 @@ { thunderbird = ./thunderbird.nix; + thunderbird-auth-methods = ./thunderbird-auth-methods.nix; + thunderbird-auth-warning = ./thunderbird-auth-warning.nix; thunderbird-with-firefox = ./thunderbird-with-firefox.nix; thunderbird-native-messaging-host = ./thunderbird-native-messaging-host.nix; } diff --git a/tests/modules/programs/thunderbird/thunderbird-auth-methods.nix b/tests/modules/programs/thunderbird/thunderbird-auth-methods.nix new file mode 100644 index 000000000..81170391b --- /dev/null +++ b/tests/modules/programs/thunderbird/thunderbird-auth-methods.nix @@ -0,0 +1,94 @@ +{ + config, + pkgs, + ... +}: +let + anonymousAccountId = builtins.hashString "sha256" "anonymous@example.com"; + digestAccountId = builtins.hashString "sha256" "digest@example.com"; + gssapiAccountId = builtins.hashString "sha256" "gssapi@example.com"; + mixedAccountId = builtins.hashString "sha256" "mixed@example.com"; + aliasSmtpId = builtins.hashString "sha256" "alias@example.comAlias"; + + inherit (pkgs.stdenv.hostPlatform) isDarwin; + profilesDir = if isDarwin then "Library/Thunderbird/Profiles" else ".thunderbird"; + userJs = "home-files/${profilesDir}/default/user.js"; +in +{ + accounts.email.accounts = { + "anonymous@example.com" = { + address = "anonymous@example.com"; + imap = { + host = "imap-anonymous.example.com"; + authentication = "anonymous"; + }; + primary = true; + realName = "Anonymous"; + thunderbird.enable = true; + }; + + "digest@example.com" = { + address = "digest@example.com"; + imap = { + host = "imap-digest.example.com"; + authentication = "digest_md5"; + }; + realName = "Digest"; + thunderbird.enable = true; + }; + + "gssapi@example.com" = { + address = "gssapi@example.com"; + ews = { + host = "ews-gssapi.example.com"; + serviceDescriptionURL = "https://ews-gssapi.example.com/EWS/Exchange.asmx"; + authentication = "gssapi"; + }; + realName = "GSSAPI"; + thunderbird.enable = true; + }; + + "mixed@example.com" = { + address = "mixed@example.com"; + aliases = [ + { + address = "alias@example.com"; + realName = "Alias"; + smtp = { + host = "smtp-alias.example.com"; + authentication = "xoauth2"; + }; + } + ]; + imap = { + host = "imap-mixed.example.com"; + authentication = "ntlm"; + }; + realName = "Mixed"; + smtp = { + host = "smtp-mixed.example.com"; + authentication = "clear"; + }; + thunderbird.enable = true; + }; + }; + + programs.thunderbird = { + enable = true; + package = config.lib.test.mkStubPackage { + name = "thunderbird"; + }; + + profiles.default.isDefault = true; + }; + + nmt.script = '' + assertFileContains ${userJs} 'user_pref("mail.server.server_${anonymousAccountId}.authMethod", 1);' + assertFileContains ${userJs} 'user_pref("mail.server.server_${digestAccountId}.authMethod", 4);' + assertFileContains ${userJs} 'user_pref("mail.server.server_${gssapiAccountId}.authMethod", 5);' + assertFileContains ${userJs} 'user_pref("mail.outgoingserver.ews_${gssapiAccountId}.auth_method", 5);' + assertFileContains ${userJs} 'user_pref("mail.server.server_${mixedAccountId}.authMethod", 6);' + assertFileContains ${userJs} 'user_pref("mail.smtpserver.smtp_${mixedAccountId}.authMethod", 3);' + assertFileContains ${userJs} 'user_pref("mail.smtpserver.smtp_${aliasSmtpId}.authMethod", 10);' + ''; +} diff --git a/tests/modules/programs/thunderbird/thunderbird-auth-warning.nix b/tests/modules/programs/thunderbird/thunderbird-auth-warning.nix new file mode 100644 index 000000000..7b01e8745 --- /dev/null +++ b/tests/modules/programs/thunderbird/thunderbird-auth-warning.nix @@ -0,0 +1,85 @@ +{ + config, + ... +}: +{ + accounts.email.accounts."hm@example.com" = { + address = "hm@example.com"; + imap = { + host = "imap.example.com"; + authentication = "oauthbearer"; + }; + primary = true; + realName = "Home Manager"; + thunderbird.enable = true; + }; + + accounts.email.accounts."exchange@example.com" = { + address = "exchange@example.com"; + ews = { + host = "ews.example.com"; + serviceDescriptionURL = "https://ews.example.com/EWS/Exchange.asmx"; + authentication = "oauthbearer"; + }; + realName = "Exchange Account"; + thunderbird.enable = true; + }; + + accounts.email.accounts."smtp@example.com" = { + address = "smtp@example.com"; + aliases = [ + { + address = "inherited-alias@example.com"; + realName = "Inherited Alias"; + } + { + address = "overridden-alias@example.com"; + realName = "Overridden Alias"; + smtp = { + host = "smtp-alias.example.com"; + authentication = "oauthbearer"; + }; + } + ]; + imap.host = "imap-smtp.example.com"; + realName = "SMTP Account"; + smtp = { + host = "smtp.example.com"; + authentication = "oauthbearer"; + }; + thunderbird.enable = true; + }; + + programs.thunderbird = { + enable = true; + package = config.lib.test.mkStubPackage { + name = "thunderbird"; + }; + + profiles.default.isDefault = true; + }; + + test.asserts.warnings.expected = [ + '' + programs.thunderbird: accounts.email.accounts.exchange@example.com.ews uses authentication method + 'oauthbearer', which Thunderbird does not support directly. Falling back + to password-based authentication. + '' + '' + programs.thunderbird: accounts.email.accounts.hm@example.com.imap uses authentication method + 'oauthbearer', which Thunderbird does not support directly. Falling back + to password-based authentication. + '' + '' + programs.thunderbird: accounts.email.accounts.smtp@example.com.smtp uses authentication method + 'oauthbearer', which Thunderbird does not support directly. Falling back + to password-based authentication. + '' + '' + programs.thunderbird: accounts.email.accounts.smtp@example.com.aliases.overridden-alias@example.com.smtp uses authentication method + 'oauthbearer', which Thunderbird does not support directly. Falling back + to password-based authentication. + '' + ]; + test.asserts.warnings.enable = true; +}