diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 4c7d4ffc9708..2dd3e2c24d12 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -17,6 +17,7 @@ + @@ -72,7 +73,7 @@ - + diff --git a/src/Core/MailTemplates/Mjml/Handlebars/Auth/send-email-otp.html.hbs b/src/Core/MailTemplates/Mjml/Handlebars/Auth/send-email-otp.html.hbs new file mode 100644 index 000000000000..095cdc82d797 --- /dev/null +++ b/src/Core/MailTemplates/Mjml/Handlebars/Auth/send-email-otp.html.hbs @@ -0,0 +1,675 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + +
+ + + + + + + + +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + + + + + +
+ + + + + + + +
+ + + +
+ +
+ +

+ Verify your email to access this Bitwarden Send +

+ +
+ +
+ + + +
+ + + + + + + + + +
+ + + + + + + +
+ + + +
+ +
+ +
+ + +
+ +
+ + + + + +
+ + +
+ +
+ + + + + + + + + +
+ + + + + + + +
+ + + +
+ + + + + + + +
+ + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
Your verification code is:
+ +
+ +
{{Token}}
+ +
+ +
+ +
+ +
This code expires in {{Expiry}} minutes. After that, you'll need to + verify your email again.
+ +
+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + +
+ + + + + + + + + +
+ +

+ Bitwarden Send transmits sensitive, temporary information to + others easily and securely. Learn more about + Bitwarden Send + or + sign up + to try it today. +

+ +
+ +
+ +
+ + +
+ +
+ + + +
+ +
+ + + + + + + + + +
+ + + + + + + +
+ + + +
+ + + + + + + +
+ + +
+ + + + + + + + + +
+ +

+ Learn more about Bitwarden +

+ Find user guides, product documentation, and videos on the + Bitwarden Help Center.
+ +
+ +
+ + + +
+ + + + + + + + + +
+ + + + + + + +
+ + + +
+ +
+ +
+ + +
+ +
+ + + +
+ +
+ + + + + + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + +
+ +

+ © 2025 Bitwarden Inc. 1 N. Calle Cesar Chavez, Suite 102, Santa + Barbara, CA, USA +

+

+ Always confirm you are on a trusted Bitwarden domain before logging + in:
+ bitwarden.com | + Learn why we include this +

+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + \ No newline at end of file diff --git a/src/Core/MailTemplates/Mjml/Handlebars/Auth/send-email-otp.text.hbs b/src/Core/MailTemplates/Mjml/Handlebars/Auth/send-email-otp.text.hbs new file mode 100644 index 000000000000..7c9c1db527af --- /dev/null +++ b/src/Core/MailTemplates/Mjml/Handlebars/Auth/send-email-otp.text.hbs @@ -0,0 +1,9 @@ +{{#>BasicTextLayout}} +Verify your email to access this Bitwarden Send. + +Your verification code is: {{Token}} + +This code can only be used once and expires in {{Expiry}} minutes. After that you'll need to verify your email again. + +Bitwarden Send transmits sensitive, temporary information to others easily and securely. Learn more about Bitwarden Send or sign up to try it today. +{{/BasicTextLayout}} diff --git a/src/Core/MailTemplates/Mjml/Handlebars/Auth/two-factor.html.hbs b/src/Core/MailTemplates/Mjml/Handlebars/Auth/two-factor.html.hbs new file mode 100644 index 000000000000..6d53f1278257 --- /dev/null +++ b/src/Core/MailTemplates/Mjml/Handlebars/Auth/two-factor.html.hbs @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + +
+ + + + + + + +
+ + + +
+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + + + + +
+ + + +
+ + + + + + + +
+ + +
+ + + + + + + + + +
+ +

+ Your two-step verification code is: {{ Token }} +

+

Use this code to complete logging in with Bitwarden.

+ +
+ +
+ + +
+ +
+ + + +
+ +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + +
+ +

+ © 2025 Bitwarden Inc. 1 N. Calle Cesar Chavez, Suite 102, Santa + Barbara, CA, USA +

+

+ Always confirm you are on a trusted Bitwarden domain before logging + in:
+ bitwarden.com | + Learn why we include this +

+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + \ No newline at end of file diff --git a/src/Core/MailTemplates/Mjml/Handlebars/invite.html.hbs b/src/Core/MailTemplates/Mjml/Handlebars/invite.html.hbs new file mode 100644 index 000000000000..8e08b833ba53 --- /dev/null +++ b/src/Core/MailTemplates/Mjml/Handlebars/invite.html.hbs @@ -0,0 +1,571 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + +
+ + + + + + + + +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + +
+ +
+ +

+ A Bitwarden member has invited you to Bitwarden Password Manager +

+ +
+ + + + + + + +
+ + Finish account setup + +
+ +
+ +
+ + + +
+ + + + + + + + + +
+ + + + + + + +
+ + + +
+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + + + + + +
+ + + + + + + +
+ + Join Organization Now + +
+ +
+ +
This invitation expires on + Tuesday, January 23, 2024 2:59PM UTC.
+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + +
+ +

+ Learn more about Bitwarden +

+ Find user guides, product documentation, and videos on the + Bitwarden Help Center.
+ +
+ +
+ + + +
+ + + + + + + + + +
+ + + + + + + +
+ + + +
+ +
+ +
+ + +
+ +
+ + + +
+ +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + +
+ +

+ © 2025 Bitwarden Inc. 1 N. Calle Cesar Chavez, Suite 102, Santa + Barbara, CA, USA +

+

+ Always confirm you are on a trusted Bitwarden domain before logging + in:
+ bitwarden.com | + Learn why we include this +

+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + \ No newline at end of file diff --git a/src/Core/MailTemplates/Mjml/build.js b/src/Core/MailTemplates/Mjml/build.js index db8a7fe43388..155b15d59f98 100644 --- a/src/Core/MailTemplates/Mjml/build.js +++ b/src/Core/MailTemplates/Mjml/build.js @@ -20,7 +20,7 @@ const flags = { // Use __dirname to get absolute paths relative to the script location const config = { inputDir: path.join(__dirname, "emails"), - outputDir: path.join(__dirname, "out"), + outputDir: flags.hbs ? path.join(__dirname, "Handlebars") : path.join(__dirname, "out"), minify: flags.minify, validationLevel: "strict", hbsOutput: flags.hbs, diff --git a/src/Core/MailTemplates/Mjml/package.json b/src/Core/MailTemplates/Mjml/package.json index f74279da7b9e..31db4f20c2f4 100644 --- a/src/Core/MailTemplates/Mjml/package.json +++ b/src/Core/MailTemplates/Mjml/package.json @@ -19,6 +19,7 @@ "build:hbs": "node ./build.js --hbs", "build:minify": "node ./build.js --hbs --minify", "build:watch": "nodemon ./build.js --watch emails --watch components --ext mjml,js", + "build:watch:hbs": "nodemon ./build.js --hbs --watch emails --watch components --ext mjml,js", "prettier": "prettier --cache --write ." }, "dependencies": { diff --git a/src/Core/Services/IMailService.cs b/src/Core/Services/IMailService.cs index 5a3428c25a36..cdeade338134 100644 --- a/src/Core/Services/IMailService.cs +++ b/src/Core/Services/IMailService.cs @@ -41,7 +41,7 @@ Task SendTrialInitiationSignupEmailAsync( /// Otp code token /// subject line of the email /// Task - Task SendSendEmailOtpEmailv2Async(string email, string token, string subject); + Task SendMJMLSendEmailOtpEmailAsync(string email, string token, string subject); Task SendFailedTwoFactorAttemptEmailAsync(string email, TwoFactorProviderType type, DateTime utcNow, string ip); Task SendNoMasterPasswordHintEmailAsync(string email); Task SendMasterPasswordHintEmailAsync(string email, string hint); diff --git a/src/Core/Services/Implementations/HandlebarsMailService.cs b/src/Core/Services/Implementations/HandlebarsMailService.cs index 19705766edb8..fe9b57f5edee 100644 --- a/src/Core/Services/Implementations/HandlebarsMailService.cs +++ b/src/Core/Services/Implementations/HandlebarsMailService.cs @@ -32,7 +32,10 @@ namespace Bit.Core.Services; public class HandlebarsMailService : IMailService { - private const string Namespace = "Bit.Core.MailTemplates.Handlebars"; + // This namespace is the legacy name space when we only utilized handlebars processes to render email templates. + private const string _handlebarsTemplateNamespace = "Bit.Core.MailTemplates.Handlebars"; + // This namespace is for mjml compiled emails, allowing for better email client rendering while still able to use handlebars for content. + private const string _mjmlTemplateNamespace = "Bit.Core.MailTemplates.Mjml.Handlebars"; private const string _utcTimeZoneDisplay = "UTC"; private const string FailedTwoFactorAttemptCacheKeyFormat = "FailedTwoFactorAttemptEmail_{0}"; @@ -224,7 +227,7 @@ public async Task SendSendEmailOtpEmailAsync(string email, string token, string await _mailDeliveryService.SendEmailAsync(message); } - public async Task SendSendEmailOtpEmailv2Async(string email, string token, string subject) + public async Task SendMJMLSendEmailOtpEmailAsync(string email, string token, string subject) { var message = CreateDefaultMessage(subject, email); var requestDateTime = DateTime.UtcNow; @@ -238,7 +241,7 @@ public async Task SendSendEmailOtpEmailv2Async(string email, string token, strin WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash, SiteName = _globalSettings.SiteName, }; - await AddMessageContentAsync(message, "Auth.SendAccessEmailOtpEmailv2", model); + await AddMessageContentAsync(message, $"{_mjmlTemplateNamespace}.Auth.send-email-otp", model); message.MetaData.Add("SendGridBypassListManagement", true); // TODO - PM-25380 change to string constant message.Category = "SendEmailOtp"; @@ -740,7 +743,12 @@ private async Task AddMessageContentAsync(MailMessage message, string templat } var assembly = typeof(HandlebarsMailService).GetTypeInfo().Assembly; - var fullTemplateName = $"{Namespace}.{templateName}.hbs"; + + var mjmlTemplate = templateName.StartsWith(_mjmlTemplateNamespace); + + // MJML templateName already includes the full namespace path + var fullTemplateName = mjmlTemplate ? $"{templateName}.hbs" : $"{_handlebarsTemplateNamespace}.{templateName}.hbs"; + if (!assembly.GetManifestResourceNames().Any(f => f == fullTemplateName)) { return null; diff --git a/src/Core/Services/NoopImplementations/NoopMailService.cs b/src/Core/Services/NoopImplementations/NoopMailService.cs index 1459fab96668..c5da0a15102c 100644 --- a/src/Core/Services/NoopImplementations/NoopMailService.cs +++ b/src/Core/Services/NoopImplementations/NoopMailService.cs @@ -98,7 +98,7 @@ public Task SendSendEmailOtpEmailAsync(string email, string token, string subjec return Task.FromResult(0); } - public Task SendSendEmailOtpEmailv2Async(string email, string token, string subject) + public Task SendMJMLSendEmailOtpEmailAsync(string email, string token, string subject) { return Task.FromResult(0); } diff --git a/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs b/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs index 34a7a6f6e763..49fbce6be226 100644 --- a/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs +++ b/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs @@ -64,7 +64,7 @@ public async Task ValidateRequestAsync(ExtensionGrantVali } if (featureService.IsEnabled(FeatureFlagKeys.MJMLBasedEmailTemplates)) { - await mailService.SendSendEmailOtpEmailv2Async( + await mailService.SendMJMLSendEmailOtpEmailAsync( email, token, string.Format(SendAccessConstants.OtpEmail.Subject, token));